【发布时间】:2014-07-11 13:36:33
【问题描述】:
在this Stackoverflow question 或文章Undefined behavior can result in time travel (among other things, but time travel is the funkiest) 中,人们可能会了解到在大于其大小的索引处访问数据结构是未定义的行为,并且当编译器看到未定义的行为 em>,它会生成疯狂的代码,甚至不会报告遇到未定义的行为。这样做是为了使代码运行速度快几纳秒,并且因为标准允许这样做。出现所谓的“时间旅行”是因为编译器在控制流分支上操作,当它在一个分支中看到未定义的行为时,它只是删除该分支(基于任何行为都会在未定义行为的位置)。
尽管如此,这是一个古老的成语:
struct myString {
int length;
char text[1];
}
用作
char* s = "hello, world";
int len = strlen(s);
myString* m = malloc(sizeof(myString) + len);
m->length = len;
strcpy(&m->text,s);
现在,如果我访问m->text[3] 会发生什么? (注意它是如何声明的。)编译器会将其视为未定义的行为吗?如果是,如何在结构的末尾添加一个静态未知数量的项目数组?
特别是,我对大小至少为 1,但可能更大的数组感兴趣。有点,
struct x {
unsigned u[1];
};
and access like `struct x* p; ... p->x[3]`.
UPD 相关:Is the "struct hack" technically undefined behavior?(正如@mafso 在评论中指出的那样)。
【问题讨论】:
-
这是一个flexible array member,您显示的版本使用old c90 style,但gcc 可能仍然支持它,
-
如果你想要 C++ 中的可变长度字符串,你应该使用
std::string。 -
@ShafikYaghmour 他展示的内容在 C90 中是不合法的?它被广泛使用,并且 C99 添加了一个特殊功能,允许使用相同的功能而不会使边界检查通常是非法的。
-
在某些未定义行为的情况下,至少有一个编译器会故意导致崩溃。
-
我希望没有人会在 C++ 中这样做(因为你这样标记它)。与正确编写的容器相比,没有效率提升。想一想,也应该用 C 编写一个容器(伪造一个具有工厂函数作为 ctor 替换的类,它在幕后分配和适当的访问函数作为成员替换)。在 C 中,我们失去了自动释放(没有 dtor),但原始方法也需要手动释放。