为什么会出现这种情况?使用 Windows WSL(ubuntu 20.04) 时的 c 库问题
Why this situation was happen? c lib problem while using Windows WSL(ubuntu 20.04)
我在 strcpy 和 strtol 函数中对 c lib 使用 gcc 时遇到一个奇怪的问题。
测试两种情况,得到截然不同的结果。
//#The bad code is "res=68"
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
int main() {
char buf[10];
char* endptr;
int x;
int res;
memset(buf, 0, sizeof(buf));
res=0;
strcpy(buf, "a678b");
while (*(buf) != '[=12=]') {
x = strtol(buf, &endptr, 10);
if (x == 0) {
strcpy(buf, (endptr + 1));
}
else {
strcpy(buf, endptr);
}
res+= x;
}
printf("%d", res);
return 0;
}
改成如下区域后,可以得到正确的值:678。
但是,为什么?
while (*(buf) != '[=13=]') {
x = strtol(buf, &endptr, 10);
if (x == 0) {
memset(kk, 0, sizeof(kk)); // add this
strcpy(kk, (endptr + 1));// add this
strcpy(buf, kk);
}
else {
strcpy(buf, endptr);
}
res+= x;
}
对strcpy
、memcpy
和朋友的调用不允许使用重叠的内存。如果内存重叠,则行为未定义。对于像您这样的短副本,由于一次复制多个字节的优化,这很可能会产生奇怪的结果。
有一个专为重叠记忆设计的函数,叫做memmove
。
但是,您根本不需要使用这些功能。我已经在下面更新了您的程序,只需通过您的字符串遍历指针即可实现此目的。好像没必要一直复制回buf
.
开头
#include <stdio.h>
#include <stdlib.h>
int main()
{
char buf[10] = "a678b";
int res = 0;
for (const char *s = buf; *s != '[=10=]'; )
{
char* endptr;
int x = strtol(s, &endptr, 10);
if (s == endptr)
++endptr;
else
res += x;
s = endptr;
}
printf("%d", res);
}
此程序产生输出:
678
另请注意,我更改了 strtol
不读取数字时的测试。 return 值 0 可以完全有效。测试它是否真的解析了一个数字的正确方法是检查 endptr
是否超过了字符串的开头。
我还把res
初始化为0,因为在你的程序中你在给它加值之前没有初始化它。这也是未定义的行为。
总而言之,由于滥用标准库函数和未初始化内存,您的程序中存在一些未定义行为的情况。
我在 strcpy 和 strtol 函数中对 c lib 使用 gcc 时遇到一个奇怪的问题。 测试两种情况,得到截然不同的结果。
//#The bad code is "res=68"
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
int main() {
char buf[10];
char* endptr;
int x;
int res;
memset(buf, 0, sizeof(buf));
res=0;
strcpy(buf, "a678b");
while (*(buf) != '[=12=]') {
x = strtol(buf, &endptr, 10);
if (x == 0) {
strcpy(buf, (endptr + 1));
}
else {
strcpy(buf, endptr);
}
res+= x;
}
printf("%d", res);
return 0;
}
改成如下区域后,可以得到正确的值:678。 但是,为什么?
while (*(buf) != '[=13=]') {
x = strtol(buf, &endptr, 10);
if (x == 0) {
memset(kk, 0, sizeof(kk)); // add this
strcpy(kk, (endptr + 1));// add this
strcpy(buf, kk);
}
else {
strcpy(buf, endptr);
}
res+= x;
}
对strcpy
、memcpy
和朋友的调用不允许使用重叠的内存。如果内存重叠,则行为未定义。对于像您这样的短副本,由于一次复制多个字节的优化,这很可能会产生奇怪的结果。
有一个专为重叠记忆设计的函数,叫做memmove
。
但是,您根本不需要使用这些功能。我已经在下面更新了您的程序,只需通过您的字符串遍历指针即可实现此目的。好像没必要一直复制回buf
.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char buf[10] = "a678b";
int res = 0;
for (const char *s = buf; *s != '[=10=]'; )
{
char* endptr;
int x = strtol(s, &endptr, 10);
if (s == endptr)
++endptr;
else
res += x;
s = endptr;
}
printf("%d", res);
}
此程序产生输出:
678
另请注意,我更改了 strtol
不读取数字时的测试。 return 值 0 可以完全有效。测试它是否真的解析了一个数字的正确方法是检查 endptr
是否超过了字符串的开头。
我还把res
初始化为0,因为在你的程序中你在给它加值之前没有初始化它。这也是未定义的行为。
总而言之,由于滥用标准库函数和未初始化内存,您的程序中存在一些未定义行为的情况。