常量和变量

1. 常量和变量概述

所谓常量就是在程序运行过程中,其值不能被改变的量,在C语言中,常量有很多种不同的类型,如下表:

常量类型 示例
整型常量 100,200,-100,0
浮点型常量 3.14 , 0.125,-3.123
字符型常量 ‘a’,‘b’,‘1’,‘\n’
字符串常量 “a”,“ab”,“12356”

变量(variable)可以理解成一块内存区域的名字。通过变量名,可以引用这块内存区域,获取里面存储的值。由于值可能发生变化,所以称为变量,否则就是常量了。

接下来一节我们来学习一下如何去定义一个变量名。

2. 变量名

变量名在 C 语言里面属于标识符(identifier),命名有严格的规范。

  • 只能由字母(包括大写和小写)、数字和下划线(_)组成。
  • 标识符中字母区分大小写。
  • 不能以数字开头。
  • 第一个字符必须为字母或下划线。
  • 标识符不能是关键字。
  • 长度不能超过63个字符。

下面是一些无效变量名的例子:

1
2
3
4
5
6
7
2bdog            // 不能数字开头
$American // 不能用_或者字母以外的字符开头
// 标识符只能由字母、数字、下划线(_)组成
mei**nv
hot-dog
don't
hello world

另外,两个下划线开头的变量名,以及一个下划线 + 大写英文字母开头的变量名,都是系统保留的,比建议自己这样给变量起名。

1
2
3
4
5
// 不建议的起名方式:
__hello
__world
_Nihao
_Shijie

3. 数据类型关键字

在C语言中为我们提供了一系列的基本类型关键字,通过这些关键字来指定定义出的变量的类型。

K&C给出了7个与类型相关的关键字,C90标准添加了2个关键字,C99标准又添加了3个关键字,如下表:

K&R 是指《The C Programming Language》一书的作者Brian Kernighan和Dennis Ritchie,它是一本经典的C语言教材。

  • K&R 给出了7个与类型(type)相关的关键字:

    类型 描述
    char 表示字符类型
    short 表示短整数类型
    unsigned 表示无符号类型的整数
    int 整数类型
    long 表示长整数类型
    float 表示单精度浮点数类型
    double 表示双精度浮点数类型
  • C90 、C99标准添加的关键字

    类型 描述
    signed 表示有符号类型的整数
    void 表示无类型,常用于函数返回类型或函数参数列表中的空参数
    _Bool bool 关键字具有相同的作用,用于声明布尔类型

    关于_Complex_Imaginary分别用于表示复数虚数(二者皆是数学概念),不常用,初学者可以无视,直接跳过即可。

4. 变量的声明和定义

在C语言中变量的声明和定义是有区别的,它们的区别就是是否会分配存储空间:

  • 声明是指在程序中提前告诉编译器变量的名称和类型,而不进行实际的内存分配
  • 定义是指除了告诉编译器变量的名称和类型之外,还会为变量分配内存

4.1 变量的定义

C 语言的变量,必须先定义后使用。如果一个变量没有定义,就直接使用,会报错。每个变量都有自己的类型(type),定义变量时,必须把变量的类型告诉编译器。

在C语言中,变量的定义方式是这样的:

1
2
3
4
5
6
// 语法
数据类型 变量名;
数据类型 变量名 = 值;
// 示例
int age;
int age = 13;

定义了一个名为age的变量,它对应的数据类型是整形

如果几个变量具有相同类型,可以在同一行定义 ,如下所示:

  • 定义变量但未初始化:

    1
    2
    3
    4
    5
    int id, age, length;
    // 上面一行代码相当于下面三行
    int id;
    int age;
    int length;
  • 定义变量并初始化:

    1
    2
    3
    4
    5
    6
    7
    int id = 9, age = 2, length = 7;
    // 上面一行代码相当于下面三行
    int id = 9;
    int age = 2;
    int length = 7;
    // 语法正确, 但是不建议这样写, 容易让人误以为 id 也是有初始值的
    int id, age = 2;

    对于上面的代码来说等号左侧的我们可以将其称之为变量,等号右侧的值我们可以称之为字面量,字面量(literal)指的是代码里面直接出现的值(注意必须是一个值,一个值,一个值)。下面举例说明:

    1
    2
    3
    4
    int id = 9;          // 9 是字面量, id 是变量
    int age = 2; // 2 是字面量, age 是变量
    int length = 7; // 7 是字面量, length 是变量
    int size = length; // size 和 length 都是变量

注意事项:定义变量的语句必须以分号结尾。一旦变量被定义,它的类型就不能在运行时修改了。

4.2 变量的声明

从广义的角度来看,在定义变量的时候也包含着对变量的声明,所以下面例子中的语句既是声明也是定义:

1
2
3
4
int count;
int number = 100;
int a, b, c;
int a=1, b=2, c=3;

但是有一种情况是实实在在的变量声明,此时需要用到一个关键字extern,将它置于要声明的变量的前面,如下:

1
2
3
// 通过 extern 关键字告知编译器 number,count 变量已被定义,类型是int
extern int number;
extern int count;

上面两句代码表示整形变量numbercount已经在其它位置被定义出来了(假设是在源文件 a.c 中定义的),这就意味着变量对应的内存空间都已分配完毕,接下来需要在源文件b.c中使用这两个变量,此时就需要告诉编译器这两个变量已经存在了,要做的事情就是对变量进行声明,所以在使用之前把上面两句代码写到源文件b.c中就可以了。

4.3 变量的赋值

C 语言会在变量定义时为它分配内存空间,但是不会清除内存里面原来的值。这导致定义变量以后,变量会是一个随机的值。所以,变量一定要赋值以后才能使用。赋值操作通过赋值运算符(=)完成。

