13.1 内存对齐
1. 什么是内存对齐
内存对齐是指数据在内存中存放时按其类型大小的整数倍对齐的规则。这种对齐方式可以提高CPU的读取效率。
内存对齐规则:
- 数据类型的大小决定了其对齐边界。例如,
int
类型通常需要4字节对齐,即它的地址应该是4的倍数。
2. 使用#pragma pack
调整对齐方式
可以使用#pragma pack
来调整结构体的对齐方式。
示例:
#include <stdio.h>
#pragma pack(push, 1) // 设置为1字节对齐
struct PackedStruct {
char c;
int i;
};
#pragma pack(pop) // 恢复默认对齐
int main() {
struct PackedStruct ps;
printf("Size of packed struct: %lu\n", sizeof(ps)); // 输出结果通常为5,而不是默认的8
return 0;
}
13.2 volatile
关键字
1. volatile
的用途
volatile
关键字告诉编译器,不要对声明的变量进行优化,因为该变量可能会被程序以外的因素(如硬件、其他线程)修改。
示例:
#include <stdio.h>
volatile int flag = 0; // 告诉编译器 flag 可能会被异步修改
void someFunction() {
while (!flag) {
// 等待 flag 被其他线程或中断修改
}
printf("Flag has been set!\n");
}
13.3 使用restrict
关键字
1. restrict
的用途
restrict
关键字用于指针,告诉编译器该指针是唯一访问其指向对象的方式。这为编译器提供了优化的机会。
示例:
#include <stdio.h>
void add(int *restrict a, int *restrict b, int *restrict result) {
*result = *a + *b;
}
int main() {
int x = 5, y = 10, z;
add(&x, &y, &z);
printf("Result: %d\n", z);
return 0;
}
13.4 位操作与位域
1. 位操作
位操作用于直接操作二进制位,常用于底层编程和优化。
常用位操作符:
&
:按位与|
:按位或^
:按位异或~
:按位取反<<
:左移>>
:右移
示例:
#include <stdio.h>
int main() {
unsigned char a = 5; // 二进制:0000 0101
unsigned char b = 9; // 二进制:0000 1001
printf("a & b = %d\n", a & b); // 输出:1(二进制:0000 0001)
printf("a | b = %d\n", a | b); // 输出:13(二进制:0000 1101)
printf("a ^ b = %d\n", a ^ b); // 输出:12(二进制:0000 1100)
printf("~a = %d\n", (unsigned char)~a); // 输出:250(二进制:1111 1010)
printf("a << 1 = %d\n", a << 1); // 输出:10(二进制:0000 1010)
printf("b >> 1 = %d\n", b >> 1); // 输出:4(二进制:0000 0100)
return 0;
}
2. 位域
位域允许在结构体中定义按位存储的变量,通常用于节省存储空间。
定义位域:
struct {
unsigned int a : 1; // 占1位
unsigned int b : 3; // 占3位
unsigned int c : 4; // 占4位
} bitField;
示例:
#include <stdio.h>
struct BitField {
unsigned int a : 1;
unsigned int b : 3;
unsigned int c : 4;
};
int main() {
struct BitField bf = {1, 5, 9};
printf("Size of BitField: %lu\n", sizeof(bf));
printf("a = %u, b = %u, c = %u\n", bf.a, bf.b, bf.c);
return 0;
}
13.5 使用宏和内联函数进行优化
1. 宏定义
宏是一种预处理指令,用于定义常量、简化代码和条件编译。
示例:
#include <stdio.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b)) // 宏定义最大值函数
int main() {
int x = 5, y = 10;
printf("Max of %d and %d is %d\n", x, y, MAX(x, y));
return 0;
}
2. 内联函数
内联函数是使用 inline
关键字定义的函数,建议编译器将其内联展开,以减少函数调用的开销。
示例:
#include <stdio.h>
inline int square(int x) {
return x * x;
}
int main() {
int num = 5;
printf("Square of %d is %d\n", num, square(num));
return 0;
}
13.6 使用static
关键字
1. static
局部变量
static
局部变量在函数调用结束后依然存在,并且保留其值。
示例:
#include <stdio.h>
void increment() {
static int counter = 0; // 静态局部变量
counter++;
printf("Counter: %d\n", counter);
}
int main() {
increment();
increment();
increment();
return 0;
}
2. static
全局变量
static
全局变量的作用域限制在声明它的文件中,使得它在其他文件中不可见。
3. static
函数
static
函数的作用域仅限于声明它的文件,防止函数名冲突。
13.7 变长数组(VLA)
变长数组(Variable Length Array, VLA)是一种数组,其大小在运行时决定。在C99标准中引入。
示例:
#include <stdio.h>
void printArray(int n) {
int arr[n]; // 变长数组
for (int i = 0; i < n; i++) {
arr[i] = i;
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
printArray(5);
return 0;
}
练习
- 编写一个程序,使用
volatile
关键字来保护一个全局变量,并演示其用法。 - 编写一个使用位操作实现简单加密和解密的程序。
- 使用
#pragma pack
编写一个程序,演示结构体的内存对齐和不同对齐方式下的大小变化。 - 编写一个程序,使用
restrict
指针关键字优化矩阵乘法运算。 - 使用变长数组编写一个程序,计算一个输入矩阵的转置。
完成这些练习后,你将掌握C语言的高级特性和技巧。