C语言中sizeof数组结果何时会失真?

99ANYc3cd6
预计阅读时长 18 分钟
位置: 首页 C语言 正文

sizeof 是一个 C 语言中的单目运算符,而不是函数,它的作用是返回一个对象或类型所占的内存字节数。

c语言 sizeof 数组
(图片来源网络,侵删)

sizeof 用于数组时,它返回的是整个数组在内存中所占的总字节数


基本用法:sizeof(数组名)

这是最核心的用法。sizeof 会计算数组所有元素占用的内存总和。

示例代码:

#include <stdio.h>
int main() {
    // 1. 整型数组
    int arr_int[5] = {10, 20, 30, 40, 50};
    // sizeof(int) 通常是 4 字节
    // sizeof(arr_int) = 5 * 4 = 20 字节
    printf("int arr_int[5] 的大小是: %zu 字节\n", sizeof(arr_int)); // 输出 20
    // 2. 字符数组
    char arr_char[10] = "Hello";
    // sizeof(char) 通常是 1 字节
    // sizeof(arr_char) = 10 * 1 = 10 字节 (注意:这里计算的是整个数组的大小,包括预留的空间)
    printf("char arr_char[10] 的大小是: %zu 字节\n", sizeof(arr_char)); // 输出 10
    // 3. 双精度浮点型数组
    double arr_double[3];
    // sizeof(double) 通常是 8 字节
    // sizeof(arr_double) = 3 * 8 = 24 字节
    printf("double arr_double[3] 的大小是: %zu 字节\n", sizeof(arr_double)); // 输出 24
    return 0;
}

关键点:

c语言 sizeof 数组
(图片来源网络,侵删)
  • sizeof(数组名) 得到的是整个数组的大小。
  • 结果可以通过 元素个数 * sizeof(元素类型) 来验证。
  • %zusize_t 类型的正确格式说明符,因为 sizeof 的返回值类型是 size_t

sizeof 与数组名的重要区别

这是最容易出错的地方,当数组名不作为 sizeof 的操作数时,它通常会“退化”(decay)为指向数组首元素的指针。

示例代码:

#include <stdio.h>
int main() {
    int arr[10] = {0};
    printf("sizeof(arr) = %zu\n", sizeof(arr));   // 输出 40 (整个数组的大小)
    // 当数组名用于其他地方(如 &arr, arr+i),它会退化
    int *p = arr; // 等价于 int *p = &arr[0];
    printf("arr 的地址: %p\n", (void*)arr);
    printf("&arr[0] 的地址: %p\n", (void*)&arr[0]);
    printf("p 的地址: %p\n", (void*)p);
    // 三个地址打印出来是一样的
    // 关键区别在这里:
    printf("sizeof(p) = %zu\n", sizeof(p)); // 输出 4 或 8 (取决于系统,是指针的大小)
    // p 是一个指针变量,它存储的是地址,sizeof(p) 是指针本身的大小,
    // 而不是它指向的数组的大小。
    return 0;
}

| 表达式 | 含义 | 结果(假设 int 是4字节,指针是8字节) | | :--- | :--- | :--- | | sizeof(数组名) | 数组占用的总字节数 | 40 (int arr[10]) | | 数组名 (非 sizeof 场合) | 退化为指向首元素的指针 | &arr[0] (一个地址值) | | sizeof(数组名 + 0) | sizeof(一个指针) | 8 (指针的大小) | | sizeof(&数组名) | sizeof(一个指向数组的指针) | 8 (指针的大小) |


作为函数参数的数组

当数组作为函数参数传递时,它会自动退化为指向其首元素的指针,这是 C 语言的一个核心规则。

c语言 sizeof 数组
(图片来源网络,侵删)

示例代码:

#include <stdio.h>
// 这个函数无法通过 sizeof 获取数组真实大小
void print_array_size(int arr_param[], int size) {
    // 在函数内部,arr_param 实际上是一个 int* 指针
    // sizeof(arr_param) 得到的是指针的大小,而不是数组的大小
    printf("在函数内部,sizeof(arr_param) = %zu\n", sizeof(arr_param)); // 输出 8 (64位系统)
    // 必须通过另一个参数来传递数组的大小
    printf("通过参数传递的数组大小是: %d\n", size);
}
int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    printf("在 main 函数中,sizeof(arr) = %zu\n", sizeof(arr)); // 输出 20
    // 调用时,只传递了数组名(它退化为指针)和它的真实大小
    print_array_size(arr, 5);
    return 0;
}

