Tag: 未定义行为

灵活的数组成员可能导致未定义的行为?

通过在结构types中使用灵活数组成员(FAM),我们是否将我们的程序暴露给未定义行为的可能性? 一个程序是否可以使用FAM,而且仍然是一个严格符合的程序? 灵活数组成员的偏移量是否需要在结构的末尾? 这些问题适用于C99 (TC3)和C11 (TC1) 。 #include <stdio.h> #include <stdlib.h> #include <stddef.h> int main(void) { struct s { size_t len; char pad; int array[]; }; struct s *s = malloc(sizeof *s + sizeof *s->array); printf("sizeof *s: %zu\n", sizeof *s); printf("offsetof(struct s, array): %zu\n", offsetof(struct s, array)); s->array[0] = 0; s->len = 1; printf("%d\n", s->array[0]); […]

当反转导致歧义时,不会有警告或错误(或运行时失败)

首先,记住一个.NET String是IConvertible和ICloneable 。 现在,考虑以下相当简单的代码: //contravariance "in" interface ICanEat<in T> where T : class { void Eat(T food); } class HungryWolf : ICanEat<ICloneable>, ICanEat<IConvertible> { public void Eat(IConvertible convertibleFood) { Console.WriteLine("This wolf ate your CONVERTIBLE object!"); } public void Eat(ICloneable cloneableFood) { Console.WriteLine("This wolf ate your CLONEABLE object!"); } } 然后尝试以下(在一些方法中): ICanEat<string> wolf = new HungryWolf(); […]

在C99中,f()+ g()是未定义的还是仅仅是未指定的?

