Tag: 语言 律师

在function原型中是顶级易失性还是​​限制显着?

以下原型有什么实际区别吗? void f(const int *p); void f(const int *restrict p); void f(const int *volatile p); C11 6.7.6.3/15(最后一句)部分说,顶级限定符不是为了确定类型兼容性而考虑的,即允许函数定义在其参数上具有与原型不同的顶级限定符。声明了。 然而(与C ++不同)它并没有说它们被完全忽略了。 在const的情况下,这显然没有实际意义; 但是在volatile和restrict的情况下可能存在差异。 例: void f(const int *restrict p); int main() { int a = 42; const int *p = &a; f(p); return a; } 原型中是否存在restrict允许编译器优化读取a for return a; ? ( 相关问题 )

char是默认提升的吗?

这可能是个愚蠢的问题,但有人可以为C ++ 11和C11提供标准参考: char是否默认提升为int ? 这里有一点背景:C和C ++都有默认参数提升的概念(C ++ 11:5.2.2 / 7; C11:6.5.2.2/6)。 这需要在以下调用中提升参数: void f(int, …); float a = 1; short int b = 2; char c = ‘x’; f(0, a, b, c); 对于函数调用, a转换为double , b转换为int 。 但是c会发生什么? 我一直认为char也被提升为int ,但我无法在标准中找到相关的陈述。

指针的memcpy与赋值相同吗?

在另一个引起很多混淆的问题之后,这里有关于指针语义的问题,希望能够解决问题: 这个程序在所有情况下都有效吗? 唯一有趣的部分是在“pa1 == pb”分支中。 #include #include int main() { int a[1] = { 0 }, *pa1 = &a[0] + 1, b = 1, *pb = &b; if (memcmp (&pa1, &pb, sizeof pa1) == 0) { int *p; printf (“pa1 == pb\n”); // interesting part memcpy (&p, &pa1, sizeof p); // make a copy of […]

使用字符类型上次写入时,使用非字符类型读取对象时的未定义行为

假设unsigned int没有陷阱表示,请执行下面标记为(A)和(B)的语句中的任何一个或两个引发未定义的行为,为什么或为什么不行为,以及(特别是如果您认为其中一个定义明确但另一个不定义)是的,您认为标准中存在缺陷吗? 我主要对当前版本的C标准(即C2011)感兴趣,但如果在标准的旧版本或C ++中有所不同,我也想知道这一点。 ( _Alignas在这个程序中被用来消除因对齐不充分而导致的任何UB问题。我在解释中讨论的规则虽然没有说明对齐。) #include #include int main(void) { unsigned int v1, v2; unsigned char _Alignas(unsigned int) b1[sizeof(unsigned int)]; unsigned char *b2 = malloc(sizeof(unsigned int)); if (!b2) return 1; memset(b1, 0x55, sizeof(unsigned int)); memset(b2, 0x55, sizeof(unsigned int)); v1 = *(unsigned int *)b1; /* (A) */ v2 = *(unsigned int *)b2; /* (B) */ […]

C内存分配器和严格别名

即使在阅读了很多关于严格别名规则之后,我仍然感到困惑。 据我所知,不可能实现遵循这些规则的合理的内存分配器,因为malloc永远不能重用释放的内存,因为内存可以用于在每次分配时存储不同的类型。 显然这不可能是正确的。 我错过了什么? 如何实现遵循严格别名的分配器(或内存池)? 谢谢。 编辑:让我用一个愚蠢的简单例子来澄清我的问题: // s == 0 frees the pool void *my_custom_allocator(size_t s) { static void *pool = malloc(1000); static int in_use = FALSE; if( in_use || s > 1000 ) return NULL; if( s == 0 ) { in_use = FALSE; return NULL; } in_use = TRUE; return pool; } […]

从指向const的指针中删除const是否遵循C中的严格别名,并引用相同的对象?

C中的以下代码是否定义了行为? int main() { const int i = 0; return *(int*)(&i); } 我问因为6.5 / 7列出了“与对象的有效类型兼容的类型的合格版本”作为有效别名。 但是对象的有效类型是const int ,我不认为int是const int的限定版本(尽管反之亦然)。 int和const int都不兼容(6.7.3 / 10)。 此外,6.3.2.3 / 2表示您可以通过添加限定符来转换指针类型,并且结果指针是相等的。 6.3.2.3/7表示你可以转换任何两种指针类型(因此允许使用cast (int*)(&i)本身)。 但是并没有说结果指针指的是同一个对象,甚至它是相等的。 它说的是它可以转换回原始类型(在本例中为const int* )。 也就是说,即使别名是合法的,我也不清楚标准是否保证我的指针转换确实会导致指向i的指针。 那么,标准是否实际上定义了我的代码的行为,如果是这样,这个定义在哪里? 我知道代码在实践中有效。 我想到了一个假设的(和奇怪的)实现,它不起作用。 我可以问这个实现是否符合标准(如果没有,它违反了哪个部分),但如果还有其他方面,我想象的实现无法符合,我不想浑水。 如果有人认为这将有助于他们回答问题,我将描述实施。

