You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中数组长度的异常行为(疑似内存损坏?)

数组传参后用指针算术计算长度的未定义行为分析

你遇到的问题本质是:数组作为函数参数时会退化为指针,此时用(&a)[1]-a这种指针算术计算长度的写法属于未定义行为,偶尔得到正确结果只是编译器栈布局的巧合,完全不可靠。

为什么第一个代码能“正常”输出?

在你的第一个join函数中,没有局部变量,栈上只存放了两个函数参数a和b(都是指针类型,64位系统下占8字节)。此时&a是指向指针变量a的指针(类型为int**),(&a)[1]等价于*( &a + 1 ),也就是栈上a的下一个相邻变量的地址——刚好是参数b的地址。

此时(&a)[1]-a计算的是(int*)&b - a,而a是指向main中数组a的指针,&b是栈上参数b的地址,这两个地址的差值除以sizeof(int)刚好等于3(栈布局的巧合),所以输出了正确的长度。同理ARR_LEN(b)也是利用了(&b)[1]指向栈上b之后的栈帧数据,刚好差值对应数组长度5。

为什么加局部变量n后结果异常?

当你在join函数中声明局部变量n后,栈布局发生了变化:(&a)[1]不再指向参数b的地址,而是指向局部变量n的地址。此时(&a)[1]-a计算的是(int*)&n - a,这两个地址没有任何逻辑关联,差值是随机的(因为栈地址比数组a的地址高,差值为负,转成无符号的size_t就变成了超大正数),所以输出了异常值。

全局变量n为什么能“正常”工作?

全局变量存放在进程的.data段,不在栈上,所以join函数的栈布局还是只有两个参数a和b,(&a)[1]依然指向b的地址,因此巧合性地得到了正确结果。但当你把ARR_LEN(b)存入局部变量m时,(&b)[1]会指向m的地址,此时ARR_LEN(b)的计算就会出现异常,这再次验证了这种写法的不可靠性。

正确的解决方案

要在函数中获取数组长度,必须显式传递长度参数,或者使用C99的变长数组(VLA)来保留数组的长度信息:

方案1:显式传递长度

#define ARR_LEN(a) (sizeof(a)/sizeof((a)[0]))

void join(int a[], size_t len_a, int b[], size_t len_b) {
    printf("%zu %zu", len_a, len_b);
}

int main() {
    int a[3] = {0, 1, 2};
    int b[5] = {0, 1, 2, 3, 4};
    join(a, ARR_LEN(a), b, ARR_LEN(b));
    return 0;
}

注:ARR_LEN宏在main中可以正常使用,因为a和b是数组类型,不是指针。

方案2:使用变长数组(VLA)

void join(size_t len_a, int a[len_a], size_t len_b, int b[len_b]) {
    // 对于VLA,sizeof(a)会在运行时计算为len_a * sizeof(int)
    printf("%zu %zu", len_a, sizeof(a)/sizeof(a[0]));
}

int main() {
    int a[3] = {0, 1, 2};
    int b[5] = {0, 1, 2, 3, 4};
    join(3, a, 5, b);
    return 0;
}

总结

(&a)[1]-a这种写法完全依赖编译器的栈布局,属于C标准明确的未定义行为,任何时候都不能用于生产代码。必须通过显式传递长度或使用VLA的方式来安全地获取数组长度。

内容的提问来源于stack exchange,提问作者user17531058

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:23:12