一
1. 数据类型概述
C语言的数据类型系统是其核心特性之一,它决定了如何存储数据以及可以对数据执行哪些操作。C语言的数据类型可以分为以下几大类:
2. 基本数据类型
2.1 整型 (Integer Types)
| 类型 | 大小(通常) | 取值范围 | 格式说明符 |
|---|---|---|---|
| char | 1字节 | -128 到 127 或 0 到 255 | %c, %d |
| unsigned char | 1字节 | 0 到 255 | %c, %u |
| short | 2字节 | -32,768 到 32,767 | %hd |
| unsigned short | 2字节 | 0 到 65,535 | %hu |
| int | 4字节 | -2,147,483,648 到 2,147,483,647 | %d |
| unsigned int | 4字节 | 0 到 4,294,967,295 | %u |
| long | 4或8字节 | 取决于系统 | %ld |
| unsigned long | 4或8字节 | 取决于系统 | %lu |
| long long | 8字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 | %lld |
| unsigned long long | 8字节 | 0 到 18,446,744,073,709,551,615 | %llu |
示例代码:
#include <stdio.h>
int main() {
int a = 10;
unsigned int b = 20;
long c = 100000L;
printf("int: %d, unsigned int: %u, long: %ld\n", a, b, c);
return 0;
}
运行结果:(以vs2022为例)

2.2 浮点型 (Floating-Point Types)
| 类型 | 大小(通常) | 精度 | 取值范围 | 格式说明符 |
|---|---|---|---|---|
| float | 4字节 | 6-7位小数 | ±3.4e±38 | %f |
| double | 8字节 | 15-16位小数 | ±1.7e±308 | %lf |
| long double | 10或16字节 | 19-20位小数 | 取决于系统 | %Lf |
示例代码:
#include <stdio.h>
int main() {
float f = 3.14f;
double d = 3.1415926535;
long double ld = 3.141592653589793238L;
printf("float: %.2f, double: %.10lf, long double: %.15Lf\n", f, d, ld);
return 0;
}
运行结果:(以vs2022为例)

