#include <iostream> using namespace std; int main() { int arr[3] = { 10, 20, 30 }; cout << arr[-2] << endl; cout << -2[arr] << endl; return 0; } 输出: 4196160 -30 这里arr[-2]超出范围且无效,导致未定义的行为 。 但-2[arr]评估为-30 。 为什么? 不是arr[-2]等于-2[arr] ?
我们class的C程序devise教授问了这个问题: 你得到的代码是: int x=1; printf("%d",++x,x+1); 它会一直产生什么输出? 大多math生说未定义的行为。 谁能帮我理解为什么是这样吗? 感谢编辑和答案,但我仍然困惑。
所以我有这个代码: uint32_t s1 = 0xFFFFFFFFU; uint32_t s2 = 0xFFFFFFFFU; uint32_t v; … v = s1 * s2; /* Only need the low 32 bits of the result */ 在所有的下面,我假设编译器不能对s1或s2的范围有任何的先入之见,初始值设定项仅用于上面的一个例子。 如果我在一个整数大小为32位的编译器(例如编译x86时)上编译这个,没问题。 编译器会简单地使用s1和s2作为uint32_ttypes的值(不能进一步提升它们),乘法只会给出结果,如注释所示(模UINT_MAX + 1 ,这是0x100000000这种情况)。 但是,如果我编译这个整数大小为64位的编译器(如x86-64),可能会有一些未定义的行为,我可以从C标准中推断出来。 整数提升会看到uint32_t可以被提升为int (64位有符号),然后乘法会试图乘以两个int ,如果碰巧有例子中显示的值,会导致整数溢出,这就是未定义的行为。 我纠正这一点,如果是的话,你会如何避免它以一种理智的方式? 我发现了这个类似的问题,但涵盖了C ++: 安全地模块化乘无符号整数的最佳C ++方法是什么? 。 在这里我想得到一个适用于C的答案(最好是C89兼容)。 尽pipe(通常在代码中,这是值得关注的,32位性能可能更为重要,因为通常那些是较慢的机器),所以我不会考虑制作一台可怜的32位机器,可能会执行一个64位乘法的可接受答案。 请注意,使用32位int大小的编译器编译时,同样的问题可以应用于16位无符号整数,编译时使用16位int大小的编译器编译无符号字符(后者可能与8位CPU的编译器相同:C标准要求整数至less为16位,所以符合的编译器可能会受到影响)。
在C ++中,有很多方法可以编写编译代码,但会产生未定义的行为(Wikipedia) 。 在C#中有类似的东西吗? 我们可以编写C#编译的代码,但有未定义的行为?
当sscanf()或scanf系列的另一个函数被赋予一个转换值超过目标整数types最大值的数字序列时, 应该认为转换失败了吗? 是所有定义的行为?
我最近读了C和C ++中的有符号整数溢出导致未定义的行为: 如果在expression式评估过程中,结果不是math定义的,或者不在其types的可表示值范围内,则行为是未定义的。 我目前正试图了解这里未定义行为的原因。 我以为未定义的行为发生在这里,因为当它变得太大而不适合底层types时,整数开始操作内存。 所以我决定在Visual Studio 2015中编写一个testing程序,用下面的代码来testing这个理论: #include <stdio.h> #include <limits.h> struct TestStruct { char pad1[50]; int testVal; char pad2[50]; }; int main() { TestStruct test; memset(&test, 0, sizeof(test)); for (test.testVal = 0; ; test.testVal++) { if (test.testVal == INT_MAX) printf("Overflowing\r\n"); } return 0; } 我在这里使用了一个结构来防止Visual Studio在debugging模式下的任何保护性问题,比如临时填充栈variables等等。 无限循环应该会引起test.testVal多次溢出,而且确实是这样,除了溢出本身之外没有其他任何后果。 我在运行溢出testing时看到了内存转储,结果如下( test.testVal的内存地址为0x001CFAFC ): 0x001CFAE5 00 00 […]
这个问题出现在我正在阅读(答案)时,为什么我在C ++ 11中定义了明确的i ++ I + 1? 我收集的微妙的解释是(1)expression式++i返回一个左值,但是+ prvalues作为操作数,所以必须执行从左值到左值的转换; 这涉及获得该左值的当前值(而不是一个比旧的值多一个),因此必须在增量的副作用(即,更新i ) 后sorting(2)该赋值的LHS也是因此其价值评估不涉及获取i的当前价值; 而这个值的计算与RHS的值计算是不一致的,这就不存在问题。(3)赋值本身的值计算涉及更新i (再次),而是在RHS的值计算之后进行sorting,因此在先给i更新; 没问题。 好吧,那里没有UB。 现在我的问题是,如果将assigment操作符从=改成+= (或类似的操作符),会怎样。 expression式i += ++i + 1的评估是否会导致未定义的行为? 正如我所看到的那样,这个标准似乎在这里相互矛盾。 由于+=的LHS仍然是一个左值(而其RHS仍然是一个值),就上面(1)和(2)而言,同样的推理也适用。 在+=上操作数的评估中没有未定义的行为。 至于(3),复合赋值的操作+= (更准确地说是该操作的副作用;如果需要的话,它的值计算在任何情况下都在其副作用之后被sorting)现在必须同时获取当前值i , 然后 (明显地按顺序排列,即使标准没有明确说明,否则评估这样的操作符总是会调用未定义的行为)添加RHS并将结果存回i 。 如果这些操作在++副作用下没有被确定,那么这两个操作都会给出未定义的行为,但是正如上面所讨论的( ++的副作用是在给出+=操作符的RHS的值计算之前进行sorting的,哪个值计算在该复合赋值操作之前被sorting),情况并非如此。 但是另一方面,标准还表示E += F相当于E = E + F ,只是(左值)E只被评估一次。 现在在我们的例子中, 作为左值的i (这是E在这里是什么)的值计算不涉及需要与其他动作sorting的任何事情,因此做一次或两次都没有区别; 我们的expression应严格等于E = E + F 但是这是问题。 很明显,评估i = i + […]
在这段代码中我们无法解释一个烦人的错误: unsigned char bitmap[K_BITMAP_SIZE] = {0} ; SetBit(bitmap, K_18); // Sets the bit #18 to 1 for(size_t i = 0; i < K_END; ++i) { if(TestBit(bitmap, i)) // true for 18 { size_t i2 = getData(i); // for 18, will return 15 SetBit(bitmap, i2); // BUG: IS SUPPOSED TO set the bit #15 to 1 […]
大多数围绕未定义行为 (UB)的对话都谈论如何有一些平台可以做到这一点,或者一些编译器这样做。 如果你只对一个平台感兴趣而且只有一个编译器(同一版本),而且你知道你会使用它们多年? 没有什么改变,但代码和UB没有实现定义。 一旦UB已经体现了这种架构,编译器和你已经testing过了,你不能假设从那时起,无论编译器第一次使用UB,每次都会这样做吗? 注意:我知道未定义的行为是非常非常糟糕的 ,但是当我在这种情况下用某人编写的代码指出UB时,他们问了这个问题,我没有什么比这更好的了,如果你需要升级或者港口,所有的UB将是非常昂贵的修复。 看来有不同的行为类别: Defined – 这是由标准logging的行为 Supported – 这是Supported行为logging的行为定义 Extensions – 这是一个文档的补充,支持低级别的操作,如popcount ,分支提示,属于这个类别 Constant – 虽然没有logging,但是这些行为可能会在给定的平台上是一致的,如endianness, sizeof int而不是可移植的可能不会改变 Reasonable – 通常是安全的,通常是遗留的,从无符号到有符号的转换,使用指针的低位作为临时空间 Dangerous – 读取未初始化或未分配的内存,返回一个临时variables,在非pod类上使用memcopy 看起来Constant在一个平台上的补丁版本中可能是不变的。 “ Reasonable和“ Dangerous之间的界限似乎越来越趋于Dangerous因为编译器在优化方面变得更加积极
首先,为了澄清,我不是在引用无效指针的引用! 考虑以下两个例子。 例1 typedef struct { int *p; } T; T a = { malloc(sizeof(int) }; free(ap); // ap is now indeterminate? T b = a; // Access through a non-character type? 例2 void foo(int *p) {} int *p = malloc(sizeof(int)); free(p); // p is now indeterminate? foo(p); // Access through a non-character type? 题 […]