我试图说服(引用C99标准的具体部分)一位同事,以下是未定义的行为:
int *p = malloc(1);
p[0] = 0;
但我无法在标准中找到明确确保未定义的具体部分。 我正在寻找标准中的逻辑步骤,这些步骤从这些方面引出结论:未定义的行为 。它是第一行中从void *
到int *
的转换吗?第二行的作业?
我能找到的关于malloc
的唯一相关部分是它返回一个适当对齐的指针(7.20.3):
如果分配成功,则返回指针,以便可以将其指定给指向任何类型对象的指针,然后用于在分配的空间中访问此类对象或此类对象的数组(...)
我尝试在常规中使用 space 进行grepping,但由于空格和其他词汇问题而导致噪音太大。
答案 0 :(得分:18)
从 7.20.3.3 malloc函数添加到您的引用:
malloc函数为 size 所在的对象分配空间 由大小指定,其值是不确定的 malloc函数返回空指针或指向已分配空间的指针。
因此有两种可能的未定义行为来源,一种是覆盖(int的大小保证是16位或更多,但是你只分配1个字节,几乎所有系统都是8位)缓冲区,第二个可以取消引用空指针。
从 6.5.2.1数组下标,p[0] = 0
相当于*p = 0
。 *p
的类型为int
,因此它会填充sizeof(*p) * CHAR_BIT
位0
,这些位可能并非都属于分配的缓冲区,从而导致UB。
第一行代码(赋值)中没有未定义的行为,UB(如果有的话)在第二行(取消引用)。
但是在CHAR_BIT
较大且sizeof(int)
为1
的计算机上,对于malloc
不{{1} {{1}的情况,这将是明确定义的行为空指针。
答案 1 :(得分:7)
int *p = malloc(1);
p[0] = 0;
这是未定义的行为,因为您已经分配了1个字节,并且在上面的赋值中,您尝试写入4个字节(假设int
是4个字节)。只要sizeof(int) > 1
。
答案 2 :(得分:5)
6.5.3.2 Address and indirection operators
...
<强>语义强>
一元&amp;运算符产生其操作数的地址。如果是操作数 类型为''type'',结果有''指向类型''的指针。如果 操作数是一元*运算符的结果,既不是运算符也不是运算符 和&amp;运算符被评估,结果就像两个都被省略, 除了对运营商的约束仍然适用和 结果不是左值。同样,如果操作数是结果 a []运算符,&amp;算子也不是一元* 评估[] 隐含的结果,结果就像&amp; 删除了运算符,并将[]运算符更改为+ 运营商。否则,结果是指向对象或函数的指针 由其操作数指定。
一元*运算符表示间接。如果操作数指向a 功能,结果是功能指示符;如果它指向一个 对象,结果是指定对象的左值。如果是操作数 有类型''指向类型'',结果类型''类型''。 如果是 无效值已分配给指针,行为 unary *运算符未定义。
[]
运算符是指针上隐含的*
运算符。只要int
,分配给指针的值为sizeof( int ) > 1
的无效。
行为未定义。
并且NULL
是无效指针,因此这也涵盖malloc()
返回NULL
。
答案 3 :(得分:5)
标准引用:
J.2,未定义的行为:在以下情况下,行为未定义:...数组下标超出范围,即使某个对象显然可以使用给定的下标访问
6.2.5,类型,20:数组类型描述了一组连续分配的非空对象。
只要sizeof(int) > 1
,您的malloc(1)
没有分配一组非空对象,所以分配的数组大小为零,并且使用p[0]
访问时带有下标范围。 QED。
答案 4 :(得分:2)
代码*p
涵盖(至少 - 其他部分也可能涵盖它)6.3.2.1/1:
lvalue是一个潜在的表达式(对象类型不是void) 指定一个对象;如果左值在评估时未指定对象,则行为未定义。
“对象”的定义是:
执行环境中的数据存储区域,其内容可以表示值
左值*p
指定sizeof(int)
个字节的空间,但是只有1
个字节的存储可以表示值(换句话说,未分配的空间不能构成对象的一部分) 。因此,如果sizeof(int) > 1
,则*p
不会指定对象。
对于问题p[0]
中的实际代码:这相当于*(p+0)
。从6.5.6 / 8我不清楚p + 0
是否会导致UB。但这没有实际意义,因为即使它不会导致UB,也会如上所示引用结果;所以p[0]
会导致UB。
答案 5 :(得分:0)
malloc(1)
将地址返回到1字节的大缓冲区。
一般来说,int
大于1个字节。
因此,为1字节大缓冲区分配一个int值是UB。
malloc
返回的指针不需要在c中输入,因为它们在使用时可以安全地自动提升为正确的指针类型。