Tag: 语言 律师

未定义的行为是否适用于asm代码?

假设您知道您的软件只能在两个补码机器上运行,其中已经很好地定义了带符号的溢出行为。 签名溢出仍然是C和C ++中未定义的行为,编译器可以自由地用“ret”替换你的整个程序,开始核战争,格式化你的驱动器,或让恶魔飞出你的鼻子。 假设您已在内联asm中签名溢出,您的程序是否仍然调用UB? 如果是,那么单独编译和链接汇编程序呢?

通过指针访问是否会更改严格的别名语义?

有了这些定义: struct My_Header { uintptr_t bits; } struct Foo_Type { struct My_Header header; int x; } struct Foo_Type *foo = …; struct Bar_Type { struct My_Header header; float x; } struct Bar_Type *bar = …; 说这个C代码( “案例一” )是否正确: foo->header.bits = 1020; …实际上与此代码在语义上不同( “case two” ): struct My_Header *alias = &foo->header; alias->bits = 1020; 我的理解是它们应该是不同的: 情况一考虑分配不能影响Bar_Type中的标题。 […]

比较从字符串转换的浮点值与文字

这并不是着名的浮动数学数据的重复,即使它看起来像乍一看。 我正在使用fscanf(file, “%lf”, &value);从文本文件中读取一个double fscanf(file, “%lf”, &value); 并将它与==运算符与双字面值进行比较。 如果字符串与文字相同,那么在所有情况下使用==的比较是否都是true ? 例 文字文件内容: 7.7 代码段: double value; fscanf(file, “%lf”, &value); // reading “7.7” from file into value if (value == 7.7) printf(“strictly equal\n”); 预期和实际产出是 strictly equal 但是这假设编译器将双文字7.7转换为与fscanf函数完全相同的双fscanf ,但编译器可能会也可能不会使用相同的库将字符串转换为double。 或者另有要求:从字符串到double的转换是否会产生唯一的二进制表示forms,或者可能存在轻微的实现依赖性差异? 现场演示

数组初始化中数组元素的赋值

考虑以下简单程序: #include int main(void) { int a[5] = { a[2] = 1 }; printf(“%d %d %d %d %d\n”, a[0], a[1],a[2], a[3], a[4]); } 使用GCC 7.3.0输出 1 0 1 0 0 考虑到a[1]为零,似乎初始化类似于 int a[5] = { 1 }; a[2] = 1; 问题是:虽然初始化程序可以是任何通用表达式,其初始化和赋值顺序是什么? 这甚至是有效且定义明确的吗? 可能是实现定义,未定义或未指定? 这个问题与C中关于数组初始化的混淆问题有关 。

在刚刚超过数组末尾的指针上调用长度为零的memcpy是否合法?

正如在其他地方所解释的那样,即使长度参数为零,调用具有无效或NULL指针的memcpy函数也是未定义的行为。 在这样一个函数的上下文中,尤其是memcpy和memmove ,是一个指针刚刚超过数组末尾的一个有效指针? 我问这个问题,因为一个指针刚好超过一个数组的末尾是合法的(相反,例如一个指针超过一个数组末尾的两个元素),但你不能取消引用它,但是脚注106 ISO 9899:2011表明这样的指针指向程序的地址空间,这是指针根据§7.1.4有效所需的标准。 这种用法发生在我想在一个数组的中间插入一个项目的代码中,要求我在插入点之后移动所有项目: void make_space(type *array, size_t old_length, size_t index) { memmove(array + index + 1, array + index, (old_length – index) * sizeof *array); } 如果我们想在数组的末尾插入,则index等于length , array + index + 1指向刚好超过数组的末尾,但复制元素的数量为零。

C中暂定定义背后的基本原理是什么?

考虑以下程序。 这会给出任何编译错误吗? #include int s=5; int s; int main(void) { printf(“%d”,s); } 乍一看似乎编译器会给出变量重定义错误,但程序根据C标准完全有效。 (请参阅http://ideone.com/Xyo5SY上的现场演示)。 暂定定义是没有存储类说明符且没有初始化程序的任何外部数据声明。 C99 6.9.2 / 2 对于具有文件范围而没有初始化程序且没有存储类指定程序或存储类指定静态的对象的标识声明构成了一个暂定的定义。 如果翻译单元包含一个或多个用于标识符的暂定定义,并且翻译单元不包含该标识符的外部定义,则该行为就像翻译单元包含该标识符的文件范围声明一样,其复合类型为翻译单元的结尾,初始化程序等于0。 我的问题是,允许暂定定义的理由是什么? 在C中有没有用过这个? 为什么C允许暂定?

结构可以为其自己的初始和唯一成员设置别名吗?

例如,此代码是否有效,还是通过违反别名规则来调用未定义的行为? int x; struct s { int i; } y; x = 1; y = *(struct s *)&x; printf(“%d\n”, yi); 我的兴趣在于使用基于此的技术来开发用于执行别名读取的可移植方法。 更新:这是预期的用例,略有不同,但当且仅当上述内容有效时才有效: static inline uint32_t read32(const unsigned char *p) { struct a { char r[4]; }; union b { struct ar; uint32_t x; } tmp; tmp.r = *(struct a *)p; return tmp.x; } GCC根据需要将其编译为单个32位负载,并且它似乎避免了如果p实际指向除char之外的类型可能发生的混叠问题。 换句话说,它似乎充当了GNU […]

实现可以将提示视为实际语句吗?

在C中, register存储限定符是对实现的暗示 ,应尽可能快地访问此标识符(例如,存储在CPU寄存器中)。 §6.7.1具有存储类说明符寄存器的对象的标识符声明建议尽可能快地访问对象。 这些建议有效的程度是实施定义的。 和 §6.7.3限制限定符的预期用途(如寄存器存储类)是为了促进优化[…] 但是,我听说过register具有更强意义的实现(特别是嵌入式系统中的实现):它是一个命令 ,编译器应将限定标识符放在寄存器中。 那么, 是否允许实现遵循该行为并因此被视为符合标准? 什么会允许? 我提出这个问题是因为我发现有必要将该物品放在登记册中,这不再是标准规定的建议; 换句话说,他们发生冲突。

C和C ++标准是否意味着地址空间中的特殊值必须仅存在才能表示空指针的值?

在关于C和C ++中的空指针的这个问题的讨论之后,我想在这里分开结束问题。 如果可以从C和C ++标准推断(答案可以针对两个标准),取消引用其值等于nullptr (或(void *)0 )值的指针变量是未定义的行为,是否意味着这些语言需要地址空间中的特殊值是死的 ,这意味着除了表示nullptr的角色之外它是不可用的? 如果系统在同一地址上具有真正有用的函数或数据结构等于nullptr会怎么样? 这应该永远不会发生,因为编译器编写的每个系统都要找出一个非冲突的空指针值,这是编译器的编写者的责任吗? 或者,在“未定义的行为模式”下编程以实现其意图时,需要访问此类函数或数据结构的程序员是否满足? 这看起来模糊了编译器和计算机系统角色的界限。 我会问这样做是否正确,但我想这里没有空间。 这篇博文详细介绍了解决问题的情况

使用大括号初始化标量

在C和C ++中,可以使用大括号初始化数组和结构: int a[] = {2, 3, 5, 7}; entry e = {“answer”, 42}; 然而,在2007年的一次演讲中 ,Bjarne提到这种语法也适用于标量。 我尝试过这个: int i = {7}; 它确实有效! 允许使用大括号初始化标量的原理是什么? 注意:我特别不是在谈论C ++ 11统一初始化。 这是很好的旧C89和C ++ 98。