从Use the correct syntax when declaring a flexible array member中可以看出,当malloc
用于报头和灵活的数据时,data[1]
被入侵到struct
;
此示例在访问任何其他元素时具有未定义的行为
而不是数据数组的第一个元素。(见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
代码是否也会产生未定义的行为?如果不是,有什么区别?(或者卡内基梅隆维基是不正确的?)在实现中,什么因素是不起作用的? 最佳答案
这一点应明确界定:
a->array = (char *)(a + 1);
因为您创建了一个指针,指向一个超过1大小数组末尾的元素,但不要取消对它的引用。因为
a->array
现在指向的字节还没有有效的类型,所以可以安全地使用它们。不过,这只起作用,因为您使用的是后面的字节作为
char
的数组。如果您试图创建一个大于1的其他类型的数组,则可能会出现对齐问题。例如,如果您使用32位指针为ARM编译了一个程序,并且您拥有:
struct Array {
int size;
uint64_t *a;
};
...
Array a = malloc(sizeof *a + (length * sizeof(uint64_t)));
a->length = length;
a->a= (uint64_t *)(a + 1); // misaligned pointer
a->a[0] = 0x1111222233334444ULL; // misaligned write
你的程序会因为写错而崩溃。所以总的来说你不应该依赖这个。最好坚持一个灵活的数组成员的标准保证将工作。