- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
根据“未定义行为”的现代解释,编译器有权假设不会发生导致未定义行为“不可避免”的事件链,并且可以消除仅适用于以下情况的代码:将执行未定义的行为;这可能会导致未定义行为的影响及时倒退,并使原本可以观察到的行为无效。另一方面,如果除非程序终止,否则未定义行为将不可避免,但程序可以并且确实在调用未定义行为之前终止,程序的行为将保持完全定义。
在做出这个决定时,编译器需要考虑哪些终止原因?举几个例子:
在许多平台上,对像“getc”这样的函数的调用通常会返回(至少最终),但在某些超出编译器控制的情况下不会返回。如果有这样的程序:
int main(int argc, char *argv[])
{
if (argc != 3)
{
printf("Foo\n");
return 0;
}
else
{
int ch;
printf("You'd better type control-C!\n");
int ch = getc();
if (ch < 65)
return (ch-33) / (argc-3);
else
return INT_MAX + ch;
}
}
如果使用等于 3 的 argc
调用程序,但 SIGINT 阻止 getc()
调用返回,是否会定义行为?当然,如果 getc()
可以返回任何值,这将导致定义的行为,则在编译器可以确定此类输入不会发生之前,不会发生未定义的行为已收到。如果没有值 getc()
可以返回,这将避免未定义的行为,但是,如果 getc()
被阻止返回任何值,整个程序是否会保持定义值(value)? getc()
的返回值与调用未定义行为的操作之间是否存在因果关系会影响事情吗(在上面的示例中,编译器无法知道任何特定 在不知道输入什么字符的情况下会发生未定义行为的形式,但任何可能的输入都会触发某种形式)。
同样,如果在平台上存在地址,如果读取这些地址,则指定会导致程序立即终止,编译器指定 volatile 读取将触发硬件读取请求,并且该平台上的某些外部库指定它会返回指向这样一个地址的指针,这些因素是否意味着 bar
在这个单独的例子中的行为:
int foo(int x)
{
char volatile * p = get_instant_quit_address();
if (x)
{ printf("Hey"); fflush(stdout); }
return *p / x; // Will cause UB if *p yields a value and x is zero
}
int bar(void)
{
return foo(0);
}
如果尝试读取 *p 实际上会立即终止程序执行而不产生值,那么
将被定义(作为终止而没有打印任何东西)?在返回一个值之前,除法不能继续;因此,如果没有返回值,则不会除以零。
允许 C 编译器通过什么方式确定给定的操作是否可能导致程序执行以它不知道的方式终止,以及在什么情况下允许在此类操作之前重新安排未定义的行为?
最佳答案
这在 [intro.execution] 下的 C++ 中有很好的描述:
5 - A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible executions of the corresponding instance of the abstract machine with the same program and the same input. However, if any such execution contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).
普遍认为 C 具有相同的特性,因此 C 编译器可以类似地执行 "time travel"存在未定义的行为。
重要的是,请注意问题是是否存在表现出未定义行为的抽象机实例;您可以通过先终止程序执行来安排防止机器上出现未定义的行为,这并不重要。
如果您使程序以抽象机器无法摆脱的完全定义的方式自行终止,您可以防止未定义的行为(以及由此产生的时间旅行)。例如,在您的第二个示例中,如果您将对 *p
的访问替换为 (exit(0), 0)
那么未定义的行为就不会发生,因为不可能执行exit
返回给它的调用者的抽象机。但是无论您的平台有何特点,抽象机都不必在访问 insta-kill 地址时终止您的程序(事实上,抽象机没有任何 insta-kill 地址)。
关于如果 getc() 通过 SIGINT 退出,则 getc() 之后的未定义行为是否可以改变程序行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29855620/
根据“未定义行为”的现代解释,编译器有权假设不会发生导致未定义行为“不可避免”的事件链,并且可以消除仅适用于以下情况的代码:将执行未定义的行为;这可能会导致未定义行为的影响及时倒退,并使原本可以观察到
在阅读 getc 手册页时,我遇到了: may be implemented as a macro which evaluates stream more than once. 那么,getc 也可以
程序有什么问题? #include #include #include main( ) { char * buf="robot.c"; char c;int i=0; FILE*f
在下面的代码中,我尝试存储文件中的所有字符(包括换行符)。如果读取换行符,变量“i”应该递增,“j”重置为 0,但这种情况不会发生。通过从我的数组打印到控制台,我已经确认换行符实际上正在被读取和存储。
它只是一个程序,我试图读取文件中作为参数传递的单词的出现次数,该文件也作为下一个参数传递。 代码如下所示: #include extern void exit(int); void main(int
我正在用 C 开发一个链表。我正在从一个 txt 文件中获取数据。但是当我尝试运行该程序时,它给我一个 getc() 的段错误这是代码, #include #include struct node{
程序: #include #include char *f_gets(char *s, int n, FILE *iop) { int c=0; char *cs; cs =
我试图找到 getc 和 fgetc 之间的区别。当时我看到这样的说法: The difference between getc and fgetc is that getc can be imple
我正在使用 Perl 6 模块 Term::termios . #!/usr/bin/env perl6 use v6; use Term::termios; my $saved_termios :=
我有一个函数读取 unsigned long long 中的每一位并将值存储在数组中,现在我想将此函数转换为返回数字中的下一位并将数据处理留给调用者的函数. 理想情况下,它的工作方式与 getc()
我有一个函数读取 unsigned long long 中的每一位并将值存储在数组中,现在我想将此函数转换为返回数字中的下一位并将数据处理留给调用者的函数. 理想情况下,它的工作方式与 getc()
我有一个函数 getNum(),它从文件中获取一个数字并返回它。当我回到 getNum() 时,我丢失了指针,它再次从文件的开始处开始。我想知道如何获取 getc 所在的位置,然后返回到那个地方。我在
所以我开始实现霍夫曼树,为此,我尝试从标准输入或输入文件获取字符值。输入文件(只是一个字符串“cheese”)被添加到数组 freqcounts 中,其中添加的 freqcounts 索引是它读取的字
我遇到的问题是在这一行: int tok = getc(fp); getc 返回 -1。为什么?提前致谢。 #include #include #include "file_reader.h" /
printf("hello2"); int i = 0; int done = 0; while (!done) { char c; printf("hello3"); c =
我正在阅读 Jim Trevor 等人所著的Cyclone:C 的安全方言。一切都是为了编程语言类(class)。作者表示,如果调用 getc(null) 可能会导致段错误,因为 C 标准没有指定如何
当我尝试从名为“file1”的文件中读取输入时,我的程序正确显示文件中的字符数,但采用无法识别的字符格式。下面是代码 #include #include void db_sp(FILE*); in
// Program to remove the comments and the spaces from the given input file #include #include using
这是我的代码。 #include #include int main(int argc,char** argv) { char a; a=9; FILE * fp; f
我正在尝试用 C 编写一个简单的“猫”克隆。我正在运行 Windows 7 并使用 MinGW 编译器。但是,每当我运行该程序时,它都会返回文本文件,但每个字符都替换为“☺”字符。提前谢谢你。 #in
我是一名优秀的程序员,十分优秀!