sizeof 深度解析:避免 C 语言编程中的常见内存陷阱

sizeof 深度解析:避免 C 语言编程中的常见内存陷阱

sizeof 深度解析:避免 C 语言编程中的常见内存陷阱

2025-12-03

sizeof 是 C 语言中的一个一元运算符 (unary operator),用于获取类型 (type) 或变量 (variable) 所占用的内存字节数。

它在编译时 (compile time) 就会计算出结果,而不是在程序运行时。

它的返回值类型是 size_t,这是一个无符号整型(通常是 unsigned int 或 unsigned long),取决于你的系统和编译器。在打印 size_t 类型的值时,推荐使用格式说明符 %zu。

#include

#include // size_t 类型通常在这里或 中定义

int main() {

int a = 10;

int arr[5]; // 包含 5 个 int 元素的数组

// 1. 用于类型名 (需要括号)

printf("int 类型的大小: %zu 字节\n", sizeof(int));

// 2. 用于变量名 (不需要括号,但推荐加上,因为看起来像函数调用)

printf("变量 a 的大小: %zu 字节\n", sizeof(a));

printf("变量 a 的大小: %zu 字节\n", sizeof(int)); // 推荐用类型名

// 3. 用于整个数组

printf("数组 arr 的总大小: %zu 字节\n", sizeof(arr)); // 5 * sizeof(int)

// 4. 计算数组元素的个数 (非常常用!)

size_t count = sizeof(arr) / sizeof(arr[0]);

printf("数组 arr 的元素个数: %zu\n", count); // 5

return 0;

}

这是初学者最容易遇到的陷阱!

当你将一个数组作为参数传递给函数时,C 语言会发生数组名退化 (array decay)。在函数内部,数组名不再代表整个数组,而是退化为一个指向其第一个元素的指针。

#include

#include

// 这里的 int arr[] 实际上等同于 int* arr

void print_array_size(int arr[]) {

// 陷阱! sizeof(arr) 返回的是指针 (int*) 的大小,而不是整个数组的大小。

size_t size = sizeof(arr);

printf("在函数内部,sizeof(arr) 返回: %zu 字节\n", size);

// 例如,在 64 位系统上可能是 8 字节,而不是 5 * 4 = 20 字节

}

int main() {

int data[5] = {1, 2, 3, 4, 5};

printf("在 main 函数中,sizeof(data) 返回: %zu 字节\n", sizeof(data)); // 20 字节 (5 * 4)

print_array_size(data);

return 0;

}

最标准和可靠的方法是显式地传递数组的长度。

#include

#include

// 正确做法:传递数组本身 (指针) 和它的长度 (size_t)

void process_array(int* arr, size_t length) {

printf("函数知道的数组长度: %zu\n", length);

// 现在你可以安全地使用 length 来遍历数组了

}

int main() {

int data[5] = {1, 2, 3, 4, 5};

// 计算长度并传递

size_t count = sizeof(data) / sizeof(data[0]);

process_array(data, count);

return 0;

}

当你对一个 struct 使用 sizeof 时,结果可能大于其所有成员大小的总和。这是因为编译器为了提高内存访问效率,会引入填充字节 (padding) 来确保成员地址的对齐 (alignment)。

#include

#include

// 假设在一个 64 位系统上:

// sizeof(char) = 1, sizeof(int) = 4, sizeof(double) = 8

struct Example {

char c1; // 1 字节

int i; // 4 字节

char c2; // 1 字节

double d; // 8 字节

};

int main() {

// 成员总大小: 1 + 4 + 1 + 8 = 14 字节

// 但实际 sizeof(struct Example) 可能返回 24 字节!

// 这是因为对齐和填充 (padding) 的作用。

printf("结构体 Example 的大小: %zu 字节\n", sizeof(struct Example));

return 0;

}

注意 结构体实际大小由编译器和平台决定,但通常遵循对齐规则,即整个结构体的大小是其最大对齐要求(通常是结构体中最大成员的大小)的整数倍。

为了减少填充字节,你可以调整结构体成员的声明顺序,通常建议将占用空间小的成员放在一起,将占用空间大的成员放在前面。

sizeof 不能用于某些不完整或非对象类型

void 类型 void 不能声明变量,所以 sizeof(void) 是非法的(除非是 void* 指针,它的大小等于普通指针的大小)。

函数 sizeof 不能直接用于函数,但可以用于指向函数的指针。

位域 (Bit fields) 不能单独对位域成员使用 sizeof。

外部数组 (extern arrays) 如果没有提供大小,它们是不完整类型。

// 编译错误: 'sizeof' 运算符的操作数无效

// printf("%zu\n", sizeof(void));

// 正确: 可以对 void 指针使用 sizeof

void* ptr = NULL;

printf("void* 指针的大小: %zu 字节\n", sizeof(ptr)); // 比如 8 字节

如果你想知道结构体中某个成员相对于结构体起始位置的偏移量 (offset),可以使用 中定义的 offsetof 宏。

#include

#include // 需要包含此头文件

struct Point {

char id;

int x;

int y;

};

int main() {

// 计算 x 成员相对于结构体起始地址的偏移量

size_t offset_x = offsetof(struct Point, x);

printf("x 成员的偏移量: %zu 字节\n", offset_x);

// 结果可能是 4 字节,因为 id (1 字节) 后会填充 3 个字节来对齐 int x。

return 0;

}

在进行动态内存分配(如使用 malloc 或 calloc)时,sizeof 是必不可少的,它可以确保你分配了正确数量的内存。这是 sizeof 最重要的应用场景之一。

#include

#include // for malloc and free

#include

int main() {

int num_elements = 10;

int *arr;

// 正确做法:使用 sizeof(int) 来分配 10 个 int 所需的内存

// 最好使用 sizeof(*arr) 而不是 sizeof(int),这样如果 arr 的类型改变,这里也不需要修改

arr = (int *)malloc(num_elements * sizeof(*arr));

if (arr == NULL) {

// 内存分配失败处理...

return 1;

}

// ... 使用 arr ...

free(arr);

return 0;

}

相关推荐

fpga用什么语言编程
beat365中文官网

fpga用什么语言编程

📅 09-20 👁️ 1366
骉、轟、毳、瞐……这些汉字,你知道怎么读吗
beat365中文官网

骉、轟、毳、瞐……这些汉字,你知道怎么读吗

📅 06-18 👁️ 7400
还原特色小吃傣味凉拌舂茄子
365足球体育网站

还原特色小吃傣味凉拌舂茄子

📅 10-18 👁️ 3937
中国有嘻哈
beat365中文官网

中国有嘻哈

📅 07-05 👁️ 9519
在携程买机票怎么打印行程单
beat365上不去

在携程买机票怎么打印行程单

📅 08-29 👁️ 6000
黑暗骑士艾希的皮肤特效
365足球体育网站

黑暗骑士艾希的皮肤特效

📅 12-24 👁️ 9166