1
2
3
int number;
number = 123; // 赋值
number = 321; // 修改变量的值, 此时变量值从 123 变为 321

上面的例子中定义了一个整形变量number并且没有初始化,所以它的默认值是随机的,此时可以通过=为其指定一个正确的值,当然我们也可以通过= 去修改某一个变量的值(变量对应的内存中的数据被覆盖了)。

image-20230810091820234

= 前后的空格不是必须的,但建议大家加上,这是一种良好的编程习惯。

1
2
3
int number = 123;
int x, y, z;
x = y = z = 250;

对于示例中的number而言不存在值为随机数的情况,因为在定义这个变量的时候就已经进行了初始化。

对于示例中的x, y, z而言,它们的值是随机的,如果想要给这三个变量设置相同的值,可以将赋值操作写到一行(x = y = z = 250;)。

4.4 常量的定义

在C语言中想要定义常量(Constant),我们必须要了解常量有什么特点:

  • 常量是用来存储不可变的数据,其值在程序执行过程中不能被修改。
  • 常量在使用前可以直接赋值,一旦赋值后就不能再修改了。
  • 常量的内存空间在编译时就确定,并且常量通常存储在只读存储器中(如常量区)。
  • 常量的值在程序中可以直接使用,通常用于表示固定的数值、字符串或者其他不可变的数据。

在C语言中想要定义常量和定义变量差不多,但是需要多使用一个关键字const,通过这个关键字标记标识符中存储的值是只读的、不可修改的。

1
2
3
4
5
6
7
const int value1 = 520;      // 整型常量
const char value2 = 'a'; // 字符型常量
const char value3 = 3.14; // 浮点型常量
//////// 华丽的分割线 ///////
int const value1 = 520; // 整型常量
char const value2 = 'a'; // 字符型常量
char const value3 = 3.14; // 浮点型常量

在C语言中,const intint const 没有实质性的区别,它们都用于声明一个常量整数,表示整数是一个常量,其值不可更改。相比较而言const int 这种形式更常用一些。

在定义常量的时候有一些细节必须要知道,否则会出现语法错误:

  1. 定义常量的时候必须直接进行初始化。
  2. 定义出的常量对应的值是不允许修改的。
1
2
3
4
const int num = 99;     // ok
const int count; // 语法没问题,但是逻辑上有问题
count = 9; // error
num = 100; // error
  • 1 行:定义了常量 num 并进行了初始化,常量值为99
  • 2行:定义了常量 count 没有进行初始化,常量值是随机
  • 3、4 行:语法错误, 无法对常量进行赋值

4.5 printf() 函数

printf()的作用是将参数文本输出到屏幕。它名字里面的f代表format(格式化),表示可以定制输出文本的格式。该函数是标准C库函数,函数在stdio.h中进行了声明,其函数原型如下:

1
2
#include <stdio.h>
int printf(const char *format, ...);

通过函数声明可以得知:

  • 函数返回值为整形,也就是函数执行完毕之后会返回一个整数
  • 函数的参数是一个字符串(char* 类型)
  • 这是一个变参函数,即参数个数不定(… 表示变参
image-20230807113242106

如果想要打印一个字符串,对应的语句可以这样写:

1
printf("Hello World\n");

上面语句中要输出的字符串为Hello World,在C/C++中字符串要放到一对双引号中(“”),末尾的\n是一个换行符,这是一个特殊字符。

使用printf函数不仅可以打印常量字符串,还可以打印变量的值,此时就需要用到一些占位符,不同的占位符对应的数据类型也不同,下面先介绍一些常用的占位符,占位符的第一个字符一律为百分号%

占位符 描述
%c 字符,对应 char 类型
%d 十进制整数, 对应int类型
%ld 十进制整数,对应 long int 类型
%s 字符串, 对应 char* 类型
%f 浮点数(小数),对应 floatdouble 类型
%o 八进制整数
%x 十六进制整数
%% 输出一个百分号

printf()可以在输出文本中指定占位符。所谓“占位符”,就是这个位置可以用其他值代入。接下通过举例来为大家演示占位符的使用方法:

1
2
3
4
5
6
7
#include <stdio.h>
int main()
{
printf("There are %d apples\n", 3);
printf("%s will come tonight\n", "Tom");
printf("%c and %c are %d English characters", 'A', 'B', 2);
}
  • 第 1 行:%d表示整数,会被替换为 3
  • 第 2 行:%s表示字符串,会被替换为 Tom
  • 第 3 行:%c表示字符,按照从前到后的顺序依次被替换为AB

以上语句打印出的信息如下:

1
2
3
There are 3 apples
Tom will come tonight
A and B are 2 English characters

printf()参数与占位符是一一对应关系,如果有n个占位符,printf()的参数就应该有n + 1个。如果参数个数少于对应的占位符,printf()可能会输出内存中的任意值。

5. 类型限定符

限定符 含义
extern 声明一个变量,extern声明的变量没有建立存储空间。extern int a;
const 定义一个常量,常量的值不能修改。const int a = 10;
volatile 防止编译器优化代码 volatile int a = 10;
register 定义寄存器变量,提高效率。register是建议型的指令,而不是命令型的指令,如果CPU有空闲寄存器,那么register就生效,如果没有空闲寄存器,那么register无效。register int counter = 9;

在现代编译器中,对于自动变量的存储和访问,编译器通常会根据程序的优化级别和目标平台上的寄存器数量等因素进行智能优化。因此,程序员通常不需要显式使用 register 关键字来优化变量的存储和访问。