C语言中访问部分赋值数组的未赋值区域是否具有良好定义?
访问部分赋值数组的未赋值区域:定义性分析
咱们来把这个问题掰扯清楚,结合C标准的规则和你的例子一步步分析:
先看foo()里的两种情况
void foo() { char s[10]; char v1 = s[0]; // 你的注释标了UB,其实这里需要澄清 char v2 = s[10]; // 这才是明确的UB }
s[0]:这个数组是自动存储期的局部变量,没初始化,所以元素是不确定值。但因为是char类型,C标准明确规定字符类型不存在陷阱表示,所以读取这个值的行为其实是良定义的——只是你得到的是内存里遗留的随机垃圾值,不是未定义行为。s[10]:这是明确的数组越界访问,直接踩过了数组的内存边界,属于标准里的未定义行为,编译器可以任意处理这段代码,绝对要避免。
重点分析bar()里的s[4]
void bar() { char s[10]; strcpy(s, "foo"); char v3 = s[3]; // v3是'\0',没问题 char v4 = s[0]; // v4是'f',没问题 char v5 = s[4]; // 这就是你问的情况 }
strcpy把"foo"(包括末尾的终止符'\0')复制到s里,所以s[0]到s[3]的值是确定的。而s[4]到s[9]这些元素,因为没被初始化或赋值,它们的值是不确定值。
那读取s[4]的行为到底怎么样?答案是:
行为是完全良定义的,但得到的值是不确定的。
理由很简单:
s[4]在数组的合法范围内(数组下标从0到9),没有越界,所以不会触发数组越界的未定义行为。- 还是那句话,
char类型没有陷阱表示,读取它的不确定值是标准允许的操作——你会得到一个随机的字节值,可能是之前程序运行遗留的垃圾,每次执行结果可能不一样,但这个读取动作本身是符合C标准的,不属于未定义行为。
最后总结几个关键规则
- 数组越界访问:不管数组有没有赋值,只要下标超出范围(比如
s[10]),绝对是未定义行为,碰都别碰。 - 访问未赋值的自动存储char元素:行为合法,但值是不确定的垃圾值。
- 访问未赋值的非char自动存储元素:如果该类型存在陷阱表示(比如某些平台上的有符号int),读取就是未定义行为;否则值不确定。
内容的提问来源于stack exchange,提问作者iBug
相关产品推荐
相关产品推荐

