本站所有文章支持改变字体大小和开启夜间模式。请下滑后注意查看屏幕右下角对应图标。

此篇文章大量参考犹他大学官网文档Variables。原文有更详细的说明,强烈推荐前去阅读

写下此篇文章的背景是:我在阅读经典神书cpp的时候,发现不太懂C语言。于是我去找了著名的K&R神书来看,最后学到变量的时候,发现这几年来,我一直对变量的认真都是非常浅薄的:我每天都用它,但我却不了解它。因此去查阅资料,整理在此。

在C语言中,变量必须先经过声明才能被使用,声明的位置一般位于函数的开头以及所有可执行语句之前:

1
2
3
4
5
func() {
// 这里是函数开头
int foo; // 这里是函数的开头以及所有可执行语句之前
printf("hello scankernel\n"); // 这一行是可执行语句(executable statement)
}

上面示例除了揭示变量的声明位置,其实还透露了变量的声明方法:一个类型名字(int)后面跟着变量名列表。注意这里的变量名列表五个字。因为它意味这你可以这样声明变量:

1
2
3
func() {
int foo, bar, baz;
}

至此,你已经了解到声明变量的全部过程。

为何要声明变量

为何要声明变量呢?著名的K&R经典书籍里面原话是这么说的:

A declaration announces the properties of variables。

意思是声明语句(向编译器)宣告了变量的属性。

但要理解这句话可不是那么容易。且放下看!

变量

变量是对信息的指向,而变量名是该变量所指向的信息的代表。变量之所以被称之为变量是因为变量所指向的信息是可以改变的,但是附加在变量的操作是不变的。

变量–自然符号

变量其实就是一种符号。通过这个符号,我们可以理解这个符号所携带的信息/数据。

这么说可能咋看不太容易理解。举个你每天都会用的例子:文字。

文字是人类用来纪录特定事物、简化图像而成的书写符号。

你看,文字也是符号。

无论是文字还是变量,它们其实都是符号。通过符号,(我们)可以了解到该符号所携带的信息/数据。

在计算机程序里,变量有点像“口袋”或者“信封”。计算机可以往口袋/信封里存放信息,取出信息。我们则通过变量名对“口袋里的数据”进行操作。这里划个重点:我们不能直接操作数据,而是只能通过变量名对数据进行操作。

变量是符号/符号名。这意味变量可以代表所有可能的值。(ps:至于这些可能的值是哪些,则由变量的类型决定)

这有点像在数学里:

1
a + b > a  # a,b都大于0

总是对的。你看,a和b的值可以变的,你可以令a=1,b=1,也可以令a=10,b=5,但无论a和b在它们的限定范围(a,b>0)内怎么变,附加在a+b>a上的操作总是不会变的。比如>不会变成<,也不会变成=a+b也不会变成a-b

回想一下上面提过的一句话:

变量之所以被称之为变量是因为变量所指向的信息是可以改变的,但是附加在变量的操作是不变的。

这下,理解了吧?

我们将目光从数学回到计算机程序这边来。

在计算机里,把类似于a+b>b的表达式称为符号表达式(Symbolic Expression)。再次提醒,无论变量的值是多少,表达式的逻辑结果总是对的。

我们来看一个例子:如果我们希望找到任何两个数相加的和,我们可以这样写:

1
result = a + b

a和b都是变量,它们是符号,即它们可以代表任何数值。比如a代表5,b代表10,当程序运行的时候,符号表达式result=a+b会被换成result=5+10,因此result是15。

变量的美妙之处在于无论变量的值怎么变,附加在变量的操作本身是不会因变量的改变而改变的。

另一个例子:如果我们希望算出一个人的身高的多少厘米,我们可以利用公式:height in centimeters = height in inches * 2.54。这个公式的逻辑结果总是为真。

1
height in centimeters = height in inches * 2.54

“逻辑结果总是对的”,“逻辑结果总是为真”或者说“附加在变量的操作是不变的”其实表达的是一个意思,就是表达式总是成立。

变量操作

我们可以对变量进行的操作如下:

  1. 创建变量(名字起得漂亮些哦):变量本身是一种抽象的概念,创建变量的过程就是变量具体化的过程。比如16位大小的int类型的变量范围是-32768~+32767,本身只是一种抽象的概念,当我们创建一个该类型的变量,并且赋予该变量一个名字,我们就得到了一个具体的变量。(拗口吧,嘻嘻)
  2. 将一些(数据)信息存放进变量名所代表的变量里去,即存数据。(该变量原先的值会被销毁,比如a=3, 当我们把4存放到a中去,3会被销毁)(销毁只是一种相对说法,有些编程语言设计用到共享,那么3就不会被销毁,无论销毁不销毁,a放进去4后,和3就没关系啦)
  3. 从该变量里复制出(数据)信息,即取数据。(该变量原先的值不会被销毁)result=a+b其实就是从a和b里取出具体的数值,然后计算。计算之后,a和b本身的值是不变的。

