[深入浅出C语言]常量与变量

246 阅读11分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情

前言

        本文致力于用简单浅显的方式讲解C的常量和变量的基础知识,简单地介绍常量变量,可以说本文是作为前置铺垫的,更多深入的内容可以到我写的别的文章里进一步学习。

        新手上路,文章拙劣而纰漏难免,不好的地方还请指正,希望于你有益。


概念

        在程序中,我们使用的量无非就是常量和变量。

        听过数学里的常数没?其实差不多。所谓常量就是预先设定好值并且在程序执行过程中不发生变动的量,比如我要计算圆的的面积,那在编写程序的时候我就先定义一个常量PI为3.14嘛,而这个值在整个程序运行过程中不会改变。

#define PI 3.14

        这里的PI是标识符常量,它表示的值在程序执行过程中不可更改也不会更改。还有就是字面常量,指的是字面意义上的常量,比如说100、25.5、'a'、"hello"等等。

        变量,顾名思义就是会随着某些条件变化的量,其实就是会在程序运行过程中根据某些条件或设计而改变的量。比如我要计算两个整数之和,定义一个整型sum初始化为零,当执行完求和语句后sum的值就变成了60了。

int sum = 0;
sum = 10 + 50;

局部变量与全局变量

作用域

        作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字(标识符)并不总是有效可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。

        作用域有块作用域,函数作用域,函数原型作用域和文件作用域。

        在"存储的类别"的文章会继续讲到作用域,这里先简单理解一下。

        变量的作用域简单来说就是变量能发挥用处的范围,世间万物都有自己能起到作用和起不到作用的地方嘛,就比如说老师的作用域在哪?学校呗!

        局部变量,就是作用域在局部范围的变量。

        我们来看看下面这段代码,想一想变量a, b的作用域分别是什么?

int main()
{
    int a = 10;
    printf("%d", a);

    {
        int b = 10;
        printf("%d", b);
        printf("%d", a);
    }
    printf("%d", a);
    printf("%d", b);
    return 0;
}

        我们让代码跑起来看看,发现发生错误无法运行,警告如下:

image.png

        为什么b会未声明呢?我不是在main函数花括号里面的花括号里定义了b吗?奇怪捏🤔。

        这就要注意了,这里的b作用域就是这个花括号,出了花括号b就没用了。

image.png         这里可能有人就要问了,b不是定义并赋值了吗,b就算不能再使用了那应该还存在吧?怎么会未声明呢?搞得好像b不曾来过一般。😵‍💫(在后面会细讲好吧,别急呢米娜桑~)

        那a的作用域就是main函数的花括号范围咯,真聪明!

        实际上,局部变量作用域是块作用域,看的就是离它定义处最近的花括号,花括号括起来的范围就是它的“活动范围”,所谓的块就是花括号括起来的代码区域。

        在函数里面定义的变量,作用域都是块作用域,一般是整个函数的范围,因为函数就是一个大的块,不过若是在函数包含的某个块中定义的话,作用域会限制在那个块内,就比如上面那个例子的b就是定义在函数内的块中,所以作用域就是那个块的开头到结尾。

        关于深入讲解我也会放在"存储类别"的文章再接着讲的,我们接下来看看全局变量。

        全局变量,就是作用域为整个工程项目的变量。

        一句话,全局变量就是这条道上的爷👴,想搁哪沾花惹草那都不是事儿。

        首先,全局变量在整个文件范围内都有用,而一个文件里可能有若干个函数,这个变量也就是能够在这若干个函数内使用。(其实就是文件作用域)

        看看这段代码,全局变量c穿梭在两个函数之间。

int c = 0;//全局变量可以在函数外面定义并初始化

//求两数之和函数
int add(int a, int b)
{
    c = a + b;//在这个函数里就用到了全局变量c接收求和的值
    return c;
}

int main()
{
    int a = 10;
    printf("%d ", a);

    int b = 10;
    printf("%d ", b);

    printf("%d", c); //main函数里也用到了c

    int e = add(a, b);
    printf("%d ", e);

    return 0;
}

        这里顺便插一嘴,上面定义了一个函数嘛,那么请问函数形参的作用域是哪呢

        这形参不是搁圆括号里头嘛,我寻思这函数形参也不是全局变量吧,前面不是说局部变量找花括号,那这在花括号外头啊,咋判断呀?

        小伙汁,年纪轻轻的可别被这“奇装异服”给骗了🕵️‍♀️,虽然函数形参声明是在花括号外头,圆括号里头,但是它的作用域是块作用域, 也就是后面的函数体花括号内呗(具体为什么的话可以结合我写的那篇"函数栈帧"的文章思考原因)。

        这就好比如啥呢,“洋装虽然穿在身,我心仍是中国人”,就算我可以到处去国外旅游,但是一到要我出力的时候那肯定只能为祖国发光发热嘛——虽然我函数形参待在圆括号里头,但是我真正发挥用处的地方是在花括号里面呢。🤣

        一个项目包含若干个文件,也就是说全局变量可以在各个文件中使用,只是需要用extern关键字标明一下告诉编译器我要在别的文件用这个文件里的全局变量呗。(讲的很片面了解一下即可)

extern int a = 100;

        这里其实就粗略了解一下即可,更多细节我会在存储的类别里再给大家讲讲。这样一看,我好像一直在说xx会在存储类别文章讲😂,那是因为这篇文章本来就是作为前置铺垫的,很多东西要往深入地讲解就不像前面写的那么简单,会比较复杂,分篇章由浅入深了来讲效果可能会更好些。😏


