C和C++中未定义,未指定和实现定义的行为有什么区别?
c c++ undefined-behavior unspecified-behavior implementation-defined-behavior
这个问题试图收集社区维护的关于c编程语言的优质书籍清单,目标是各种技能水平.
C是一种复杂的编程语言,通过阅读在线教程很难在旅途中学习.综合性书籍通常是学习语言的最佳方式,找到一本好书是第一步.重要的是要避免写得不好的书籍,更重要的是要避免包含严重技术错误的书籍.
请建议编辑接受的答案,以添加高质量的书籍,具有近似的技能水平和每本书的简短描述/描述.(请注意,问题已被锁定,因此不会接受新的答案.列表中会保留一个答案)
随意讨论书籍选择,质量,标题,摘要,技能水平以及您认为错误的任何其他内容.C社区认为令人满意的书籍将列在名单上; 其余的将定期删除.
对于由C和C++用户协会(ACCU)进行评论的书籍,应该与书籍一起添加指向这些评论的链接.
也可以看看:
这个问题在Meta上作为2018年删除问题审计的一部分进行了讨论.
达成共识的目的是保持其未被删除和积极维护.
我正在阅读有关评估违规的顺序,他们给出了一个令我困惑的例子.
1)如果标量对象的副作用相对于同一标量对象的另一个副作用未按顺序排列,则行为未定义.
Run Code Online (Sandbox Code Playgroud)// snip f(i = -1, i = -1); // undefined behavior
在这种情况下,i是一个标量对象,显然意味着
算术类型(3.9.1),枚举类型,指针类型,指向成员类型的指针(3.9.2),std :: nullptr_t和这些类型的cv限定版本(3.9.3)统称为标量类型.
在这种情况下,我不明白该陈述是如何含糊不清的.在我看来,无论第一个或第二个参数是否首先被评估,i最终都是-1,并且两个参数也是-1.
有人可以澄清一下吗?
我非常感谢所有的讨论.到目前为止,我非常喜欢@ harmic的答案,因为它暴露了定义这个陈述的陷阱和错综复杂,尽管它看起来有多么简单.@ acheong87指出了使用引用时出现的一些问题,但我认为这与这个问题的未测序副作用方面是正交的.
由于这个问题得到了很多关注,我将总结一下主要观点/答案.首先,请允许我进行一个小小的题外话,指出"为什么"可以具有密切相关但又略有不同的含义,即"为什么原因 ","为什么原因 "和"为了什么目的 ".我将根据他们所解决的"为什么"的含义分组答案.
这里的主要答案来自Paul Draper,Martin J提供了类似但不那么广泛的答案.Paul Draper的回答归结为
它是未定义的行为,因为它没有定义行为是什么.
答案在解释C++标准所说的内容方面总体上非常好.它还解决了UB的一些相关案例,如f(++i, ++i);和f(i=1, i=-1);.在第一个相关案例中,不清楚第一个论点是否应该是i+1第二个i+2,反之亦然; 在第二个中,不清楚i函数调用后是否应为1或-1.这两种情况都是UB,因为它们属于以下规则:
如果相对于同一标量对象的另一个副作用,标量对象的副作用未被排序,则行为未定义.
因此,f(i=-1, i=-1)也是UB,因为它属于同一规则,尽管程序员的意图是(恕我直言)显而易见且毫不含糊.
Paul Draper在他的结论中也明确表示
可以定义行为吗?是.它被定义了吗?没有.
这让我们想到"为什么原因/目的是 …
考虑这个代码:
int i = 1;
int x = ++i + ++i;
Run Code Online (Sandbox Code Playgroud)
我们对编译器可能会为这段代码做些什么有一些猜测,假设它可以编译。
++i返回2,导致x=4.++i返回2,另一个返回3,结果为x=5。++i返回3,导致x=6.对我来说,第二个似乎最有可能。两个++运算符之一用 执行i = 1,i递增,并2返回结果。然后用++执行第二个运算符i = 2,i递增,并3返回结果。然后2和3相加得到5。
但是,我在 Visual Studio 中运行了这段代码,结果是6. 我试图更好地理解编译器,我想知道什么可能导致6. 我唯一的猜测是代码可以通过一些“内置”并发来执行。++调用了两个运算符,每个运算符i在另一个返回之前递增,然后它们都返回3。这与我对调用堆栈的理解相矛盾,需要加以解释。
C++ …
如何以两种不同的方式为后缀a++和前缀重载operator ++ ++a?
在典型的C++代码中,C++ 17评估顺序保证(P0145)投票的含义是什么?
对于像这样的事情,它有什么变化
i=1;
f(i++, i)
Run Code Online (Sandbox Code Playgroud)
和
std::cout << f() << f() << f() ;
Run Code Online (Sandbox Code Playgroud)
要么
f(g(),h(),j());
Run Code Online (Sandbox Code Playgroud) 因此,谷歌快速搜索fflush(stdin)清除输入缓冲区会发现许多网站警告不要使用它.然而,这正是我的CS教授教授课程的原因.
使用有多糟糕fflush(stdin)?即使我的教授正在使用它并且似乎完美无缺地工作,我是否真的应该放弃使用它?
在C中调用时,可以假设函数参数的评估顺序吗?根据以下程序,我执行时似乎没有特定的顺序.
#include <stdio.h>
int main()
{
int a[] = {1, 2, 3};
int * pa;
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
/* Result: a[0] = 3 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(pa),*(++pa));
/* Result: a[0] = 2 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(++pa), *(pa));
/* a[0] = 2 a[1] = 2 a[2] = …Run Code Online (Sandbox Code Playgroud) 如果我试试这个:
$a = 0;
echo $a + ++$a, PHP_EOL;
echo $a;
Run Code Online (Sandbox Code Playgroud)
我得到这个输出:
2
1
Run Code Online (Sandbox Code Playgroud)
演示:http://codepad.org/ncVuJtJu
我希望得到这个作为输出:
1
1
Run Code Online (Sandbox Code Playgroud)
$a = 0; // a === 0
echo $a + ++$a, PHP_EOL; // (0) + (0+1) === 1
echo $a; // a === 1
Run Code Online (Sandbox Code Playgroud)
但为什么输出不是这样呢?
免责声明: 这不是一个真实的例子.这只是这些语言如何运作的理论问题.
在发布和预增量运算符方面,C/C++,C#和Java之间究竟有什么区别?
这是我用VC++ 10,Java 1.6和C#4获得的
int a = 2;
int b = a++ + a++;
int c = ++a + a++ + a++;
+-----+------+------+----+
| C | C++ | Java | C# |
+-----+-----+------+------+----+
| a | 7 | 7 | 7 | 7 |
+-----+-----+------+------+----+
| b | 4 | 4 | 5 | 5 |
+-----+-----+------+------+----+
| c | 15 | 15 | 16 | 16 |
+-----+-----+------+------+----+
Run Code Online (Sandbox Code Playgroud) c++ ×6
c ×5
c# ×1
c++17 ×1
evaluation ×1
fflush ×1
implementation-defined-behavior ×1
java ×1
math ×1
parameters ×1
php ×1
stdin ×1