我曾经认为在C99中,即使函数f和g的副作用受到干扰,尽pipeexpression式f() + g()不包含序列点,但f和g将包含一些,所以行为会未指定:f()将在g()之前调用,或者在f()之前调用g()。 我不再那么肯定。 如果编译器内联函数(编译器可能决定执行哪个函数(即使这些函数没有声明为inline函数),然后重新排列指令呢? 可能有人得到上述两个不同的结果吗? 换句话说,这是不确定的行为? 这不是因为我打算写这样的东西,这是在静态分析器中为这样的陈述select最好的标签。

如何在没有中间拷贝的情况下在标准C中实现memmove?

从我的系统上的手册页: void * memmove(void * dst,const void * src,size_t len); 描述 memmove()函数将len字节从stringsrc复制到stringdst。 这两个string可能重叠 ; 副本总是在一个非破坏性的 方式。 从C99标准: 6.5.8.5比较两个指针时,结果取决于指向对象的地址空间中的相对位置。 如果两个指向对象或不完整types的指针指向同一个对象,或者两个指向同一个数组对象的最后一个元素,则它们相等。 如果指向的对象是同一个聚合对象的成员,则稍后声明的结构成员的指针比结构中较早声明的成员的指针要多,指向具有较大下标值的数组元素的指针比指向同一数组元素的指针具有较低的下标值。 所有指向同一联合对象的成员的指针相等。 如果expression式P指向数组对象的元素,并且expression式Q指向相同数组对象的最后一个元素,则指针expression式Q+1将比P 。 在所有其他情况下,行为是不确定的 。 重点是我的。 参数dst和src可以转换为指向char指针,以减轻严格的别名问题,但是可以比较指向不同块的两个指针,以便在指向内部时按照正确的顺序进行复制同一块? 显而易见的解决scheme是if (src < dst) ,但是如果src和dst指向不同的块,那么它是不确定的。 “未定义”意味着你甚至不应该假设条件返回0或1(这在标准的词汇中被称为“未指定”)。 另一种方法是if ((uintptr_t)src < (uintptr_t)dst)至less没有被指定,但是我不确定标准是否保证当src < dst被定义时,它相当于(uintptr_t)src < (uintptr_t)dst) 。 指针比较是从指针算术定义的。 例如,当我阅读第6.5.6节的内容时,在我看来,指针运算可能与uintptr_t运算的方向相反,也就是说,如果p是char*types, ((uintptr_t)p)+1==((uintptr_t)(p-1) 这只是一个例子。 一般来说,将指针转换为整数时似乎很less有保证。 这是一个纯粹的学术问题,因为memmove与编译器一起提供。 实际上,编译器作者可以简单地将未定义的指针比较为未指定的行为,或者使用相关的编译指令来强制编译器正确编译它们的memmove 。 例如, 这个实现有这个片段: if ((uintptr_t)dst < […]

虚拟析构函数和未定义的行为

这个问题不同于“ 何时/为什么我应该使用virtual析构函数? ”。 struct B { virtual void foo (); ~B() {} // <— not virtual }; struct D : B { virtual void foo (); ~D() {} }; B *p = new D; delete p; // D::~D() is not called 问题 : 这可以被归类为一个未定义的行为(我们知道~D()不会被肯定的调用)? 如果~D()是空的。 它会以任何方式影响代码? 用B* p;使用new[] / delete[] B* p; , ~D()肯定不会被调用,而不pipe析构函数的virtual性如何。 这是一个未定义的行为或明确的行为?

未定义的行为和序列点重新加载

请考虑以下主题的续篇: 以前的安装 未定义的行为和顺序点 让我们重温一下这个有趣而复杂的expression(斜体字是从上面的话题*微笑*中获得的): i += ++i; 我们说这调用了未定义的行为。 我假设说这个时候,我们隐含地认为i types是内置types之一。 如果i的types是用户定义的types呢? 说它的types是在这篇文章后面定义的Index (见下文)。 它会调用未定义的行为吗? 如果是,为什么? 这不等于写i.operator+=(i.operator++()); 甚至在句法上更简单i.add(i.inc()); ? 或者,他们是否也调用未定义的行为? 如果不是,为什么不呢? 毕竟,对象i在连续的序列点之间被修改两次 。 请回想一下经验法则: expression式只能在连续的“序列点之间修改一个对象的值 ,如果i += ++i是一个expression式,那么它必须调用未定义的行为i.operator+=(i.operator++());和i.add(i.inc());还必须调用undefined-行为似乎是不真实的(据我所知) 或者, i += ++i不是一个expression式开始? 如果是这样,那么它是什么, expression的定义是什么? 如果它是一个expression式,并且同时它的行为也是明确定义的,那么它意味着与某个expression式相关联的序列点的数量在某种程度上取决于expression式中涉及的操作数的types 。 我是否正确(甚至部分)? 顺便说一下,这个expression呢? //Consider two cases: //1. If a is an array of a built-in type //2. If a is user-defined […]

为什么整数在x86上与GCC溢出导致无限循环?

以下代码在GCC上进入无限循环: #include <iostream> using namespace std; int main(){ int i = 0x10000000; int c = 0; do{ c++; i += i; cout << i << endl; }while (i > 0); cout << c << endl; return 0; } 所以这里的交易:签名整数溢出技术上是未定义的行为。 但是x86上的GCC使用x86整数指令实现整数运算 – 在溢出时换行。 因此,我会期望它会溢出 – 尽pipe这是不确定的行为。 但事实并非如此。 那么我错过了什么? 我编译这个使用: ~/Desktop$ g++ main.cpp -O2 GCC输出: ~/Desktop$ ./a.out […]

什么时候调用null实例的成员函数会导致未定义的行为?

考虑下面的代码: #include <iostream> struct foo { // (a): void bar() { std::cout << "gman was here" << std::endl; } // (b): void baz() { x = 5; } int x; }; int main() { foo* f = 0; f->bar(); // (a) f->baz(); // (b) } 我们期望(b)崩溃,因为空指针没有对应的成员x 。 在实践中, (a)不会因为this指针从未被使用而崩溃。 因为(b)取消引用this指针( (*this).x = 5; ),并且this为null,所以程序进入未定义的行为,因为dereferencing的null总是被认为是未定义的行为。 (a)是否会导致未定义的行为? […]

为什么这些构造(使用++)未定义的行为?

#include <stdio.h> int main(void) { int i = 0; i = i++ + ++i; printf("%d\n", i); // 3 i = 1; i = (i++); printf("%d\n", i); // 2 Should be 1, no ? volatile int u = 0; u = u++ + ++u; printf("%d\n", u); // 1 u = 1; u = (u++); printf("%d\n", u); […]

未定义的行为和顺序点

什么是“序列点”? 未定义的行为和顺序点之间的关系是什么? 我经常使用有趣和令人费解的expression式,如a[++i] = i; ,让自己感觉更好。 为什么我应该停止使用它们? 如果您已经阅读过这些内容,请务必访问后续问题重新加载未定义的行为和顺序点 。 (注意:这是一个Stack Overflow的C ++常见问题解答的入口,如果你想批评在这个表单中提供FAQ的想法,那么在这个开始所有这些的meta上的贴子将是这个地方的答案。那个问题在C ++聊天室中进行监控,常见问题解决scheme首先出现,所以你的答案很可能会被那些提出这个想法的人阅读)。