生命周期

        变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段。

        生命周期也可以用在人身上嘛,人的生老病死这么一个过程的时间段呗👶->👴->💀。

        变量啥时候创建的呢?在定义变量的时候开辟了相应的内存空间来存储变量的值,这个时候变量就“诞生”了。

        那变量啥时候“翘辫子”呢?其实吧这和它作用域有关系,局部变量生命周期就是从程序执行到局部变量处开始,局部变量被创建被使用,直到程序执行出了作用域,局部变量被销毁

       全局变量生命周期就是程序执行的整个过程

        再看看前面那个例子,出了花括号后b就被销毁了,所以在花括号外是被认为是未声明的。

image.png

image.png

全局变量与局部变量可否同名

        综合上面所讲的内容,我们来看看下面的问题。

比如:

image.png

        会打印哪一个值呢?还是编译失败呢?

        看看这个或许你就能想清楚了:

在C语言中,

1. 不允许在同一个作用域中定义多个相同名称的变量

    比如:在一个班级中存在两个名字相同的学生王帅,当老师点王帅回答问题时,那个回答就冲突了

2. 允许在不同的作用域中定义多个相同名称的变量

    比如:两个班级中各有一个王帅,老师在A班中点王帅回答问题,不会有任何歧义

3. 不同作用域中定义的变量,在访问时采用就近原则

    比如:你们村有一个小伙伴名字叫刘德华,那你在你们村喊刘德华时,你们村的刘德华就会跑过来响应你,

    而我们世界级别人见人爱的天王他不会理你,因为距离远听不见,但是两个刘德华可以同时存在这个 世界上,只要不在一个村,就不会冲突。

 根据以上描述可知,对于上面那份代码:

  1. 全局作用域中的num和main中的num可以同时存在,不会冲突,因为不是同一个作用域

  2. 在main函数中访问num时,采用就近原则,因此访问的是main中的num,相当于将全局作用域中的num屏蔽了

初识常量

        这里就初步认识一下各类常量,以后地文章里会或多或少地深入讲解的。

        C语言中的常量分为以下以下几种:

  1. 字面常量(字面常量就是单纯的常量比如3.14,1000,'a')

  2. const 修饰的常变量

  3. #define 定义的标识符常量

  4. 枚举常量

const修饰的常变量(基本了解)

         比如说原本有一个变量float a = 20.5;,我可以任意修改它吧,a = 60.5;。

         这时候如果我想让它不能再被修改了呢?那就const float a = 10.5;

        一旦使用了const修饰后,这个变量的值就不能直接修改了,从而具有类似于常量的性质。

        C++和C会有一些不一样的地方,我们这里讲的是C。

        举个例子就是,我有一个盒子,我想装游戏机就装,想装鸭舌帽就装,完全凭我需要来使用这个盒子的空间对吧,那要是有一天我突然想让这个盒子只装我的RMB¥咋办,简单,买个🔒一上锁就搞定了,这下这个盒子里装的的东西就不能直接改变了嘛(最起码也得先解开锁)。

        const就像那把锁头一样嘛,"锁住"了变量对应的那块空间呗。

        那这玩意儿到底是变量还是常量?我们来简单检验一下

const int n = 10;

int arr[n] = {0};

        我们知道,C语言中数组定义时数组长度得为常量表达式而不能是变量(这里不讨论C99标准后的变长数组),所以如果const修饰后n变成了常量,那这样应该没什么问题,而如果const修饰后n仍为变量的话,这样做就会出问题,我们让代码走起来看看:

image.png

        诶呀,出现错误了,也就是说这里的n它仍然是变量,本质未变,只是具有了与常量类似的性质

#define定义的标识符常量

        我想要在程序中反复用到一个常量,并且它对于我这个程序有着特定的意义,比如我要求长方体体积,我需要长宽高对吧,为了方便区分,我就想给它们起个名字,高就是height,宽就是width,长就是length,然后呢我这长宽高是给定的常量,那这时候我就可以这样

#define height 100

#define width 50

#define length 200

        这样就定义了三个常量,并且每个常量都给它们定了个名字(也就是标识符),这样我在写程序的时候就可以用标识符来代表我要使用的常量

int V = height * width * length;

        这些标识符在程序运行之前就会自动被替换为对应的变量值,也就是在预处理阶段时变成这样:int V = 100 * 50 * 200;

有什么好处?

1.能很清楚我要用到的常量代表什么意义,标识符一般带有某些信息来代表常量

2.在多次中多次用到该常量的时候,如果我想要把它的值改变一下,只需要在#define定义的地方把数值改动一下就可以了,不需要在程序中一个一个地改

枚举常量

        什么是枚举?一一列举呗,也就是把常量一一列举出来。

        我们生活中有什么常量是可以一一列举出来的?

        比如性别:

enum sex
{
    MALE,    //男性
    FEMALE,  //女性
    UNKNOWN  //未知
}

        这里创建了一个数据类型enum sex,它的可能取值为MALE或FEMALE,而这个MALE和FEMALE就是所谓的枚举常量,它们的值不可改变。(不细讲,在"自定义类型"文章里会讲)


到此为止就是全部内容了,更多敬请期待以后的文章,感谢观看,你的支持就是对我最大的鼓励~

v2-f02a18512f818276f5b2c8903fb38ceb_720w.jpg