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

你的程序会因为写错而崩溃。所以总的来说你不应该依赖这个。最好坚持一个灵活的数组成员的标准保证将工作。

09-09 18:46