重要结论:

  • 你无法在函数内部通过 sizeof 来获取传递过来的数组的大小。
  • 如果函数需要知道数组的大小,必须额外传递一个 size 参数
  • 这就是为什么 C 标准库函数(如 strcpy, memcpy)要么需要一个长度参数,要么需要一个特殊的结束标记(如 '\0' 用于字符串)。

sizeof 与多维数组

sizeof 对于多维数组同样有效,它会计算所有维度元素的总大小。

示例代码:

#include <stdio.h>
int main() {
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    // sizeof(matrix) = 3行 * 4列 * sizeof(int) = 3 * 4 * 4 = 48 字节
    printf("sizeof(matrix) = %zu\n", sizeof(matrix)); // 输出 48
    // 当只指定第一维时,它不会退化,而是表示一个“数组指针”
    // sizeof(matrix[0]) 计算的是第一行的大小 (4个int)
    printf("sizeof(matrix[0]) = %zu\n", sizeof(matrix[0])); // 输出 16
    // sizeof(matrix[0][0]) 计算的是单个元素的大小
    printf("sizeof(matrix[0][0]) = %zu\n", sizeof(matrix[0][0])); // 输出 4
    // 可以推导出行数和列数
    int rows = sizeof(matrix) / sizeof(matrix[0]);
    int cols = sizeof(matrix[0]) / sizeof(matrix[0][0]);
    printf("矩阵的行数: %d\n", rows); // 输出 3
    printf("矩阵的列数: %d\n", cols); // 输出 4
    return 0;
}

sizeof 与变长数组 (VLA - Variable Length Array)

C99 引入了变长数组,其大小在运行时确定。sizeof 对于 VLA 也是在编译时计算(如果编译器支持 VLA 优化),或者在运行时计算。

示例代码:

#include <stdio.h>
void calculate_vla_size(int n) {
    // VLA 的大小由函数参数 n 决定
    int vla[n];
    printf("在函数内部,n = %d\n", n);
    // 对于 VLA,sizeof(vla) 在编译时可能无法确定,编译器会生成代码在运行时计算
    printf("sizeof(vla) = %zu\n", sizeof(vla)); // 输出 n * sizeof(int)
}
int main() {
    int size = 10;
    int vla_main[size];
    printf("在 main 函数中,sizeof(vla_main) = %zu\n", sizeof(vla_main)); // 输出 40
    calculate_vla_size(5);
    return 0;
}

注意: VLA 是 C99 的特性,但并非所有编译器都完全支持或默认开启,C11 标准将 VLA 设为可选特性。


总结表格

场景 sizeof 行为 结果
在作用域内 sizeof(数组名) 计算整个数组的总字节数 元素个数 * sizeof(元素类型)
作为函数参数 sizeof(数组名) 数组名退化为指针,计算指针的大小 sizeof(指针类型) (通常是 4 或 8 字节)
在作用域内 数组名 (非 sizeof) 退化为指向首元素的指针 &arr[0] (地址)
多维数组 sizeof(数组名) 计算所有元素的总字节数 行数 * 列数 * sizeof(元素类型)
变长数组 (VLA) sizeof(数组名) 计算数组的总字节数(可能在运行时计算) 运行时确定的元素个数 * sizeof(元素类型)

核心要点:

  1. sizeof(数组名) 是获取数组大小的唯一可靠方式,且必须在数组定义的作用域内使用。
  2. 数组名在大多数情况下会退化为指针,sizeof 作用于指针得到的是指针本身的大小,而不是它指向的数据的大小。
  3. 函数无法通过 sizeof 获取传递过来的数组的大小,必须额外传递大小参数。
-- 展开阅读全文 --
头像
织梦运行缓慢怎么办?优化提速技巧有哪些?
« 上一篇 今天
C语言中printf如何输出彩色文字?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]