据称"巧妙"(但实际上效率低下)交换两个整数变量而不是使用临时存储的方式通常涉及这一行:
int a = 10;
int b = 42;
a ^= b ^= a ^= b; /*Here*/
printf("a=%d, b=%d\n", a, b);
Run Code Online (Sandbox Code Playgroud)
但我想知道,复合赋值运算符^=不是序列点,是吗?这是否意味着它实际上是未定义的行为?
在阅读序列点后,我了解到这i = ++i是未定义的.
那么这段代码怎么样:
int i;
int *p = &i;
int *q = &i;
*p = ++(*q); // that should also be undefined right?
Run Code Online (Sandbox Code Playgroud)
假设p和q的初始化取决于某些(复杂)条件.他们可能像上面的情况一样指向同一个对象.会发生什么?如果未定义,我们可以使用哪些工具进行检测?
编辑:如果两个指针不应该指向同一个对象,我们可以使用C99限制吗?这是"严格"的意思吗?
以下表达式通常用于演示未定义的未指定行为:
f() + g()
Run Code Online (Sandbox Code Playgroud)
如果f()和g()都有一些共享对象的副作用,则行为是不确定的不确定,因为执行的顺序是未知的.f()可以在之前评估,g()反之亦然.
现在我想知道当你在一个对象上链接成员函数时会发生什么.比方说,我有一个类,叫做实例的实例obj,它有两个成员函数,foo()而bar()这两个修改的对象.这些函数的执行顺序不是可交换的.在一个接一个之前调用一个的效果与用另一个方式调用它们的效果不同.这两个方法都返回一个引用,*this以便它们可以像这样链接:
obj.foo().bar()
Run Code Online (Sandbox Code Playgroud)
但这是不明确的行为吗?我在标准中找不到任何东西(不可否认只是扫描),区分了这个表达式和我在帖子顶部给出的表达.两个函数调用都是full-expression的子表达式,因此它们的执行顺序是未指定的.但肯定foo() 必须先评估,以便bar()知道要修改哪个对象.
也许我错过了一些明显的东西,但我无法看到序列点的创建位置.
我在这里读到有一个序列点:
在与输入/输出转换格式说明符相关联的操作之后.例如,在表达式中
printf("foo %n %d", &a, 42),%n在打印之前评估之后有一个序列点42.
但是,当我运行此代码时:
int your_function(int a, int b) {
return a - b;
}
int main(void) {
int i = 10;
printf("%d - %d - %d\n", i, your_function(++i, ++i), i);
}
Run Code Online (Sandbox Code Playgroud)
而不是我期望得到的:
12 - 0 - 12
这意味着没有为转换格式说明符创建序列点.http://en.wikipedia.org是错误的,还是我只是误解了某些内容,或者在这种情况下gcc是否不合规(顺便提一下Visual Studio 2015会产生相同的意外结果)?
编辑:
我理解,参数your_function的评估顺序和分配给参数的顺序是未定义的.我不是在问我为什么我的中期是0.我在问为什么其他两个术语都是12.
在遇到问题" 为什么这些构造使用前后增量未定义的行为? "之后,我决定抓住最新的草案,为我能找到的下一个C标准,并阅读更多相关内容.
在我在C17草案中发现以下段落后不久:
表达式是操作符和操作数的序列,其指定值的计算,或指定对象或函数,或者生成副作用,或执行其组合.在运算符结果的值计算之前对运算符的操作数的值计算进行排序
来源:ISO/IEC 9899:2017,第6.5.1节"表达式"
现在我有点困惑.这i = i++是不是意味着定义了行为?我这次看了另一个草案,C99:
表达式是操作符和操作数的序列,其指定值的计算,或指定对象或函数,或者生成副作用,或执行其组合.
资料来源:ISO/IEC 9899:1999,第6.5.1节"表达式"
它错过了那句话!
Originally, I presented a more complicated example, this one was proposed by @n. 'pronouns' m. in a now-deleted answer. But the question became too long, see edit history if you are interested.
Has the following program well-defined behaviour in C++17?
int main()
{
int a=5;
(a += 1) += a;
return a;
}
Run Code Online (Sandbox Code Playgroud)
I believe this expression is well-defined and evaluated like this:
a is evaluated to 5.我在以下方面使用strcmp
将指针传递给字符串文字,但第二个结果是seg错误.即使我已经确认指针指向正确的字符串文字,我很困惑为什么我得到seg错误..这是代码: -
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *args[])
{
char firstName[strlen(*++args)];
strcpy(firstName, *args);
char lastName[strlen(*++args)];
strcpy(lastName, *args);
printf("%s\t%s\n", firstName, lastName);
printf("%d\n", strcmp(firstName, lastName));// this works
printf("%d\n", strcmp(*(--args),*(++args)));//this gives me a seg fault
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)我将它保存为str.c,当我编译它时,首先我得到以下警告:
[Neutron@Discovery examples]$ gcc -Wall str.c -o str
str.c: In function ‘main’:
str.c:15: warning: operation on ‘args’ may be undefined
Run Code Online (Sandbox Code Playgroud)
最后运行它,给出一个seg错误,如下所示
[Neutron@Discovery examples]$ ./str Jimmy Neutron
Jimmy Neutron
-4
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud) 假设以下代码:
#include <iostream>
using namespace std;
char one()
{
cout << "one\n";
return '1';
}
char two()
{
cout << "two\n";
return '2';
}
int main(int,char**)
{
// 1:
cout << one()
<< '\n'
<< two()
<< '\n';
// 2:
operator<<(
operator<<(
operator<<(
operator<<(
cout,
one()),
'\n'),
two()),
'\n');
}
Run Code Online (Sandbox Code Playgroud)
执行标记为1和的行,并2使用ideone编译时执行相同的操作,它打印如下:
two
one
1
2
Run Code Online (Sandbox Code Playgroud)
从我的观点来看,我们在这里观察到的是未指定的行为,因为未指定解析函数参数的顺序.
这是一个在采访中的问题,打印上面给出的序列(没有任何替代方案)应该是正确答案,但它是否真的正确?
在最近的这个问题中,一些代码显示有未定义的行为:
a[++i] = foo(a[i-1], a[i]);
Run Code Online (Sandbox Code Playgroud)
因为即使实际调用的foo()是一个序列点,该分配是未测序,所以你不知道该功能是否被调用后的副作用不++i发生或之前.
进一步思考这一点,函数调用的序列点只保证在输入函数后执行评估函数参数的副作用,例如
int y = 1;
int func1(int x) { return x + y; }
int main(void)
{
int result = func1( y++ ); // guaranteed to be 3
}
Run Code Online (Sandbox Code Playgroud)
但是看看标准,还有§7.1.4p3(关于标准库的章节):
库函数返回之前有一个序列点.
我的问题是:这一段的结果是什么?为什么它只涉及库函数以及实际依赖的代码类型?
像(无意义的代码)这样的简单想法
errno = 0;
long result = ftell(file) * errno;
Run Code Online (Sandbox Code Playgroud)
这个时候仍然是未定义的,乘法是不确定的.我正在寻找一个利用这个特殊保证的例子§7.1.4p3使库函数成为可能.
关于建议的重复, …
sequence-points ×10
c ×6
c++ ×5
args ×1
c++17 ×1
c17 ×1
c99 ×1
constructor ×1
evaluation ×1
exception ×1
printf ×1
side-effects ×1
strcmp ×1