【发布时间】:2019-03-06 02:37:42
【问题描述】:
从Use the correct syntax when declaring a flexible array member 开始,当data[1] 被黑进struct 时,malloc 用于标题和灵活数据时,
此示例在访问其他任何元素时具有未定义的行为 比数据数组的第一个元素。 (参见 C 标准,6.5.6。) 因此,编译器可以生成不返回 访问第二个数据元素时的期望值。
我查阅了 C 标准 6.5.6,但看不出这将如何产生未定义的行为。我使用了一种我很熟悉的模式,其中标题隐含地跟随着数据,使用相同类型的malloc,
#include <stdlib.h> /* EXIT malloc free */
#include <stdio.h> /* printf */
#include <string.h> /* strlen memcpy */
struct Array {
size_t length;
char *array;
}; /* +(length + 1) char */
static struct Array *Array(const char *const str) {
struct Array *a;
size_t length;
length = strlen(str);
if(!(a = malloc(sizeof *a + length + 1))) return 0;
a->length = length;
a->array = (char *)(a + 1); /* UB? */
memcpy(a->array, str, length + 1);
return a;
}
/* Take a char off the end just so that it's useful. */
static void Array_to_string(const struct Array *const a, char (*const s)[12]) {
const int n = a->length ? a->length > 9 ? 9 : (int)a->length - 1 : 0;
sprintf(*s, "<%.*s>", n, a->array);
}
int main(void) {
struct Array *a = 0, *b = 0;
int is_done = 0;
do { /* Try. */
char s[12], t[12];
if(!(a = Array("Foo!")) || !(b = Array("To be or not to be."))) break;
Array_to_string(a, &s);
Array_to_string(b, &t);
printf("%s %s\n", s, t);
is_done = 1;
} while(0); if(!is_done) {
perror(":(");
} {
free(a);
free(b);
}
return is_done ? EXIT_SUCCESS : EXIT_FAILURE;
}
打印,
<Foo> <To be or >
兼容的解决方案使用C99 灵活的数组成员。该页面还说,
声明灵活数组时未能使用正确的语法 成员可能导致未定义的行为,尽管语法不正确 将适用于大多数实现。
从技术上讲,这个C90 代码是否也会产生未定义的行为?如果不是,有什么区别? (或者 Carnegie Mellon Wiki 不正确?)在实现上这不起作用的因素是什么?
【问题讨论】:
-
注意:“访问数据的第二个元素。” --> 这里没有 array 的第二个元素(
struct Array类型)。 “除了数据数组的第一个元素”不适用,因为没有 array。我在这里看不到 UB, -
没错,我可以在
Array的末尾加上一个char [1];这很重要吗? -
断言 UB 是由于无效的数组使用。没有数组,因此很难支持 UB 声明,
-
在
Array的末尾加上char [1]会改变struct Array的类型,但这里仍然没有struct Arrayarray。struct Array foo[1];是一个数组。struct Array *a;是一个指针。 -
在链接的示例中,是的,UB 由于索引越界
标签: c language-lawyer c89 flexible-array-member