TIP如果你从未接触过 C 语言, 那么我建议你先阅读前面的文章:
数据类型 和 变量
在上一篇文章中, 我们运行了一个最简单的 C 语言程序:
#include <stdio.h>
int main() { printf("Hello World");
return 0;}通过在main()函数中调用printf("Hello World");, 在终端中打印出来了Hello World
我们知道printf()是函数, 那么"Hello World"是什么呢?
在 C 语言中, 凡是被双引号""包裹的字符, 被称为字符串, 也叫字符串字面量
无论里面是英文字符、数字、符号, 甚至是中文字符, 只要被双引号括起来, 它就是一个字符串 :
"123"、"2.333"、"###"、"你好"、"Hello"等等- 哪怕是一个字符, 如
"A"、" "、"9", 只要用双引号, 也是字符串 - 甚至
"", 也表示一个合法的字符串, 称为空字符串
💡 但需要注意: 字符串其实是由一个个字符按顺序组合而成的, 比如
"Hello"由'H' 'e' 'l' 'l' 'o'组成C 语言中, 单个字符通常使用单引号''包裹, 比如'A'、'1'、'%'
这些字符通常会存储为一种特定的数据类型: char类型
除char类型之外, C 语言中还有其他基本数据类型:
// 空类型void// 字符类型char// 短整型short// 整型(整数类型)int// 长整型long// 更长的整型long long// 单精度浮点数(单精度小数)float// 双精度浮点数(双精度小数)double// 扩展精度浮点数(不常用)long double这就是 C 语言中所有的基本数据类型了
你会发现, 这里边并没有字符串类型
实际上, C 语言中字符串并不是一种基本数据类型, 字符串是由
char类型字符按顺序组合成的
变量
C 语言中, 你可以使用这些数据类型, 定义并使用变量
什么是变量?
从字面上看, “变量”就是可以变化的量, 即值会发生变化的数据
而在编程语言中, 并不是这么简单的, 不过可以 以一种简单的视角理解变量: 将变量, 当作一个有名字的”盒子”
这个盒子有特定的大小(由数据类型决定), 我们可以把数据放进去, 也可以随时拿到数据使用或替换成新的数据
变量的声明和定义
什么是变量的定义?
在 C 语言中, 变量 具体的定义方式是这样的:
char character = 'c';int age = 20;float weight = 55.5;character是一个char类型的变量, 存储了字符'c'age是一个int类型的变量, 存储了整数20weight是一个float类型的变量, 存储了浮点数55.5
character age weight为不同类型、不同大小的变量, 把变量当成一个”盒子”来看, 这些盒子可以存储对应类型大小的数据, 这里的大小指的是bit(位)、byte(字节)
变量的定义, 实际上就是创建这个具有大小的盒子的动作
变量定义之后, 这个具有一定大小的盒子就已经真正存在了
只有这个盒子真正存在了, 你才能使用这个盒子
除了定义之外, 还有一个概念是: 声明
定义是, 创建具有大小的盒子的动作, 可以使这个盒子真正存在
那么声明又是什么呢?
声明, 是声明一下有一个盒子, 但这个盒子是否真正存在, 则是未知的
如果你只声明变量, 这个变量是没有办法使用的
NOTEC 语言中, 声明变量需要使用一个关键字
extern声明变量的动作为:
extern int var; // 声明有一个变量var这只是一个声明动作, 并没有真正的创建变量
所以, 如果直接使用, 是会有问题的:
#include <stdio.h>int main() {extern int var;printf("%d\n", var);return 0;}
编译结果是, 引用的
var未定义因为只是声明了
var, 但是var并没有真正存在
NOTEC 语言中,
=被称为赋值符号, 意为 为…赋值 或 将值赋给…, 通常表示”将右边的值赋给左边的变量”
已经放在变量中的数据, 在之后的代码中, 就可以直接通过变量名来使用了, 比如:
#include <stdio.h>
int main() { int age1 = 18; // 变量age1, 放入数字18 int age2 = 20; // 变量age2, 放入数字20 printf("age1 加 age2 和为 %d\n", age1 + age2);
return 0;}这段代码的执行结果为:

