接上集我们继续
常量
常量:程序运行的过程中,不能修改的,常见的有字面值常量
在C语言中,常量是程序运行过程中值不能被改变的量。正确使用常量不仅能提高代码可读性(消除“魔术数字”),还能便于维护和减少错误。
C语言提供了4种定义常量的方式,每种方式在内存、类型检查和适用场景上都有显著区别。以下是深度对比和使用指南:
- 字面常量(直接量) 这是最直观的形式,直接在代码中写数值或字符。
int a = 100; // 100 是整型字面量
float b = 3.14; // 3.14 是浮点字面量
char c = 'A'; // 'A' 是字符字面量
char *s = "Hello"; // "Hello" 是字符串字面量(存储在只读数据段,修改它会导致段错误)注意:字符串字面量本质是只读的,尝试 s[0]=‘h’; 会引发运行时崩溃。
- #define 宏常量(预处理阶段)
由预处理器处理,在编译前进行纯文本替换。它不占用运行时内存(不分配变量地址)。
#define MAX_SIZE 100
#define PI 3.14159
#define SQUARE(x) ((x)*(x)) // 注意:宏容易产生副作用,务必加括号优点:任何地方都能用,可用于定义数组长度(编译期常量)。 致命缺点:没有类型检查,编译器不检查类型,容易因类型不匹配产生隐蔽 bug,且调试时看不到符号(宏已被替换)。
- 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 在 * 左边修饰数据,在 * 右边修饰指针本身。- enum 枚举常量(整型专用)
用于定义一组具有名字的整型常量,非常适合状态码、标志位。
enum Color { RED = 0, GREEN, BLUE }; // GREEN 自动为 1,BLUE 为 2
enum Status { SUCCESS = 1, FAIL = -1 };
// 它是编译期常量,可以直接用作数组大小(enum { SIZE = 10 }; int arr[SIZE];)。相比 #define,枚举会进行类型检查,且调试器能显示变量名。
- 核心选择指南(什么时候用哪个?) 场景 推荐方案 原因 定义数组长度 /
Case 标签 #define 或 enum 必须是编译期常量(C语言中 const 达不到)
浮点数、字符串、结构体等复杂类型 const 有类型检查,作用域可控,更安全
一组相关的整数(如状态码) enum 语义清晰,自动递增,调试友好
全局共享常量(跨文件) .h 文件中用 extern const int GLOBAL_VAL;,在 .c
文件中定义 避免宏污染全局命名空间,且便于链接 - 进阶技巧: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=10float a = 10.3;
int b = (int)a; //b=10int a = 50000;
short b = a//会数据溢出占位符
%d %c %p %s
用法:
- 限定宽度(默认向右对齐)
printf("%5d",123);//输出结果:" 123"(前面有2个空格,总宽度为5)向左对齐
printf("%-5d",123);输出结果:"123 "(后面有2个空格,总宽度为5)- 限定小数位数
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的值给bint a = 10;
int b = a++;//a=10,b=11
//先给b赋值a的值,后a的值自增1a++是先增加后使用,++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短路或
如果左边的表达式满足逻辑运算符条件,就不再对右边表达式求值
Comments NOTHING