Tag: language lawyer

什么是C / C ++中的“字节”

例如,这是fread的参考: size_t fread ( void * ptr, size_t size, size_t count, FILE * stream ); 读取一个count元素数组,每个元素的大小为“size bytes”…那么有多少BITS会读取一个fread(&x, 1, 1, stream) ? 八或CHAR_BIT ?

当printf的相应参数不是short / char时,使用h或hh长度修饰符是不合法的吗?

printf系列提供了一系列长度修饰符,其中两个是hh (表示有signed char或unsigned char参数提升为int )和h (表示有signed short或unsigned short参数,提升为int )。 从历史上看,这些长度修改器仅用于创建与scanf的长度修饰符对称,并且很少用于printf 。 以下是ISO 9899:2011§7.21.6.1“fprintf函数”¶7的摘录: 7长度修饰符及其含义是: hh指定以下d , i , o , u , x或X转换说明符适用于signed char或unsigned char参数(该参数将根据整数提升进行提升,但其值应转换为signed char或打印前unsigned char ); 或者后面的n转换说明符适用于指向signed char参数的指针。 h指定以下d , i , o , u , x或X转换说明符适用于short int或unsigned short int参数(该参数将根据整数提升进行提升,但其值应转换为short int打印前的short int或unsigned short int ); 或者后面的n转换说明符适用于指向short int参数的指针。 … 忽略n转换说明符的情况,这些几乎相同的段落对h和hh的行为有何看法? 在这个答案中 ,声称传递的参数超出了signed char , […]

在C函数中创建的对象的存在

已经建立(见下文)创建对象需要placement new int* p = (int*)malloc(sizeof(int)); *p = 42; // illegal, there isn’t an int 然而,这是在C中创建对象的一种非常标准的方式。 问题是,如果在C中创建int并返回到C ++,那么它是否存在? 换句话说,以下是否合法? 假设int对于C和C ++是相同的。 foo.h中 #ifdef __cplusplus extern “C” { #endif int* foo(void); #ifdef __cplusplus } #endif foo.c的 #include “foo.h” #include int* foo(void) { return malloc(sizeof(int)); } main.cpp中 #include “foo.h” #include int main() { int* p = foo(); […]

C中命令行参数`argv`的类型是什么?

我正在阅读C Primer Plus中关于命令行参数argv一节,我很难理解这句话。 它说, 程序将命令行字符串存储在内存中,并将每个字符串的地址存储在指针数组中。 该数组的地址存储在第二个参数中。 按照惯例,指向指针的指针称为argv ,用于参数值。 这是否意味着命令行字符串作为指向char数组的指针数组存储在内存中?

当sizeof(int)== 4时,1 << 31在C中定义得很好

根据这个问题的答案: E1 << E2的结果是E1左移E2位位置; 腾出的位用零填充。 如果E1具有无符号类型,则结果的值为E1×2 E2 ,比结果类型中可表示的最大值减少一个模数。 如果E1具有带符号类型和非负值,并且E1×2 E2可在结果类型中表示,那么这就是结果值; 否则,行为是未定的。 这似乎意味着1 << 31未定义。 但是,如果我使用1 << 31 GCC不会发出警告。 它确实为1 << 32发出一个。 链接 那是哪个呢? 我误解了这个标准吗? 海湾合作委员会有自己的解释吗?

别名结构和数组是否合法?

在结构中相同类型的连续成员之间的指针算术过去常常是一种常见的做法,而指针算术仅在数组内有效。 在C ++中,它将是明确的Undefined Behavior,因为数组只能由声明或新表达式创建。 但是C语言将数组定义为具有特定成员对象类型的连续分配的非空对象集,称为元素类型。 (n1570 C11草案,6.2.5类型§20)。 因此,如果我们可以确保成员是连续的(意味着它们之间没有填充),那么将其视为数组是合法的。 这是一个简化的示例,它在没有警告的情况下编译,并在运行时给出预期的结果: #include #include #include struct quad { int x; int y; int z; int t; }; int main() { // ensure members are consecutive (note 1) static_assert(offsetof(struct quad, t) == 3 * sizeof(int), “unexpected padding in quad struct”); struct quad q; int *ix = &q.x; for(int i=0; […]

符合C标准的方式来访问空指针地址?

在C中,引用空指针是未定义行为,但是空指针值具有位表示,在某些体系结构中它使其指向有效地址(例如,地址0)。 为了清楚起见,我们将此地址称为空指针地址 。 假设我想在C中编写一个软件,在一个无限制访问内存的环境中。 假设我想在空指针地址处写一些数据: 我将如何以标准兼容的方式实现这一点? 示例案例(IA32e): #include int main() { uintptr_t zero = 0; char* p = (char*)zero; return *p; } 当使用带有-O3的 gcc与IA32e编译时,此代码将转换为 movzx eax, BYTE PTR [0] ud2 由于UB(0是空指针的位表示)。 由于C接近低级编程,我相信必须有一种方法来访问空指针地址并避免UB。 只是要清楚 我问的是标准对此有何看法,而不是如何以实现定义的方式实现这一点。 我知道后者的答案。

是否使用未定义所有成员的结构未定义?

在块范围内考虑此代码: struct foo { unsigned char a; unsigned char b; } x, y; xa = 0; y = x; C [N1570] 6.3.2.1 2说“如果左值指定了一个自动存储持续时间的对象,该对象可以用寄存器存储类声明(从未使用过它的地址),并且该对象未初始化(未使用初始化程序声明)在使用之前没有对它进行任何分配),行为是不确定的。“ 尽管已为x的成员分配了值,但未执行对x赋值,并且未对其进行地址处理。 因此,看起来6.3.2.1 2告诉我们y = x中x的行为是未定义的。 但是,如果我们为x每个成员分配了一个值,那么考虑到为了6.3.2.1 2而未初始化x似乎是不合理的。 (1)严格来说,标准中是否有任何内容导致6.3.2.1 2不适用于(未定义)上述代码? (2)假设我们正在修改标准或确定对6.3.2.1的合理修改2,是否有理由偏好以下其中一项而不是其他? (a)6.3.2.1 2不适用于结构。 (b)如果一个结构的至少一个成员被赋予了一个值,则该结构在6.3.2.1的目的下不是未初始化的。(c)如果一个结构的所有已命名的1个成员都被赋予了一个值,那么该结构是为6.3.2.1的目的而未初始化2。 脚注 1结构可能具有未命名的成员,因此并不总是可以为结构的每个成员分配值。 (即使结构初始化,未命名的成员也具有不确定的值,每6.7.9 9.)

从堆栈地址形成指针范围是不确定的行为?

一些C或C ++程序员惊讶地发现即使存储无效指针也是未定义的行为 。 但是,对于堆或堆栈数组,可以存储一个超过数组末尾的地址,这允许您存储“结束”位置以便在循环中使用。 但是从单个堆栈变量形成指针范围是未定义的行为,如: char c = ‘X’; char* begin = &c; char* end = begin + 1; for (; begin != end; ++begin) { /* do something */ } 虽然上面的例子很没用,但是如果某个函数需要一个指针范围,并且你只有一个值来传递它,那么这可能很有用。 这是未定义的行为吗?

从未初始化的变量memcpy是未定义的行为吗?

是否使用未初始化的变量作为C中memcpy未定义行为的src ? void foo(int *to) { int from; memcpy(to, &from, sizeof(from)); }