NOTE注释(Comment)
各编程语言, 都会提供一种 可以实现 在代码文件中对代码片段的功能或原理等进行说明, 同时不影响代码功能 的语法, 这种语法被称为 注释语法
注释语法的功能就是在代码中插入说明性文本, 而不影响其他代码的编译
因为在正常情况下, 源代码文件中的每行语句, 都应该是符合编程语言语法标准的语句
举个例子:
在 C 语言中,
int a = 10;printf("hello world\n");是符合语法标准的如果你想对代码进行解释:
这一个语句定义了一个变量a, 并赋值为10,这一个语句使用printf()打印"hello world"是不符合语法标准的// 符合语法标准int a = 10;printf("hello world\n");// 不符合语法标准这一个语句定义了一个变量a, 并赋值为10这一个语句使用printf()打印"hello world"不符合语法标准的语句, 编译器是无法进行编译的, 因为编译器无法理解非法语句, 也就没有办法把代码翻译成计算机语言
所以, 各编程语言均提供有注释语法, 它可以使编译器在编译代码时, 忽略不符合语法标准的语句
当然, 注释语法事实上就是语法标准的一部分, 通常情况下 注释语法的使用都非常简单
C 语言中, 注释语法分两种:
单行注释
作用范围只在当前行内
其实在上面已经使用过:
// 注释内容以双斜杠开始, 后续的同行内容 均为注释:
//, 无法跨行作用多行注释:
作用范围在语法包裹范围内
语法也很简单:
/*注释内容*/多行注释是划定范围来注释的, 以
/*开始,*/结束, 在开始和结束之间的所有范围均为注释
/*和*/必须配对使用也可以同行注释
/* 注释内容 */
NOTE
bit位, 表示二进制位, 即0/1
1bit可以表示 2 个数:
十进制 二进制 0011
2bit可以表示 4 个数:
十进制 二进制 000101210311
3bit可以表示 8 个数:
十进制 二进制 00001001201030114100510161107111
4bit可以表示 16 个数: …;5bit可以表示 32 个数: …;6bit可以表示 64 个数: …;7bit可以表示 128 个数: …;8bit可以表示 256 个数: …
byte字节, 一字节是 8 位
在 C 语言程序中, 不同的数据类型, 占用的大小是不同的, 这意味着”盒子”的大小不同, 即 变量的大小不同
每种类型的大小, 具体如下表所示:
| 数据类型 | 占用大小 |
|---|---|
char | 1 byte |
short | 2 byte |
int | 4 byte |
long | 4/8 byte(根据平台编译器的具体实现决定) |
long long | 8 byte |
float | 4 byte |
double | 8 byte |
long double | 8/16 byte(根据平台编译器的具体实现决定) |
void | 不是用来存储数据的类型, 可以看作是一种空类型占位符, 不能用于定义变量 |
这些数据类型具体的大小占用, 也可以通过代码的形式在程序中打印出来:
#include <stdio.h>
int main() { printf("char size: %lu\n", sizeof(char)); printf("short size: %lu\n", sizeof(short)); printf("int size: %lu\n", sizeof(int)); printf("long size: %lu\n", sizeof(long)); printf("long long size: %lu\n", sizeof(long long)); printf("float size: %lu\n", sizeof(float)); printf("double size: %lu\n", sizeof(double)); printf("long double size: %lu\n", sizeof(long double));
return 0;}这段代码, 使用gcc main.c将其编译为可执行程序并运行的结果是:

可以看到打印结果是符合上面表格中的内容的
变量的命名规则
已经大概了解了, 变量可以简单理解为 程序中, 一个命名的、有大小的、存储数据用的盒子
不过, 变量命名也是有一定规则的, 并不是可以随便给变量命名
C 语言标准中, 标识符(变量名、函数名等)的命名有一套明确的规则:
-
只能包含以下字符:
- 字母:
a-z和A-Z - 数字:
0-9 - 下划线:
_
标准中并未提及中文字符命名的相关规则, 但编译器可能支持
- 字母:
-
首字符必须是字母或下划线
即, 首字符不允许是数字
-
区分大小写
即,
age与Age不属于同一标识符 -
禁止与关键字冲突
即, 标识符的命名禁止与 C 语言中的关键字完全一致
有关 关键字 并非本章介绍内容
下面是一些变量命名的正确例子和错误例子:
/* 正确例子 */int age;int Age;float weight1;double _theFloat;double Double;
/* 错误例子 */int 2age; // 首字符禁止为数字int float; // 禁止与关键字完全一致float the-weight; // 非法字符double $float; // 非法字符变量的分类 以及 作用域和生命周期
C 语言程序的入口是main()函数
那么, 即使是最简单的 C 语言程序, 也会分出两种区域:
/*入口外, 即 main() 函数外*/int main() { /* 入口内, 即 main() 函数内 */ return 0;}/*入口外, 即 main() 函数外*/在之前的例子中, 变量都是在main()函数内部定义的
那么, 变量可以在main()函数外部定义吗?
答案是可以
C 语言中, 变量可以在main()函数外部定义
并且, 定义在main()函数内部和外部的变量, 是有一定区别的:
-
在
main()函数外部定义的变量, 被称为全局变量在函数内部定义的变量, 被称为局部变量
-
全局变量的作用域, 一般为整个 C 程序
局部变量的作用域, 则只在定义局部变量所在的范围
什么是变量的作用域?
C 语言中, 变量的作用域, 就是变量能被使用的有效范围
比如, 在
main()函数内定义的变量, 那这个变量的作用域就在main()函数的范围内, 出了main()函数就无法再使用实际上, 在 C 语言中一个
{}声明一个代码块, 在代码块内定义变量, 此变量的作用域即为所在代码块 -
全局变量的生命周期, 为整个 C 程序
局部变量的生命周期, 为定义局部变量所在的范围
变量的生命周期, 可以简单理解为变量的存活时间
变量的生命周期, 一般是从变量完成定义到其所在作用域结束
-
全局变量和局部变量可以同名, 但在使用上 遵循就近原则
以实际的代码为例子:
#include <stdio.h>
int globalVar = 10; // 全局变量int var = 20; // 全局变量
int main() { printf("globalVar: %d\n", globalVar); printf("var: %d\n", var);
int var = 30; // 局部变量 printf("var: %d\n", var);
{ int _var = 40; int var = 50; printf("_var: %d\n", _var); printf("var: %d\n", var); }
// printf("_var: %d\n", _var); printf("var: %d\n", var);
return 0;}这段代码定义了很多个变量, 可以先来看一下执行结果:

