`while(*p++ = *(p+1));` 是未定义的行为吗?
Is `while(*p++ = *(p+1));` undefined behavior?
我有使用单行 while
循环语句操作 C 字符串的代码。
它在使用 MSVC2015 编译时完美运行,但在使用 TDM-GCC (gcc (tdm-1) 5.1.0) 编译时给出不同的结果。
这是一个显示问题的最小示例。该代码用下一个字符覆盖当前字符,一遍又一遍地重复,直到将当前字符设置为 [=13=]
.
#include <stdio.h>
int main()
{
char buf[999] = "Foobar", *p = buf;
while(*p++ = *(p+1));
printf("buf = %s\n", buf);
return 0;
}
当使用 MSVC2015 编译代码时,输出为 buf = oobar
,符合预期。然而,对于 TDM-GCC,输出是 buf = obar
.
如果我将 while 语句更改为 while(*p = *(p+1)) { ++p; }
,两个编译器都会给出预期的结果 buf = oobar
。似乎通过将 post-increment 运算符放在表达式中,我以某种方式触发了未定义的行为。
我的问题是,为什么代码在使用不同的编译器编译时表现不同?将增量运算符放在非平凡的 while
语句中是错误的(或非标准的)吗?我是否触发了未定义的行为?如果是这样,代码应该如何按照 C 标准运行?如果不是,谁应该为此负责? TDM-海湾合作委员会? MSVC?
更新:对于那些和我有同样疑问的人,答案是:是的,代码调用了UB。明确的方法是这样做: while(*p = *(p+1)){++p;}
有人问我们为什么要这样编码。这里是这个成语可能有用的场景。
#include <stdio.h>
#include <Windows.h>
static void EscapeDquote(char * const sz)
{
char *p = sz;
BOOL bs = FALSE;
for (; *p; ++p)
{
if (*p == '\') {
bs = !bs;
continue;
}
if (*p == '\"') {
if (bs) {
/*
discard prev char (backslash before dquote)
overwrite with next char until null-termi
*/
char *q = --p;
/* OLD version, not OK for GCC */
/* while(*q++ = *(q+1)); */
/* Safer version, works in GCC as well: */
while(*q = *(q+1)){++q;}
}
}
bs = FALSE;
}
}
int main()
{
/* "call \"D:\foo bar.exe\" */
char szTest[] = "call \\"D:\foo bar.exe\\"";
printf("Before = %s\n", szTest);
EscapeDquote(szTest);
printf("After = %s\n", szTest);
return 0;
}
如果您使用的是 GCC 编译器,请使用 -Wall
。这确实是 C 和 C++ 中的未定义行为。
查看现场演示 here。
查看编译器给出的诊断
main.cpp: In function 'int main()':
main.cpp:6:13: warning: operation on 'p' may be undefined [-Wsequence-point]
while(*p++ = *(p+1));
~^~
这是未定义的行为,因为以下两个操作是无序的:
- 在
p++
中写入p
p
在 (p+1)
中的读取
这是未定义的行为,因为有未定义的序列点。
解决方法是 while ((*p = *(p + 1)) && p++);
,如果您希望它在单行中。
现在您将首先设置您的指针,如果分配的值为 非零 ,您将继续定义序列到 p++
语句。如果赋值值为 0,while 循环将结束意味着字符串被移位。
是的,这是未定义的行为,因为 Clang 编译器给出以下错误:
source_file.cpp:6:13: warning: unsequenced modification and access to 'p' [-Wunsequenced]
while(*p++ = *(p+1));
^ ~
C11:6.5 表达式:
If a side effect on a scalar object is unsequenced relative to either
a different side effect on the same scalar object or a value
computation using the value of the same scalar object, the behavior is
undefined. If there are multiple allowable orderings of the
subexpressions of an expression, the behavior is undefined if such an
unsequenced side effect occurs in any of the orderings
我有使用单行 while
循环语句操作 C 字符串的代码。
它在使用 MSVC2015 编译时完美运行,但在使用 TDM-GCC (gcc (tdm-1) 5.1.0) 编译时给出不同的结果。
这是一个显示问题的最小示例。该代码用下一个字符覆盖当前字符,一遍又一遍地重复,直到将当前字符设置为 [=13=]
.
#include <stdio.h>
int main()
{
char buf[999] = "Foobar", *p = buf;
while(*p++ = *(p+1));
printf("buf = %s\n", buf);
return 0;
}
当使用 MSVC2015 编译代码时,输出为 buf = oobar
,符合预期。然而,对于 TDM-GCC,输出是 buf = obar
.
如果我将 while 语句更改为 while(*p = *(p+1)) { ++p; }
,两个编译器都会给出预期的结果 buf = oobar
。似乎通过将 post-increment 运算符放在表达式中,我以某种方式触发了未定义的行为。
我的问题是,为什么代码在使用不同的编译器编译时表现不同?将增量运算符放在非平凡的 while
语句中是错误的(或非标准的)吗?我是否触发了未定义的行为?如果是这样,代码应该如何按照 C 标准运行?如果不是,谁应该为此负责? TDM-海湾合作委员会? MSVC?
更新:对于那些和我有同样疑问的人,答案是:是的,代码调用了UB。明确的方法是这样做: while(*p = *(p+1)){++p;}
有人问我们为什么要这样编码。这里是这个成语可能有用的场景。
#include <stdio.h>
#include <Windows.h>
static void EscapeDquote(char * const sz)
{
char *p = sz;
BOOL bs = FALSE;
for (; *p; ++p)
{
if (*p == '\') {
bs = !bs;
continue;
}
if (*p == '\"') {
if (bs) {
/*
discard prev char (backslash before dquote)
overwrite with next char until null-termi
*/
char *q = --p;
/* OLD version, not OK for GCC */
/* while(*q++ = *(q+1)); */
/* Safer version, works in GCC as well: */
while(*q = *(q+1)){++q;}
}
}
bs = FALSE;
}
}
int main()
{
/* "call \"D:\foo bar.exe\" */
char szTest[] = "call \\"D:\foo bar.exe\\"";
printf("Before = %s\n", szTest);
EscapeDquote(szTest);
printf("After = %s\n", szTest);
return 0;
}
如果您使用的是 GCC 编译器,请使用 -Wall
。这确实是 C 和 C++ 中的未定义行为。
查看现场演示 here。
查看编译器给出的诊断
main.cpp: In function 'int main()':
main.cpp:6:13: warning: operation on 'p' may be undefined [-Wsequence-point]
while(*p++ = *(p+1));
~^~
这是未定义的行为,因为以下两个操作是无序的:
- 在
p++
中写入 p
在(p+1)
中的读取
p
这是未定义的行为,因为有未定义的序列点。
解决方法是 while ((*p = *(p + 1)) && p++);
,如果您希望它在单行中。
现在您将首先设置您的指针,如果分配的值为 非零 ,您将继续定义序列到 p++
语句。如果赋值值为 0,while 循环将结束意味着字符串被移位。
是的,这是未定义的行为,因为 Clang 编译器给出以下错误:
source_file.cpp:6:13: warning: unsequenced modification and access to 'p' [-Wunsequenced]
while(*p++ = *(p+1));
^ ~
C11:6.5 表达式:
If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings