只是想理解在最近的采访中询问的以下代码。
#include <stdio.h>
#include <string.h>
int main() {
char *ptr = "Linux";
char a[] = "Solaris";
strcat(a, ptr);
printf("%s\n", ptr);
printf("%s\n", a);
return 0;
}
执行轨迹:
gcc -Wall -g prog.c
gdb a.out
(gdb) p ptr
$15 = 0x400624 "Linux"
(gdb) p a+1
$20 = 0x7fffffffe7f1 "olarisLinux"
**(gdb) p a
$21 = "SolarisL"**
**(gdb) p a+0
$22 = 0x7fffffffe7f0 "SolarisLinux"**
(gdb)
$23 = 0x7fffffffe7f0 "SolarisLinux"
**(gdb) p ptr
$24 = 0x78756e69 <error: Cannot access memory at address 0x78756e69>
(gdb)**
我有几个问题:
strcat
是否从原始位置删除字符串文字,因为访问ptr
会出现段错误?为什么 gdb 中的
p a
没有给出正确的输出,而p a+0
显示"SolarisLinux"
?
最佳答案
如果我理解你的问题是正确的,你知道程序有未定义的行为,因为 a
不能保存与“Linux”连接的字符串“Solaris”。
所以您要寻找的答案不是“这是未定义的行为”,而是:
why is it behaving this way
在处理未定义行为时,我们无法对正在发生的事情给出一般性解释。它可能在不同的系统上做不同的事情,或者为不同的编译器(或编译器版本)做不同的事情等等。
因此,人们常说尝试解释具有未定义行为的程序中发生的事情是没有意义的。好吧 - 没错。
但是 - 有时您可以找到针对您的特定系统的解释 - 请记住它是特定于您的系统的,绝不是通用的。
所以我更改了您的代码以添加一些调试打印:
#include<stdio.h>
#include<string.h>
int main()
{
char *ptr = "Linux";
char a[] = "Solaris";
printf(" a = %p\n", (void*)a);
printf("&ptr = %p\n", (void*)&ptr);
printf(" ptr = %p\n", (void*)ptr);
// Print the data that ptr holds
unsigned char* p = (unsigned char*)&ptr;
printf("\nBefore strcat\n");
printf(" a:\n");
for (int i = 0; i < 8; ++i) printf("%02x ", *(a+i));
printf("\n");
printf(" ptr:\n");
for (int i = 0; i < 8; ++i) printf("%02x ", *(p+i));
printf("\n");
strcat(a,ptr);
printf("\nAfter strcat\n");
printf(" a:\n");
for (int i = 0; i < 8; ++i) printf("%02x ", *(a+i));
printf("\n");
printf(" ptr:\n");
for (int i = 0; i < 8; ++i) printf("%02x ", *(p+i));
printf("\n\n");
printf("%s\n", a);
printf("%s\n", ptr);
return 0;
}
在我的系统上这会生成:
a = 0x7ffff3ce5050
&ptr = 0x7ffff3ce5058
ptr = 0x400820
Before strcat
a:
53 6f 6c 61 72 69 73 00
ptr:
20 08 40 00 00 00 00 00
After strcat
a:
53 6f 6c 61 72 69 73 4c
ptr:
69 6e 75 78 00 00 00 00
SolarisLinux
Segmentation fault
这里的输出添加了一些注释:
a = 0x7ffff3ce5050 // The address where the array a istored
&ptr = 0x7ffff3ce5058 // The address where ptr is stored. Notice 8 higher than a
ptr = 0x400820 // The value of ptr
Before strcat
a:
53 6f 6c 61 72 69 73 00 // Hex dump of a gives Solaris\0
ptr:
20 08 40 00 00 00 00 00 // Hex dump of ptr is the value 0x0000000000400820 (little endian system)
// Here strcat is executed
After strcat
a:
53 6f 6c 61 72 69 73 4c // Hex dump of a gives SolarisL
ptr:
69 6e 75 78 00 00 00 00 // Ups.. ptr has changed! It's not a valid pointer value anymore
// As a string it is inux\0
SolarisLinux // print a
Segmentation fault // print ptr crashes because ptr doesn't hold a valid pointer value
所以在我的系统上的解释是:
a
位于内存中 ptr
之前,所以当 strcat
写出 a
的边界时,它实际上覆盖 ptr
的值。因此,当尝试将 ptr
用作有效指针时,程序会崩溃。
所以对于你的具体问题:
1)Does strcat removes the string literal from the original location, as accessing ptr gives a segmentation fault.
没有。被覆盖的是 ptr
的值。 sring 文字很可能未被触及
2)Why does p a in gdb doesn't give the proper o/p where as p a+0 shows "SolarisLinux".
这是一个猜测 - 仅此而已。我的猜测是 gdb 知道 a
是 8 个字节,所以直接打印 a
只会打印 8 个字节。当打印 a + 0
我的猜测是 gdb 将 a + 0
视为指针(因此无法知道对象大小)所以 gdb 一直打印直到它看到 a零终止。
关于c - 带有指向字符串文字的 char 指针的 strcat,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51746106/