我们可以结合现象, 分析一下每一句printf()所使用的变量是哪一个:
| 序号 | 调用语句 | 打印值 | 值对应变量名 | 是否有重名变量 | 实际使用变量 |
|---|---|---|---|---|---|
| 1 | 第7行: printf("globalVar: %d\n", globalVar); | 10 | globalVar | 否 | globalVar(第3行) |
| 2 | 第8行: printf("var: %d\n", var); | 20 | var | 是 | var(第4行) |
| 3 | 第11行: printf("var: %d\n", var); | 30 | var | 是 | var(第10行) |
| 4 | 第16行: printf("_var: %d\n", _var); | 40 | _var | 否 | _var(第14行) |
| 5 | 第17行: printf("var: %d\n", var); | 50 | var | 是 | var(第15行) |
| 6 | 第21行: printf("var: %d\n", var); | 30 | var | 是 | var(第10行) |
从结果来看, 可以分析出一些特性:
-
全局变量和局部变量可以重名
编译没有问题, 运行也没有问题
-
变量的使用, 遵循就近原则
即, 如果存在重名变量, 使用时 会使用已经定义的, 离调用位置近的变量
在例子中表现为:
局部变量
var没有定义时, 使用var变量, 实际使用的是全局变量var局部变量
var定义后, 使用var变量, 实际使用的是局部变量var -
变量只在其作用域内生效
即, 变量出了其作用域, 其生命周期就会结束, 无法再使用
在例子中的表现为:
第
17行打印的var值是50, 而第21行打印的var值是30如果把第
20行代码的注释解开(删掉//), 编译会报错:
报错信息为:
_var未声明
WARNING虽然全局变量和局部变量之间可以重名, 在同一个作用域中, 变量之间是不可以重名的
#include <stdio.h>int var = 0;int main() {int var = 20;return 0;}这样是允许的
但是下面这样是不允许的:
#include <stdio.h>int main() {int var = 0;int var = 20;return 0;}
这种情况下, 编译会失败, 并且会出现报错: 变量重定义
总结来讲, 究竟什么是变量?
变量是一个命名的存储单元, 它可以用来存储程序运行过程中可能发生变化的数据
它有: 名字(标识符)、类型(数据类型决定它能存什么数据)、大小(由类型决定)、作用域(在哪能访问)、生命周期(存在多久)
还有这些特点:
-
类型明确: 每个变量都有明确的数据类型(如
int、float等) -
内存占用不同: 不同类型的变量占用不同的字节数
-
命名有规则:
只能由字母、数字和下划线组成, 且不能以数字开头
不能与 C 语言中的关键字完全一致
-
作用域限制:
全局变量在程序内全局有效
局部变量只能在其定义的代码块中有效.
-
生命周期不同:
局部变量: 随函数或代码块执行完毕而消失
全局变量: 存在于整个程序生命周期
-
支持赋值与修改: 变量可以在程序中多次赋新值
-
使用需先定义: C 语言要求变量在使用前必须先定义
const修饰变量
NOTE与变量相对, 还存在另一个重要概念: 常量
常量, 通常指一个固定不变的量
现实生活中的代表有: 光速(恒定为约
299,792,458 米/秒)、π(约等于3.1415926……)…这些量之所以被称为”常量”, 是因为它们的数值具有不可修改性
const是 C 语言标准中的一个关键字, 它是单词constant(常量)的简写
const常用于定义不可变的变量
在变量定义时, 将const放在类型前面, 表示该变量不能被修改(即只读), 也就是说, 被const修饰的变量在程序中不能被重新赋值
来看下面的代码:
#include <stdio.h>
int main() { int var = 10; const int con = 20; /* 定义变量时, cosnt 放在类型前 修饰变量 */
printf("var: %d\n", var); printf("con: %d\n", con);
return 0;}这段代码将会输出如下结果:

但是如果你试图修改con:
#include <stdio.h>
int main() { int var = 10; const int con = 20; con = 11;
printf("var: %d\n", var); printf("con: %d\n", con);
return 0;}那么, 编译就会发生报错:

报错信息为: 试图给只读变量con赋值
可以看到, 被const修饰的变量, 具有了常量属性, 无法修改, 只能读取
WARNING
const修饰变量会使变量具有常量属性这表示变量定义完成之后, 就无法再进行赋值
所以, 在定义变量时, 如果要使用
const修饰, 请保证变量初始化无误
NOTE变量的定义:
int var;变量的初始化:
int var = 10;变量的初始化是, 在变量定义的同时给变量赋初值
下面这样不叫初始化:
int var;var = 10;

