4098 字
20 分钟
从零接触C语言(初览)-II: 数据类型 和 变量
TIP

如果你从未接触过 C 语言, 那么我建议你先阅读前面的文章:

📌 从零开始接触 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类型的变量, 存储了整数20
  • weight是一个float类型的变量, 存储了浮点数 55.5

character age weight为不同类型、不同大小的变量, 把变量当成一个”盒子”来看, 这些盒子可以存储对应类型大小的数据, 这里的大小指的是bit(位)byte(字节)

变量的定义, 实际上就是创建这个具有大小的盒子的动作

变量定义之后, 这个具有一定大小的盒子就已经真正存在了

只有这个盒子真正存在了, 你才能使用这个盒子

除了定义之外, 还有一个概念是: 声明

定义是, 创建具有大小的盒子的动作, 可以使这个盒子真正存在

那么声明又是什么呢?

声明, 是声明一下有一个盒子, 但这个盒子是否真正存在, 则是未知的

如果你只声明变量, 这个变量是没有办法使用的

NOTE

C 语言中, 声明变量需要使用一个关键字extern

声明变量的动作为:

extern int var; // 声明有一个变量var

这只是一个声明动作, 并没有真正的创建变量

所以, 如果直接使用, 是会有问题的:

#include <stdio.h>
int main() {
extern int var;
printf("%d\n", var);
return 0;
}

编译结果是, 引用的var未定义

因为只是声明了var, 但是var并没有真正存在

NOTE

C 语言中, =被称为赋值符号, 意为 为…赋值 或 将值赋给…, 通常表示”将右边的值赋给左边的变量”

已经放在变量中的数据, 在之后的代码中, 就可以直接通过变量名来使用了, 比如:

#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 语言中, 注释语法分两种:

  1. 单行注释

    作用范围只在当前行内

    其实在上面已经使用过:

    // 注释内容

    以双斜杠开始, 后续的同行内容 均为注释: //, 无法跨行作用

  2. 多行注释:

    作用范围在语法包裹范围内

    语法也很简单:

    /*
    注释内容
    */

    多行注释是划定范围来注释的, 以/*开始, */结束, 在开始和结束之间的所有范围均为注释

    /**/必须配对使用

    也可以同行注释/* 注释内容 */

NOTE

bit位, 表示二进制位, 即0/1

1bit可以表示 2 个数:

十进制二进制
00
11

2bit可以表示 4 个数:

十进制二进制
000
101
210
311

3bit可以表示 8 个数:

十进制二进制
0000
1001
2010
3011
4100
5101
6110
7111

4bit可以表示 16 个数: …; 5bit可以表示 32 个数: …; 6bit可以表示 64 个数: …; 7bit可以表示 128 个数: …; 8bit可以表示 256 个数: …

byte字节, 一字节是 8 位

在 C 语言程序中, 不同的数据类型, 占用的大小是不同的, 这意味着”盒子”的大小不同, 即 变量的大小不同

每种类型的大小, 具体如下表所示:

数据类型占用大小
char1 byte
short2 byte
int4 byte
long4/8 byte(根据平台编译器的具体实现决定)
long long8 byte
float4 byte
double8 byte
long double8/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 语言标准中, 标识符(变量名、函数名等)的命名有一套明确的规则:

  1. 只能包含以下字符:

    • 字母: a-zA-Z
    • 数字: 0-9
    • 下划线: _

    标准中并未提及中文字符命名的相关规则, 但编译器可能支持

  2. 首字符必须是字母或下划线

    即, 首字符不允许是数字

  3. 区分大小写

    即, ageAge不属于同一标识符

  4. 禁止与关键字冲突

    即, 标识符的命名禁止与 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()函数内部和外部的变量, 是有一定区别的:

  1. main()函数外部定义的变量, 被称为全局变量

    在函数内部定义的变量, 被称为局部变量

  2. 全局变量的作用域, 一般为整个 C 程序

    局部变量的作用域, 则只在定义局部变量所在的范围

    什么是变量的作用域?

    C 语言中, 变量的作用域, 就是变量能被使用的有效范围

    比如, 在main()函数内定义的变量, 那这个变量的作用域就在main()函数的范围内, 出了main()函数就无法再使用

    实际上, 在 C 语言中一个{}声明一个代码块, 在代码块内定义变量, 此变量的作用域即为所在代码块

  3. 全局变量的生命周期, 为整个 C 程序

    局部变量的生命周期, 为定义局部变量所在的范围

    变量的生命周期, 可以简单理解为变量的存活时间

    变量的生命周期, 一般是从变量完成定义到其所在作用域结束

  4. 全局变量和局部变量可以同名, 但在使用上 遵循就近原则

以实际的代码为例子:

#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()所使用的变量是哪一个:

序号调用语句打印值值对应变量名是否有重名变量实际使用变量
17行: printf("globalVar: %d\n", globalVar);10globalVarglobalVar(第3行)
28行: printf("var: %d\n", var);20varvar(第4行)
311行: printf("var: %d\n", var);30varvar(第10行)
416行: printf("_var: %d\n", _var);40_var_var(第14行)
517行: printf("var: %d\n", var);50varvar(第15行)
621行: printf("var: %d\n", var);30varvar(第10行)

从结果来看, 可以分析出一些特性:

  1. 全局变量和局部变量可以重名

    编译没有问题, 运行也没有问题

  2. 变量的使用, 遵循就近原则

    即, 如果存在重名变量, 使用时 会使用已经定义的, 离调用位置近的变量

    在例子中表现为:

    局部变量var没有定义时, 使用var变量, 实际使用的是全局变量var

    局部变量var定义后, 使用var变量, 实际使用的是局部变量var

  3. 变量只在其作用域内生效

    即, 变量出了其作用域, 其生命周期就会结束, 无法再使用

    在例子中的表现为:

    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;
}

这种情况下, 编译会失败, 并且会出现报错: 变量重定义


总结来讲, 究竟什么是变量?

变量是一个命名的存储单元, 它可以用来存储程序运行过程中可能发生变化的数据

它有: 名字(标识符)、类型(数据类型决定它能存什么数据)、大小(由类型决定)、作用域(在哪能访问)、生命周期(存在多久)

还有这些特点:

  1. 类型明确: 每个变量都有明确的数据类型(如 intfloat 等)

  2. 内存占用不同: 不同类型的变量占用不同的字节数

  3. 命名有规则:

    只能由字母、数字和下划线组成, 且不能以数字开头

    不能与 C 语言中的关键字完全一致

  4. 作用域限制:

    全局变量在程序内全局有效

    局部变量只能在其定义的代码块中有效.

  5. 生命周期不同:

    局部变量: 随函数或代码块执行完毕而消失

    全局变量: 存在于整个程序生命周期

  6. 支持赋值与修改: 变量可以在程序中多次赋新值

  7. 使用需先定义: 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;
作者
Humid1ch
发布于
2025-07-08
许可协议
CC BY-NC-SA 4.0