考虑由两个文件组成的C程序,
在f1.c:
int x;
Run Code Online (Sandbox Code Playgroud)
f2.c:
int x=2;
Run Code Online (Sandbox Code Playgroud)
我对C99标准第6.9.2段的解读是该程序应该被拒绝.在我对6.9.2的解释中,变量x是暂定的f1.c,但是这个暂定的定义在翻译单元的末尾变成了一个实际的定义,并且(在我看来),应该表现得好像f1.c包含了定义int x=0;.
对于所有编译器(以及重要的是,链接器)我能够尝试,这不是发生的事情.我试过的所有编译平台都链接了上面两个文件,两个文件中的值x都是2.
我怀疑这是偶然发生的,或者只是作为标准要求提供的"简单"功能.如果你考虑一下,这意味着链接器中对那些没有初始化器的全局变量有特殊支持,而不是那些显式初始化为零的全局变量.有人告诉我,无论如何编译Fortran可能都需要链接器功能.那将是一个合理的解释.
有什么想法吗?对标准的其他解释?文件f1.c和f2.c拒绝链接在一起的平台名称?
注意:这很重要,因为问题出现在静态分析的上下文中.如果这两个文件可能拒绝在某个平台上链接,分析器应该抱怨,但是如果每个编译平台都接受它,那么就没有理由对它进行警告.
这个问题遵循前一个关于定义的问题memcpy(0, 0, 0),该问题最终被确定为未定义的行为.
正如相关问题所示,答案取决于C11第7.1.4:1条的内容
除非在以下详细说明中另有明确说明,否则以下每个语句均适用:如果函数的参数具有无效值(例如函数域外的值,或程序地址空间外的指针,或空指针,[...])行为未定义.[...]
标准函数memcpy()需要指向void和const void,如下所示:
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
Run Code Online (Sandbox Code Playgroud)
这个问题值得一问的,只是因为有很多标准的"有效"指针两个概念都:有可以有效地通过指针算术来获得,并且可以有效地相比指针<,>同一对象内部其他指针.并且有一些指针可用于解除引用.前一类包括"一个过去"指针,例如&a + 1和&b + 1下面的代码片段,而后一个类不包括这些指针有效.
char a;
const char b = '7';
memcpy(&a + 1, &b + 1, 0);
Run Code Online (Sandbox Code Playgroud)
如果将上述片段视为已定义的行为,memcpy()则根据void无论如何将参数键入为指针的事实,因此其各自有效性的问题不能解除对它们的解除引用.还是应该&a + 1和&b + 1被认为是"程序的地址空间之外"?
这对我很重要,因为我正在使标准C函数的效果正式化.我写的一个先决条件memcpy()是requires \valid(s1+(0 .. n-1));,直到有人指出我的注意,GCC 4.9已经开始积极优化这样的库函数调用超出了公式中表示上述( …
请考虑以下代码段:
void f(void);
void g(…)
{
…
return f();
…
}
Run Code Online (Sandbox Code Playgroud)
return f();根据C11,这是否有效?
我并不主张使用这种模式:如果它完全起作用,它显然等同于f(); return;(return;如果它在函数的末尾,它本身将是多余的g()).我在C程序的静态分析的上下文中提出这个问题,其中C代码已经由其他人编写,问题是根据标准判断它是否有效.
我认为C11 6.8.6.4:1意味着它是非标准的,应该被静态拒绝.是否有可能以不同的方式解释它(我在实际和其他高质量的源代码中找到了这种模式)?
约束
带有表达式的return语句不应出现在返回类型为void的函数中.不带表达式的return语句只能出现在返回类型为void的函数中.
我正在查看以下面的指令开头的OCaml源文件:
open! MiscParser
Run Code Online (Sandbox Code Playgroud)
我理解这open MiscParser意味着"打开MiscParser模块",但我不知道感叹号的含义.
我有一个functor,它接受一个值,将其转换为double,获取日志并将值转换回原始类型.出于此问题的目的,原始和输出类型是float.这是原始的C++代码:
return static_cast< TOutput >( std::log( static_cast< double >( A ) ) )
Run Code Online (Sandbox Code Playgroud)
当我在调试模式下编译时,一切都按预期进行,GCC调用底层log函数:
51:/myfile.h **** return static_cast< TOutput >( std::log( static_cast< double >( A ) ) );
219133 .loc 112 51 0
219134 0010 488B45F0 movq -16(%rbp), %rax # A, tmp64
219135 0014 F30F1000 movss (%rax), %xmm0 # *A_1(D), D.237346
219136 0018 0F14C0 unpcklps %xmm0, %xmm0 # D.237346, D.237346
219137 001b 0F5AC0 cvtps2pd %xmm0, %xmm0 # D.237346, D.237347
219138 001e E8000000 call log #
219138 00
219139 …Run Code Online (Sandbox Code Playgroud) 注意:已经将此作为Wiki.只要有一个很好的讨论,我不在乎这个问题被标记为什么.
我听说过,因为在纯函数式程序中,没有副作用和值不变,它使编译器更容易进行更多的运行时优化.这到底有多大?
如果这是真的,我的下一个问题是我们为此交易的自由损失是什么?我的意思是,在像C++/C这样的语言中,开发人员完全可以控制并且可以调整很多东西.如果我们把这份工作交给编译器,我们就失去了这个机会.这方面的好处是,即使是非专业程序员也可以编写好的代码.此外,如今在机器架构中有如此多的缓存层,甚至可能是专家也无法真正做任何有价值的事情.因此,将此作业委托给比程序员更了解底层架构的编译器是个好主意.
你有什么建议?
相关问题:赋值运算符不是序列点的任何充分理由?
从comp.lang.c FAQ我会推断下面的程序是未定义的.奇怪的是,它只是f在参数计算和控制转移之间提到了作为序列点的调用f.从f后面到调用表达式的控制转移未列为序列点.
int f(void) { i++; return 42; }
i = f();
Run Code Online (Sandbox Code Playgroud)
它真的未定义吗?
作为我在我的许多问题中添加的最后一点,我对静态分析的背景感兴趣.我不是自己写的,我只是想知道我是否应该在其他人编写的程序中对此进行警告.
有谁知道包含浮点计算的C程序的随机生成器?
我正在寻找一些有点像Csmith的东西,除了Csmith不会生成浮点表达式,并且它会产生大量其他构造,使得修改有点困难.生成顺序计算对我来说是一个良好的开端,只要这些包括一些浮点数.条件会更好,但我不需要循环,指针,甚至数组.
由于这么多语言使用类C语法,这样的生成器可能不必特定于C.即使它特定于另一种类C语言,我也许能够将生成的语言文本处理为C程序.
编辑:这是一个Csmith生成的程序的片段,以澄清我在寻找什么.
...
int64_t *l_374 = &g_189;
int32_t l_375 = (-1L);
int i, j, k;
l_375 &= ((g_106 == ((*l_374) = (&g_324[4] == l_373[0][0][5]))) < 0x80C8L);
return (*g_207);
...
Run Code Online (Sandbox Code Playgroud)
我也应该澄清的是,虽然采取了输入csmith程序而代,比方说,int64_t
与float可以给一个语法正确的C程序,它几乎肯定不会给一个定义的程序.我可以测试替换程序是否包含未定义的行为,但这并不便宜,如果我必须拒绝99%的替换程序,因为它们未定义,过程将太慢而无法使用.