c - 带有指向字符串文字的 char 指针的 strcat

标签 c string-literals strcat

只是想理解在最近的采访中询问的以下代码。

#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)**

我有几个问题:

  1. strcat 是否从原始位置删除字符串文字,因为访问 ptr 会出现段错误?

  2. 为什么 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/

相关文章:

c++ - 如何将双引号放入字符串文字中?

java - 字符串对象和字符串文字之间的区别

Config Handling in structural configuration 使用C语言

c - 我在输出文件中收到奇怪的符号而不是适当的字母

c - 创建双向链表时出现段错误

c++ - 为什么我不能在这个专门的函数模板中将字符串文字传递给 const char* const&

c - strcat 给我一个段错误

c - 尝试将 char[] 存储到 char* 时出现问题

c - 从c中的char []创建字符串返回错误的值

c - 使用 rand() 函数的程序