03-C语言变量和数据类型(续集)

plana_lab 发布于 2026-08-22 75 次阅读



接上集我们继续


常量

常量:程序运行的过程中,不能修改的,常见的有字面值常量

在C语言中,常量是程序运行过程中值不能被改变的量。正确使用常量不仅能提高代码可读性(消除“魔术数字”),还能便于维护和减少错误。

C语言提供了4种定义常量的方式,每种方式在内存、类型检查和适用场景上都有显著区别。以下是深度对比和使用指南:

  1. 字面常量(直接量) 这是最直观的形式,直接在代码中写数值或字符。
int a = 100;      // 100 是整型字面量
float b = 3.14;   // 3.14 是浮点字面量
char c = 'A';     // 'A' 是字符字面量

char *s = "Hello"; // "Hello" 是字符串字面量(存储在只读数据段,修改它会导致段错误)

注意:字符串字面量本质是只读的,尝试 s[0]=‘h’; 会引发运行时崩溃。

  1. #define 宏常量(预处理阶段)
    由预处理器处理,在编译前进行纯文本替换。它不占用运行时内存(不分配变量地址)。
#define MAX_SIZE 100
#define PI 3.14159
#define SQUARE(x) ((x)*(x)) // 注意:宏容易产生副作用,务必加括号

优点:任何地方都能用,可用于定义数组长度(编译期常量)。 致命缺点:没有类型检查,编译器不检查类型,容易因类型不匹配产生隐蔽 bug,且调试时看不到符号(宏已被替换)。

  1. const 关键字(编译阶段)—— 现代C的推荐用法 const
    修饰的变量具有类型,编译时会进行类型安全检查,并且有作用域限制。
const int MAX_SIZE = 100;   // 有类型(int),有作用域
const double PI = 3.14159;
重点误区(C语言与C++的区别):
在 C语言 中,const 修饰的变量不是编译期常量,而是只读变量。因此:

❌ 错误:const int N = 10; int arr[N]; (在C标准中,这会导致编译错误,因为数组长度必须为常量表达式。部分编译器支持VLA可变长数组,但那是运行时特性,不推荐依赖)。

✅ 正确:数组长度请用 #define 或 enum。

const 的强悍之处在于修饰指针(必考点):

写法  含义  指针本身可改? 指向的数据可改?
const int *p;   指向常量的指针(底层const)    ✅ 可以    ❌ 不可以
int * const p;  常量指针(顶层const)   ❌ 不可以   ✅ 可以
const int * const p;    指向常量的常量指针   ❌ 不可以   ❌ 不可以
记忆口诀:const 在 * 左边修饰数据,在 * 右边修饰指针本身。
  1. enum 枚举常量(整型专用)
    用于定义一组具有名字的整型常量,非常适合状态码、标志位。
enum Color { RED = 0, GREEN, BLUE }; // GREEN 自动为 1,BLUE 为 2
enum Status { SUCCESS = 1, FAIL = -1 };

// 它是编译期常量,可以直接用作数组大小(enum { SIZE = 10 }; int arr[SIZE];)。

相比 #define,枚举会进行类型检查,且调试器能显示变量名。

  1. 核心选择指南(什么时候用哪个?) 场景 推荐方案 原因 定义数组长度 /
    Case 标签 #define 或 enum 必须是编译期常量(C语言中 const 达不到)
    浮点数、字符串、结构体等复杂类型 const 有类型检查,作用域可控,更安全
    一组相关的整数(如状态码) enum 语义清晰,自动递增,调试友好
    全局共享常量(跨文件) .h 文件中用 extern const int GLOBAL_VAL;,在 .c
    文件中定义 避免宏污染全局命名空间,且便于链接
  2. 进阶技巧:const 修饰函数参数
    这是工程中最常用的“常量”用法,用于保护传入的数据不被函数修改:
// 告诉调用者,这个函数不会修改 str 指向的内容
size_t my_strlen(const char *str) { 
    // str++ 是可以的(指针本身可变),但 *str = 'x' 会报错
    while (*str) str++; 
    return str - original;
}

强制类型转换

这个是一个风险操作,可能会丢失精度

有三种情况分别如下

float a = 10.5;
int b = a  //b=10
float a = 10.3;
int b = (int)a; //b=10
int a = 50000;
short b = a//会数据溢出

占位符

%d %c %p %s

用法:

  1. 限定宽度(默认向右对齐)
printf("%5d",123);//输出结果:"  123"(前面有2个空格,总宽度为5)

向左对齐

printf("%-5d",123);输出结果:"123 "(后面有2个空格,总宽度为5)
  1. 限定小数位数
printf("%.2f",0.5);//输出的是0.50,取值小数点后2位

这些可以组合使用

printf("%6.2d",0);   // 输出 "     00"(4个空格 + 00)
printf("%6.2d",123); // 输出 "   123"(3个空格 + 123,因为精度2不影响3位数)