C和C ++标准对位级整数表示和操作有何看法?

我知道C和C ++标准没有规定数字的特定表示(可以是二进制补码,符号和数量等)。 但我不清楚这些标准(并且无法确定是否已经说明)知道在使用位时是否存在任何特定的限制/保证/保留表示。 尤其: 如果整数类型中的所有位都为零,那么整数整数是否代表零? 如果整数类型中的任何位是1,整数整数是否表示非零? (如果这是“是”,则会对符号和幅度等一些表示进行额外限制) 是否有保证的方法来检查是否有任何位未设置? 是否有保证的方法来检查是否设置了任何位? (#3和#4类型依赖于#1和#2,因为我知道如何设置,例如某些变量x的第5位(参见#5),我想检查变量y以查看如果它的第5位为1,我想知道if if (x & y)是否有效(因为据我所知,这取决于表示的值而不是该位是否实际为1或0)) 是否有保证的方法来设置最左边和/或最右边的位? (至少比一个char c更简单的方法是所有位都为真(由c = c | ~c )并且做c = c << (CHAR_BIT – 1)来设置高位和c = c ^ (c << 1)对于低位,假设我不做任何假设,我不应该,鉴于这些问题) 如果#1的答案是“否”,那么如何迭代整数类型的位并检查每个位是1还是0? 我想我的整体问题是:C和C ++标准是否有关于位和整数的限制/保证/保留表示,尽管事实上没有规定整数的表示(如果C和C ++标准在这方面不同) ,他们的区别是什么?) 我在做作业时提出了这些问题,这需要我做一些操作(注意这些不是我作业的问题,这些都是“抽象的”)。 编辑:关于我所说的“位”,我的意思是“值形成”位而不包括“填充”位。

C89标准中的哪个部分允许“隐式整数”规则?

使用gcc ,代码: register a = 3; static b = 3; 虽然有警告,但在使用-std=c89 -pedantic-errors标志时允许使用它。 但是,它会收到-std=c99 -pedantic-errors标志-std=c99 -pedantic-errors 。 我想知道C89标准的哪一部分允许“隐含整数”规则?

memset编写的对象的有效类型是什么?

代码1: unsigned int *p = malloc(sizeof *p); memset(p, 0x55, sizeof *p); unsigned int u = *p; 代码2: void *d = malloc(50); *(double *)d = 1.23; memset(d, 0x55, 50); unsigned int u = *(unsigned int *)d; 在每种情况下, memset对malloc空间中对象的有效类型有什么影响; 那么初始化u正确的还是严格的别名违规? 有效类型的定义(C11 6.5 / 6)是: 用于访问其存储值的对象的有效类型是对象的声明类型(如果有)。 如果通过具有非字符类型的类型的左值将值存储到没有声明类型的对象中,则左值的类型将成为该访问的对象的有效类型以及不修改该值的后续访问的有效类型储值。 如果使用memcpy或memmove将值复制到没有声明类型的对象中,或者将其复制为字符类型数组,则该访问的修改对象的有效类型以及不修改该值的后续访问的有效类型是复制值的对象的有效类型(如果有)。 对于没有声明类型的对象的所有其他访问,对象的有效类型只是用于访问的左值的类型。 然而,目前还不清楚memset行为是否像通过字符类型的左值或其他东西写作一样。 memset (7.24.6.1)的描述并不是很有启发性: memset函数将c的值(转换为unsigned char )复制到s指向的对象的前n个字符中。

C11表达式中的赋值运算符排序

介绍 C11标准(ISO / IEC 9899:2011)在表达式中引入了新的副作用测序定义( 参见相关问题 )。 序列点概念已经在关系之前进行了序列补充,并且在关系之后 进行了 排序,这些关系现在是所有定义的基础。 第6.5节“表达式”,第2点说: 如果对标量对象的副作用相对于对同一标量对象的不同副作用或使用相同标量对象的值进行的值计算未进行排序,则行为未定义。 如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的。 稍后,第6.5.16节“分配操作员”,第3点指出: 在左右操作数的值计算之后,对更新左操作数的存储值的副作用进行排序。 对操作数的评估是不确定的。 问题 第一个引用的段落(6.5 / 2)由两个例子支持(与C99标准相同): 第一个例子 a[i++] = i; //! undefined a[i] = i; // allowed 这可以通过定义轻松解释: 如果相对于使用相同标量对象的值的(…)值计算,对标量对象的副作用未被排序,则行为未定义。 (6.5 / 2), 对操作数的评估是不确定的。 [在任务内](6.5.16 / 3)。 因此, i++ (LHS)的副作用与i (RHS)无关,这给出了未定义的行为。 第二个例子 i = ++i + 1; //! undefined i = i […]