Tag: 语言律师

UB“未定义行为”背景下的C“可观察行为”

(问题最初是由这个答案中的评论引起的。这个生产者 – 消费者实现中是否存在竞争条件?但是这里严格地从C语言角度提出这个问题,没有涉及任何并发或multithreading。) 考虑这个最小代码: #define BUFSIZ 10 char buf[BUFSIZ]; void f(int *pn) { buf[*pn]++; *pn = (*pn + 1) % BUFSIZ; } int main() { int n = 0; f(&n); return n; } 问题:C “as-if”规则是否允许编译器重写代码如下? void f(int *pn) { int n = *pn; *pn = (*pn + 1) % BUFSIZ; buf[n]++; } 一方面,上述内容不会改变程序的可观察行为。 另一方面, f可能会被一个无效索引调用,可能来自另一个翻译单元: […]

我可以写一个非const结构的const成员吗?

这段代码合法吗?: #include typedef struct a_d{ int const x; } a_d; int a_d__ctor(a_d *X) { *(int*)&X->x = 42; //<– legal or not? return 0; } int main() { a_d a; a_d__ctor(&a); printf("ax=%d\n", ax); }

混合符号整数数学取决于变量大小

函数g1()和g2()具有相同的逻辑,但输入类型具有不同的大小。 为什么他们会为负数返回不同的结果? /*BINFMTCXX: -Wall -Werror -Wextra -std=c++11 */ #include #include char g1( int32_t a, uint32_t b ) { return a+b<9; } // fails when a+b is negative char g2( int16_t a, uint16_t b ) { return a+b<9; } // works if no overflow int main() { for ( int a=-2, b=0; a<=2; a++ ) { […]

隐含function声明在C89中是合法的吗?

考虑这个C程序: int main() { puts(“Hello world!”); return 0; } 编译并运行正常,据我所知,是合法的C89。 但是,我不是百分百肯定的。 使用clang在C99模式下编译告诉我在C99中implicit declaration of function ‘puts’ is invalid in C99 (这让我觉得C标准必须在C99中改变以使隐式函数声明非法,这就是我要确认的)。 隐含function声明在C89中是合法的吗? (即使这样做是个坏主意(除非你在混淆的C代码挑战中))

常量32768和0x8000之间的类型区别可以有所不同吗?

标准规定hex常量如0x8000(大于有符号整数)是无符号的(就像八进制常量一样),而像32768这样的十进制常量是长符号。 (确切类型假定为16位整数和32位长。)但是,在常规C环境中,两者将具有相同的表示forms,二进制1000 0000 0000 0000 。 这种差异真的会产生不同的结果吗? 换句话说,这种差异是否很重要?

将堆分配的指针强制转换为指向VLA的指针是否安全?

如果我有一个指向某个堆分配空间的指针,该空间代表一个典型的行主要二维数组,是否可以将此指针强制转换为指向VLA的等效指针以方便子脚本编写? 例: // // Assuming ‘m’ was allocated and initialized something like: // // int *matrix = malloc(sizeof(*matrix) * rows * cols); // // for (int r = 0; r < rows; r++) { // for (int c = 0; c < cols; c++) { // matrix[r * cols + c] = some_value; // } […]

calloc()总共可以分配超过SIZE_MAX吗?

在最近的一次代码审查中 ,有人声称 在select系统上, calloc()可以分配超过SIZE_MAX总字节数,而malloc()是有限的。 我的主张是错误的,因为calloc()为一个对象数组创建了空间 – 作为一个数组,它本身就是一个对象。 并且任何对象的大小都不能超过SIZE_MAX 。 那么我们哪个是正确的? 在地址空间大于size_t范围的(可能是假设的)系统上,当使用产品大于SIZE_MAX参数调用时, calloc()允许成功? 为了使它更具体:以下程序是否会以非零状态退出? #include #include int main() { return calloc(SIZE_MAX, 2) != NULL; }

什么时候可以完全优化易失性变量?

考虑以下代码示例: int main(void) { volatile int a; static volatile int b; volatile int c; c = 20; static volatile int d; d = 30; volatile int e = 40; static volatile int f = 50; return 0; } 没有volatile ,编译器可以优化掉所有变量,因为它们永远不会被读取。 我认为a和b可以被优化掉,因为它们是完全未使用的,请参见未使用的volatile变量 。 我认为c和d因为被写入而无法删除,并且必须实际发生对volatile变量的写入。 e应该相当于c 。 GCC不会优化f ,但它也不会发出任何写入指令。 在数据部分中设置50。 LLVM(clang)完全删除f 。 这些陈述是真的吗? 如果永远不会访问volatile变量,则可以将其优化掉。 静态或全局变量的初始化不计为访问。

我无法理解C99中的一些句子

在C99 6.5中说: 在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算修改一次。 此外,先前的值应该是只读的,以确定要存储的值 什么“此外,先前的值只能读取以确定要存储的值”是什么意思? 在C99中,为什么a[i++] = 1是未定义的行为?

究竟什么是C中的翻译单元

翻译单元的常用定义是预处理之后(头文件包含,宏等与源文件一起)。 这个定义相当清楚,C标准5.1.1.1,C11说: AC程序不需要同时翻译。 该程序的文本保存在本国际标准中称为源文件(或预处理文件)的单元中。 源文件以及通过预处理指令#include包含的所有头文件和源文件称为预处理转换单元。 在预处理之后,预处理翻译单元被称为翻译单元。 更仔细地阅读第一句话: AC程序不需要同时翻译。 这意味着(对我来说),C程序可以在同一个程序中进行翻译,而不必将它们分成多个预处理源文件。 同样在同一段的末尾,标准说: 翻译单元可以单独翻译,然后链接以产生可执行程序。 它可以(通常是)解释为编译单个目标文件,然后最终链接它们以生成单个可执行程序。 但是,如果可以从上述语句中提出问题并询问:这是否意味着实现可以自由地将多个源文件视为单个翻译单元,尤其是对于以下调用: gcc file1.c file2.c -o out 编译器可以访问整个源? 特别是,如果实现将file1.c + file2.c (上面)视为单个翻译单元,是否可以将其视为不符合?