使用变量的例子

1
2
3
4
int age           = 15;       // set the users age 
int age_in_months = age * 12; // compute the users age in months
int age_in_days = age_in_months * 30; // compute the approximate age in days
int grades[4]; // create an arary

变量属性

前面说到:

A declaration announces the properties of variables。

声明语句(向编译器)宣告了变量的属性。

那么,变量变量有什么属性呢?

变量有6个属性。对去初学者来说,熟悉前面三个属性至关重要。对于老鸟来说,后面三个属性是过不去的坎,否则你永远处于一知半解的状态。

记住它们!

  1. 名字(A name);
  2. 类型(A type);
  3. 值(A value);
  4. 作用域(A scope);
  5. 生命周期(A life time);
  6. 一个在存储设备中的物理位置(A Location in memory)。

下面来逐一解释。

变量名字

变量的名字是一个符号,它代表变量所指向的信息。对于程序原来说,变量名也许是最重要的一个属性,因为变量名是我们访问变量的唯一方式。在特定的范围内,每个变量必须要有一个独一无二的名字。

变量类型

变量的类型限定存放在该变量的数据类型。编译器根据不同的变量类型去开辟不一样大小和结构的空间去存放数据。

变量的值

值,顾名思义就是具体的数据。由于计算机表示不同数据的方式不同,科学家将数据分类成不同类型的值。比如字符串,字符,整型,浮点型…

变量,正如其名,它的值是可以改变的。比如变量money,今天是10000,可能明天被仙人跳,就从10000变成0了。

在大多数时间里,如果你创建一个变量,一般你会设置该变量的类型和名字。富有经验的程序员还会给该变量赋一个初始值。如果你在使用变量之前忘记给该变量赋值,那么该变量的默认值是什么,不同的编程语言是不同的。

在c语言里,如果你创建变量的时候忘记给它赋一个初始值,那么它的默认值是未知的。它可能是数字6,可能是数字7,也可能是字符串"ncnkajndxkwje"。比如:

1
2
3
4
int age; 
printf("age is %f\n"); // answer: age is 120912312 (i.e., Garbage)
age = 20;
printf("age is %f\n"); // answer: age is 20

作用域

就像是小说作者会将小说分割成章节,然后又将章节分割成段落,好的程序员会将他们写得代码的范围划分得尽量小(写进函数里),不同功能的代码写进不同的函数里,大的功能分割成一堆小的功能。

这么做的原因其实就是因为变量的作用域。

其实想象一下不难,一般来说项目到达一个程度,就会是一堆人共同来写的,这样就很容易出问题,比如你给你创建的变量起名并赋值为bird = 8,你的同事又给它创建的变量名起名并赋值为bird = 888,那么在编译的时候,编译器发现有两个bird就傻眼了:“这个bird到底是8还是888呀?”。这种情况其实就是变量名冲突。解决变量名冲突,需要编译器和程序员同时作出努力:

对编译器来说:规定在f1函数里的变量a和f2函数里的变量a不会互相干扰。也就是说f1和f2里面的变量a分别属于不同的作用域。

那么,如果函数f1里有两个a呢?这时候会编译出错啦:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main() {
int a;
int a;
return 0;
}

# 输出
➜ cc hello.c
hello.c:5:9: error: redefinition of 'a'
int a;
^
hello.c:4:9: note: previous definition is here
int a;
^
1 error generated.

所以程序员要努力学会管理自己的变量。尽量把大的程序拆分成小的写。程序小了,一眼看过去,马上就看出有没有重复的名字啦。

生命周期

生命周期其实和作用域息息相关。程序开始执行的时候,当执行到声明变量的语句时,该变量就“come to life”,当程序离开声明该变量的作用域(一般来说,是函数),那么该变量就“die”了。

变量在存储设备中的物理位置

很幸运,程序员不用去管理变量到底被存放在内存(存储器),C语言编译器会替我们去管理的,也就是所谓的内存管理啦。当然,内存管理不容易学,包括我现在其实也不是很懂(泪ing)

但是你需要理解,我们声明的每一个变量,都会在存储器的某个地方有一个对于的物理空间(你可以把这个空间想象成我们上面提到的口袋或者信封)。对于数组来说,就是一组连续空间。每一个空间都会包含(被存放)一个值。