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

C语言局部无终止符字符串打印异常:为何输出结果有差异?

关于C语言联合体与局部变量内存布局的疑惑

代码与现象

带联合体的代码

#include <stdio.h>

union Data {
    char var1;
    char var2;
    char varArr[10];
};

int main() {
    union Data data;
    data.var1 = 'a';
    data.var2 = 'b';
    data.varArr[0] = 'c';
    char ctr[3];
    ctr[0] = data.var1;
    ctr[1] = data.var2;
    ctr[2] = data.varArr[0];
    printf("Result: %s\n", ctr);
    return 0;
}
  • 题目预期输出为ccc,但ctr是无\0终止符的3字节数组,按printf("%s")的逻辑会读取到第一个\0才停止,本应输出随机内容,但实际多次运行始终输出ccc。
  • 检查ctr[3]、ctr[4]等越界字节,发现通常为\0,偶尔是0x01这类小值,但输出仍为ccc。

简化后的代码

#include <stdio.h>

int main() {
    char ctr[3];
    ctr[0] = 'c';
    ctr[1] = 'c';
    ctr[2] = 'c';
    printf("Result: %s\n", ctr);
    return 0;
}
  • 每次运行输出cccX(X为随机变化的垃圾值)。

内存布局层面的解释(基于gcc 13、64位x86-64架构)

核心前提

x86-64架构下,栈内存从高地址向低地址增长,gcc默认按变量声明顺序从高到低分配局部变量(先声明的变量地址更高),同时栈帧需满足16字节对齐要求。

带联合体代码的栈布局

  1. 联合体的内存特性:union Data的大小由最大成员varArr[10]决定,为10字节,且所有成员共享同一块内存的起始地址。因此data.var1、data.var2、data.varArr[0]指向同一个字节,最后赋值的data.varArr[0] = 'c'会覆盖之前的'a'和'b',这是ctr三个元素均为'c'的原因。
  2. 局部变量的分配顺序:
    • 先声明的data被分配在栈的高地址区域,占用10字节内存。
    • 后声明的ctr被分配在data的低地址侧,占用3字节内存。
    • 由于栈帧16字节对齐要求,编译器会在data和ctr之间或data末尾添加3字节填充,使总局部变量空间对齐到16字节。
  3. 越界字节的来源:
    • ctr的越界字节(如ctr[3])紧邻data的内存区域,这部分内存要么是编译器添加的填充字节(默认初始化为\0),要么是data中未被赋值的剩余字节(栈上未初始化内存通常残留小值,如0x01这类非打印ASCII字符)。
    • printf("%s")输出时,遇到\0会停止;遇到非打印字符时,终端不会显示,因此最终只呈现可见的ccc。

简化代码的差异

简化代码中没有data变量,ctr直接分配在栈帧中,其越界字节指向的是之前栈帧残留的随机数据,这些数据更可能包含可打印ASCII字符(如X),因此会被终端显示出来,形成cccX的输出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 01:52:33