scanf输入函数的用法

使用例:

int a = 0;
scanf("%d",&a); //在键盘输入整数值来赋值给a,"&"是取地址符

可一次性使用多个字符

int age;
int height;
float time;
char name[20];
scanf("%d %d %f %s",&age,&height,&time,name);

scanf 返回值

含义:正常情况下返回成功读取的变量个数

  无数据读取:如果未能读取如何项或匹配失败 则返回0

  文件结束:如果在成功读取任何数据之前,发生了读取错误或遇到读取到文件结尾,则返回常量 EOF(-1) 

EOF:end of flie 是文件结束标志

使用例:

int a,b;
float f;
int r = scanf("%d %d %f",&a,&b,&f);
printf("r=%d",r);//返回成功读取的变量个数 3

按 ctrl+z 可提前结束输入 :::warming 有格式需要严格按照输入 :::

运算符

一般运算符

“ + ” “ - ” “ * ” “ / ” “ % ”

int a =1+2;//普通整数加法,1 + 2 = 3,结果存入 int 型变量 a,所以 a = 3。
int b =3-2;//普通整数减法,3 - 2 = 1,b = 1。
int c =3*5;//普通整数乘法,3 * 5 = 15,c = 15。
float d = 6/4;//操作数 6 和 4 都是 int 类型,所以 / 执行的是 整数除法,结果直接截断小数部分(向零取整),即 6 / 4 = 1(而不是 1.5)。然后这个整数 1 被隐式转换为 float 类型,赋值给 d,因此 d 的实际值是 1.0f。若想得到 1.5,必须让至少一个操作数为浮点数,例如:float d = 6.0 / 4; (6.0 是 double,除法结果提升为 double,再转为 float)float d = 6 / 4.0;float d = (float)6 / 4; (强制类型转换)
int e = 10%3; //% 是取模(求余)运算符,要求两个操作数必须为整数(int、char、short、long 等)。计算 10 ÷ 3,商为 3,余数为 1,所以 e = 1。

关于取模运算符 % 的补充注意事项 操作数必须为整型,不能用于浮点数(如 10.0 % 3 是编译错误)。

结果的符号:在 C 标准(C99 之后)中,余数的符号与被除数(左操作数)相同。例如:

-10 % 3 的结果是 -1

10 % -3 的结果是 1

-10 % -3 的结果是 -1

复合运算符

赋值

“ = ” 使用例:

int a = 0;

复合运算符

“ *= ” “ /= ” “ += ” “ -= ” “ %= ” “ >>= ” “ <<= ” “ &= ” “ |= ” “ ^= ”

使用例:

int b += 10;//b=b+10

单目运算符

“ ++ ” “ – ”

自增++

有2种用法a++和++a

使用例

int a = 10;
a++;//a=11

int b = 20;
++b;//b=21

区别如下

int a = 10;
int b = ++a;//a=11,b=11
//a先自增1,后将a的值给b
int a = 10;
int b = a++;//a=10,b=11
//先给b赋值a的值,后a的值自增1

a++是先增加后使用,++a先使用后增加

–同理

移位运算符

“ >> ” “ << ”

” << ” 左移

使用例

int a = 10 ;
int b = <<a;//a=10,b=20

原理:10*2=20

” >> ” 右移

右移

使用例

int a = 10 ;
int b = >>a;//a=10,b=5

原理:10/2=5

-1无论右移多少位,他依旧是-1

位操作符

& 按位与

按位与
按位或
按位或

^ 按位异或

按位异或

~ 按位取反

原来是0就变1 是1就变0

关系运算符

” > >= < <= == != ”

==(相等比较运算符):判断左右两侧的值是否相等,返回布尔值(True/False 或 1/0)。

示例:if (a == 10)(判断 a 是否等于 10)。 a = 10 把数字 10 赋给变量 a

如果在 if 条件中误写成 if (a = 5),程序不会报错,而是会把 5 赋给 a,然后判断 a 是否为“真”(非零/非空),导致逻辑错误。这在 C/Java 中编译能通过,但在 Python 中会直接报语法错误(强制避免此风险)。

逗号表达式

用逗号隔开的表达式

使用例

int a = 3, b=4;
int c =(a=b+3,a,b=a+4,a+b)//c=18

从左到右依次执行 整个表达式是最后一个表达式的结果

逻辑运算符

! 取反(非)

反

&& 与(and)

与

|| 或(or)

或

短路与

如果左面的表达式不满足逻辑运算符条件,就不再对右边表达式求值

使用例

if(month>=3 && month<=5)
//如果month>=3不成立则不会执行 month<=5

短路或

如果左边的表达式满足逻辑运算符条件,就不再对右边表达式求值

此作者没有提供个人介绍。
最后更新于 2026-09-12