2.3 字符型 (Character Type)
char类型用于存储单个字符,实际上存储的是ASCII码值。
示例代码:
#include <stdio.h>
int main() {
char grade = 'A';
printf("Grade: %c, ASCII value: %d\n", grade, grade);
// 字符运算
char next_grade = grade + 1;
printf("Next grade: %c\n", next_grade);
return 0;
}
2.4 布尔型 (Boolean Type)
C99标准引入了_Bool类型,需要包含stdbool.h头文件来使用bool、true和false。
示例代码:
#include <stdio.h>
#include <stdbool.h>
int main() {
bool is_c_programming_fun = true;
bool is_math_hard = false;
printf("%d\n", is_c_programming_fun); // 输出 1
printf("%d\n", is_math_hard); // 输出 0
return 0;
}
3. 派生数据类型
3.1 数组 (Arrays)
存储相同类型元素的集合。
示例代码:
#include <stdio.h>
int main() {
// 一维数组
int numbers[5] = {1, 2, 3, 4, 5};
// 二维数组
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// 访问数组元素
for (int i = 0; i < 5; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}
运行结果:(以vs2022为例)

3.2 指针 (Pointers)
存储变量内存地址的变量。
示例代码:
#include <stdio.h>
int main() {
int var = 20;
int *ptr = &var;
printf("变量值: %d\n", var);
printf("指针地址: %p\n", ptr);
printf("通过指针访问值: %d\n", *ptr);
return 0;
}
3.3 结构体 (Structures)
允许存储不同类型的数据项。
示例代码:
#include <stdio.h>
#include <string.h>
// 定义结构体
struct Student {
char name[50];
int age;
float gpa;
};
int main() {
// 声明结构体变量
struct Student student1;
// 访问结构体成员
strcpy(student1.name, "张三");
student1.age = 20;
student1.gpa = 3.8;
printf("姓名: %s\n", student1.name);
printf("年龄: %d\n", student1.age);
printf("GPA: %.2f\n", student1.gpa);
return 0;
}
3.4 联合体 (Unions)
类似结构体,但所有成员共享同一内存空间。
示例代码:
#include <stdio.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
data.i = 10;
printf("data.i: %d\n", data.i);
data.f = 220.5;
printf("data.f: %.2f\n", data.f);
// 注意:此时data.i的值已被覆盖
printf("data.i after assigning float: %d\n", data.i);
return 0;
}
3.5 枚举 (Enums)
定义一组命名的整数常量。
示例代码:
#include <stdio.h>
enum Weekday {
MONDAY, // 0
TUESDAY, // 1
WEDNESDAY, // 2
THURSDAY, // 3
FRIDAY, // 4
SATURDAY, // 5
SUNDAY // 6
};
int main() {
enum Weekday today = WEDNESDAY;
printf("Today is: %d\n", today);
if (today == WEDNESDAY) {
printf("It's Wednesday!\n");
}
return 0;
}
4. 类型限定符
4.1 const
定义不可修改的常量。
const int MAX_VALUE = 100;
// MAX_VALUE = 200; // 错误:不能修改const变量
4.2 volatile
告诉编译器变量可能在程序之外被修改。
volatile int hardware_register;
4.3 restrict
C99引入,用于指针,表示指针是访问数据的唯一方式。
void copy_array(int *restrict dest, const int *restrict src, size_t n);
5. 类型转换
5.1 隐式类型转换
编译器自动进行的类型转换。
int i = 10;
float f = i; // int自动转换为float
5.2 显式类型转换(强制类型转换)
程序员明确指定的类型转换。
float f = 3.14;
int i = (int)f; // float强制转换为int,i的值为3
6. 类型定义 (typedef)
为现有类型创建别名。
#include <stdio.h>
typedef unsigned int uint;
typedef struct {
char name[50];
int age;
} Person;int main() {
uint count = 10;
Person p1 = {"李四", 25};
printf("Count: %u\n", count);
printf("Name: %s, Age: %d\n", p1.name, p1.age);
return 0;
}
二
C语言运算符与表达式 完整学习指南
一、运算符分类总览
| 类别 | 运算符 | 说明 |
|---|---|---|
|
算术运算符 |
|
基本数学运算 |
|
关系运算符 |
|
比较大小,返回布尔值(0/1) |
|
逻辑运算符 |
| |
|
位运算符 |
|
|
|
赋值运算符 |
|
将值赋给变量 |
|
条件运算符 |
|
三元条件表达式 |
|
逗号运算符 |
|
顺序执行多个表达式 |
|
自增/自减运算符 |
|
前置与后置形式 |
|
指针与地址运算符 |
|
解引用与取地址 |
|
sizeof 运算符 |
|
获取类型或变量所占字节数 |
|
下标运算符 |
|
数组访问 |
|
成员访问运算符 |
|
结构体/联合体成员访问 |
|
函数调用运算符 |
|
调用函数 |
⚠️ 注意:C语言没有布尔类型(C99前),逻辑结果用 0(假)和 非0(真)表示。
二、算术运算符(Arithmetic Operators)
| 运算符 | 含义 | 示例 | 说明 |
|---|---|---|---|
|
|
加法 |
|
可用于数值和指针(指针偏移) |
|
|
减法 |
|
可用于数值、指针差值 |
|
|
乘法 |
|
也可用于指针解引用 |
|
|
除法 |
|
整数除法截断小数,浮点保留 |
|
|
取模(余数) |
|
仅限整型,不能用于浮点 |
1. 整数除法
int a = 7, b = 3;
printf("%d\n", a / b); // 输出 2(不是2.333)
printf("%d\n", a % b); // 输出 1
2. 取模运算符 %
- 被除数为负时,结果符号取决于编译器(C99标准规定:结果符号与被除数一致)
(-7) % 3 → -1 (C99标准)
7 % (-3) → 1
(-7) % (-3) → -1
3. 指针算术
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr; // p 指向 arr[0]
p++; // p 指向 arr[1],地址 + sizeof(int)
printf("%d\n", *(p + 2)); // 输出 30
✅ 指针 + n 表示移动 n 个元素,不是 n 字节!
三、关系运算符(Relational Operators)
| 运算符 | 含义 | 返回值 |
|---|---|---|
|
|
等于 |
1(真)或 0(假) |
|
|
不等于 |
—— |
|
|
小于 |
—— |
|
|
大于 |
—— |
|
|
小于等于 |
—— |
|
|
大于等于 |
—— |
✅ 示例:
int x = 5, y = 10;
printf("%d\n", x < y); // 1
printf("%d\n", x == y); // 0
⚠️ 重要陷阱:不要混淆 = 和 ==
if (x = 5) { ... } // ❌ 错误!赋值,结果为5(真),永远执行
if (x == 5) { ... } // ✅ 正确!比较
💡 编译器警告:有些编译器会提示“赋值在条件中”,建议开启 -Wall
四、逻辑运算符(Logical Operators)
| 运算符 | 含义 | 结合性 | 说明 |
|---|---|---|---|
|
|
逻辑与 |
左结合 |
短路求值:左为假则不计算右 |
|
` |
` |
逻辑或 | |
|
|
逻辑非 |
右结合 |
单目,取反 |
✅ 短路求值(Short-Circuit Evaluation)——极其重要!
int a = 0, b = 5;
if (a != 0 && b / a > 1) { ... } // ✅ 安全!不会除零
if (b > 0 || func()) { ... } // ✅ 如果b>0,func()不会被调用
📌 应用场景:
- 防止空指针访问
- 避免昂贵函数调用
- 条件链式判断
char *ptr = get_string();
if (ptr != NULL && strlen(ptr) > 5) {
printf("Long string: %s\n", ptr);
}
⚠️ && 和 || 只能用于布尔上下文,但C中任何非0值都为真。
五、位运算符(Bitwise Operators)
直接对整数的二进制位进行操作,效率极高,常用于嵌入式、网络协议、标志位等。
| 运算符 | 名称 | 说明 | 示例(8位) |
|---|---|---|---|
|
|
按位与 |
两数同为1才为1 |
|
|
` |
` |
按位或 |
任一为1即为1 |
|
|
按位异或 |
相同为0,不同为1 |
|
|
|
按位取反 |
所有位翻转 |
|
|
|
左移 |
左边丢弃,右边补0 |
|
|
|
右移 |
右边丢弃,左边补符号位(有符号)或0(无符号) |
|
✅ 实际应用案例:
1. 设置/清除/切换特定位
#define FLAG_A (1 << 0) // 0001
#define FLAG_B (1 << 1) // 0010
#define FLAG_C (1 << 2) // 0100unsigned int flags = 0;
flags |= FLAG_A; // 设置A位 → 0001
flags |= FLAG_B; // 设置B位 → 0011
flags &= ~FLAG_A; // 清除A位 → 0010
flags ^= FLAG_B; // 切换B位 → 0000
2. 快速乘除(仅适用于2的幂)
x * 8 → x << 3
x / 8 → x >> 3 (仅当x为正整数时成立)
3. 交换两个数(不用临时变量)
int a = 5, b = 10;
a ^= b;
b ^= a;
a ^= b;
// 现在 a=10, b=5
💡 异或交换只适用于整数,且不推荐用于生产代码(可读性差,现代编译器优化更好)
⚠️ 右移的符号扩展问题
char c = -1; // 二进制:11111111(补码)
int i = c; // 扩展为:11111111111111111111111111111111
i >>= 1; // 右移一位:11111111111111111111111111111111 → 还是 -1
如果是无符号:
unsigned char uc = 255; // 11111111
unsigned int ui = uc;
ui >>= 1; // 01111111111111111111111111111111 → 2147483647
✅ 推荐:使用 unsigned 类型进行位操作,避免未定义行为。
六、赋值运算符(Assignment Operators)
| 运算符 | 含义 | 示例 | 等价于 |
|---|---|---|---|
|
|
简单赋值 |
|
— |
|
|
加后赋值 |
|
|
|
|
减后赋值 |
|
|
|
|
乘后赋值 |
|
|
|
|
除后赋值 |
|
|
|
|
取模后赋值 |
|
|
|
|
按位与后赋值 |
|
|
|
` |
=` |
按位或后赋值 |
`a |
|
|
按位异或后赋值 |
|
|
|
|
左移后赋值 |
|
|
|
|
右移后赋值 |
|
|
七、逗号运算符(Comma Operator)
最易被忽视,但非常有用!
语法:expr1, expr2, ..., exprN
- 按顺序求值
- 整个表达式的值是最后一个子表达式的值
- 优先级最低
int a, b, c;
a = (b = 3, c = 5, b + c); // a = 8,先执行 b=3, c=5,最后 b+c=8for (i = 0, j = 10; i < j; i++, j--) {
// 初始化和更新部分使用逗号
}
九、自增与自减运算符(Increment/Decrement)
| 形式 | 名称 | 含义 |
|---|---|---|
|
|
前置 |
先自增/减,再使用值 |
|
|
后置 |
先使用值,再自增/减 |
int a = 5;
int b = ++a; // a=6, b=6
int c = a++; // c=6, a=7printf("a=%d, b=%d, c=%d\n", a, b, c); // 输出:a=7, b=6, c=6
关键区别:
| 表达式 | 计算过程 | 返回值 |
|---|---|---|
|
|
x = x+1 → 返回新值 |
新值 |
|
|
返回旧值 → x = x+1 |
旧值 |
陷阱:连续使用导致未定义行为
int x = 5;
int y = x++ + x++; // ❌ 未定义行为!x 被修改两次
int z = ++x + ++x; // ❌ 同上
十、指针与地址运算符
| 运算符 | 名称 | 说明 |
|---|---|---|
|
|
取地址 |
|
|
|
解引用 |
|
int x = 100;
int *p = &x; // p 存储 x 的地址
*p = 200; // 修改 x 的值 → x 现在是 200
printf("%d\n", x); // 输出 200
十一、sizeof 运算符
获取类型或变量在当前平台上的字节大小。
语法:
sizeof(type)
sizeof(expression)
示例:
printf("sizeof(int): %zu\n", sizeof(int)); // 4
printf("sizeof(char): %zu\n", sizeof(char)); // 1
printf("sizeof(double): %zu\n", sizeof(double)); // 8
printf("sizeof(arr): %zu\n", sizeof(arr)); // 数组总字节数
特殊情况:数组 vs 指针
int arr[10];
int *p = arr;printf("%zu\n", sizeof(arr)); // 40(10 * sizeof(int))
printf("%zu\n", sizeof(p)); // 8(指针大小,与平台有关)
特殊情况:数组 vs 指针
int arr[10];
int *p = arr;printf("%zu\n", sizeof(arr)); // 40(10 * sizeof(int))
printf("%zu\n", sizeof(p)); // 8(指针大小,与平台有关)
十二、下标运算符(Array Subscript)
语法:array[index]
本质是指针算术:
arr[i] ↔ *(arr + i)
示例:
int a[3] = {10, 20, 30};
printf("%d\n", a[1]); // 20
printf("%d\n", 1[a]); // 20!完全合法(因为 a[1] = *(a+1) = *(1+a))
十三、成员访问运算符
| 运算符 | 用途 | 示例 |
|---|---|---|
|
|
结构体/联合体变量访问成员 |
|
|
|
指向结构体/联合体的指针访问成员 |
|
示例:
struct Point {
int x, y;
};struct Point pt = {10, 20};
struct Point *p = &pt;printf("%d, %d\n", pt.x, pt.y); // 使用 .
printf("%d, %d\n", p->x, p->y); // 使用 ->
十四、函数调用运算符
int result = add(3, 5);
()是函数调用运算符- 参数列表可为空:
func() - 函数名本质上是函数指针
函数指针调用:
int (*fp)(int, int) = add;
int r = fp(3, 5); // 等价于 add(3,5)
十五、运算符优先级与结合性(最重要!)
优先级决定谁先运算,结合性决定相同优先级时从左还是右开始。
完整优先级表(从高到低):
| 优先级 | 运算符 | 结合性 | 说明 |
|---|---|---|---|
|
1 |
|
左到右 |
函数调用、下标、成员访问 |
|
2 |
|
右到左 |
单目运算符 |
|
3 |
|
左到右 |
乘除取模 |
|
4 |
|
左到右 |
加减 |
|
5 |
|
左到右 |
位移 |
|
6 |
|
左到右 |
关系运算 |
|
7 |
|
左到右 |
相等 |
|
8 |
|
左到右 |
按位与 |
|
9 |
|
左到右 |
按位异或 |
|
10 |
` |
` |
左到右 |
|
11 |
|
左到右 |
逻辑与 |
|
12 |
` |
` | |
|
13 |
|
右到左 |
条件 |
|
14 |
|
=^=<<=>>=` |
右到左 |
|
15 |
|
左到右 |
逗号 |
以上对C语言的数据类型、运算符、表达式相关的知识点可能会有遗漏与疏忽,望家人们理解。
1427

被折叠的 条评论
为什么被折叠?



