首页 > 解决方案 > 带有指向字符串文字的 char 指针的 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. 为什么p a在 gdb 中没有给出正确的输出,如图p a+0所示"SolarisLinux"

标签: cstring-literalsstrcat

解决方案


如果我理解您的问题是正确的,您就会知道该程序由于a无法保存与“Linux”连接的字符串“Solaris”而具有未定义的行为。

因此,您要寻找的答案不是“这是未定义的行为”,而是:

为什么会这样

在处理未定义的行为时,我们无法对正在发生的事情给出一般解释。它可能在不同的系统上做不同的事情,或者为不同的编译器(或编译器版本)做不同的事情等等。

因此,人们常说,试图解释具有未定义行为的程序中发生的事情是没有意义的。嗯 - 这是正确的。

但是 - 有时您可以找到针对您的特定系统的解释 - 请记住它是特定于您的系统的,绝不是通用的。

所以我更改了您的代码以添加一些调试打印:

#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) strcat 是否从原始位置删除字符串文字,因为访问 ptr 会产生分段错误。

不,它的值ptr已被覆盖。字符串字面量很可能未被触及

2)为什么 gdb 中的 pa 没有给出正确的 o/p,因为 p a+0 显示“SolarisLinux”。

这是一个猜测——仅此而已。我的猜测是 gdb 知道那a是 8 个字节,所以a直接打印只打印 8 个字节。打印a + 0时我的猜测是 gdb 看起来a + 0像一个指针(因此无法知道对象大小),因此 gdb 会继续打印,直到它看到零终止。


推荐阅读