【问题标题】:C sprintf array char pointersC sprintf 数组字符指针
【发布时间】:2013-11-04 16:46:12
【问题描述】:

谁能告诉我我在这里做错了什么?为什么我的程序会出现段错误? 我正在尝试在string1string2 之间插入第三个字符串。

#include <stdio.h>

int main (void) 
{
char *string1 = "HELLO";
char *string2 = "WORLD";
char *stringX  = "++++";
char *string3;
printf ("%s,%s\n",string1,string2);
sprintf(string3,"%s%s%s",string1,stringX,string2);
printf ("NewVar: %s",string3);
}

为什么sprintf 不将结果值存储在string3 指向的内存地址?当我将string3 声明为普通数组时它可以工作,但当它是指向char 数组的指针时则不行。

我以为 string3 没有指向任何内存位置,但当我这样做时似乎确实如此 printf("%p",string3);

输出:

# ./concat
HELLO,WORLD,0x40042

【问题讨论】:

  • 只是作为一个提示:数据应该写在哪里?你认为指针指向哪里?
  • @glglgl - 我认为 string3 没有指向任何东西,但是当我执行 printf ("%p",string3) 时,它似乎确实指向了一个内存位置。
  • 但是对于一个未指定的人......
  • @user2953313:你在说什么内存地址? string3 没有指向任何有意义的内存地址。它的值是不确定的,技术上根本不是内存地址。它可能“似乎”指向“内存位置”,但实际上这只是一种错觉——未定义行为的结果。

标签: c arrays pointers


【解决方案1】:

想象一下,你有一堆现金要放在公文包里。你需要什么?您必须测量现金的大小才能知道要使用多大的公文包,并且您需要一个把手来方便地携带现金。

现金就是你的琴弦。公文包是内存空间。公文包手柄就是指针。

  1. 衡量您的现金:strlen(string1) + strlen(string2) + strlen(stringX)。 将此称为“总数”。
  2. 现在准备一个足够大的公文包:malloc(total+1)
  3. 然后给它一个句柄:string3

将所有这些拼凑在一起......

char *string3 = malloc(strlen(string1)+strlen(stringX)+strlen(string2)+1);
sprintf(string3, "%s%s%s", string1, stringX, string2);

那么第一次尝试有什么问题?你没有公文包。你有现金,有把手,但中间没有公文包。它似乎以一种随机的方式起作用,因为编译器给了你一个肮脏的垃圾箱来存放现金。有时垃圾箱有空间,有时没有。如果没有,我们称之为“分段错误”。

只要有数据,就必须为这些数据分配空间。编译器为常量字符串分配空间,例如"HELLO"。但是你必须为运行时构建的字符串分配空间。

【讨论】:

  • string3 不指向任何可写内存。 char* 的大小与此处无关 - sprintf 将尝试写入 string3 指向的位置,而不会尝试写入指针本身。
  • 是的,我试图通过类比来解释它失败了。会改正的。
  • 大家好,感谢您的解释。当我用 %p 打印 string3 时,为什么它指向一个内存位置?当它不应该按照上面的 cmets 时。我已经编辑了我的代码以突出显示这个。
  • @user2953313 C 不会将您的变量初始化为任何特定值。指向的地址不能保证,但它可能是之前写入该堆栈位置的任何内容。尝试取消引用未初始化的指针会导致未定义的行为。有时它会崩溃;其他时候它可能会起作用。
  • @user2953313 要进行初始化,请使用 calloc() 而不是 malloc。将其用于我的类比,这是旧公文包和脏公文包 (malloc) 或新的干净公文包 (calloc) 之间的区别。我从 calloc 开始,然后如果性能是一个问题而清洁度不是问题,我会切换到 malloc。
【解决方案2】:

sprintf 确实将值存储在那里。问题是指针 string3 有未初始化的值,所以你只是在覆盖随机内存。

您可以选择使用静态字符串缓冲区:

char string3[20];
snprintf(string3, sizeof(string3), "Hello!");

或者,您可以在基于 GNU libc 的系统上使用 asprintf 自动分配适当的空间:

char * string3;
asprintf(&string3, "Hello!");
// ... after use
free(string3); // free the allocated memory

【讨论】:

  • 您的 asprintf 示例不正确 - string3 应该是 char * 而不是 char。可能还值得补充的是 asprintf 不是标准的 C 函数(或者我认为甚至是 Posix)。
  • @NigelHarper:谢谢,已修复。 asprintf 是一个 GNU 扩展,注意到这一点。
【解决方案3】:

sprintf 不为其写入的字符串分配内存。您必须提供一个有效的字符串才能写入,但当前正在向它传递一个未初始化的指针。

最简单的解决方法是改变

char *string3;
sprintf(string3,"%s%s%s",string1,stringX,string2);

char string3[200];
sprintf(string3,"%s%s%s",string1,stringX,string2);

在这种情况下,您可能希望通过使用 snprintf 来防止缓冲区溢出

char string3[200];
snprintf(string3,sizeof(string3),"%s%s%s",string1,stringX,string2);

或者,您还可以通过在运行时确定string3 的大小来处理更大长度的源字符串,完成后注意free 这个内存。

char* string3 = malloc(strlen(string1) + strlen(stringX) + strlen(string2) + 1);
if (string3 == NULL) {
    // handle out of memory
}
sprintf(string3,"%s%s%s",string1,stringX,string2);
...
free(string3);

【讨论】:

  • ,感谢使用 malloc 的回答。空字符是 +1 吗?
  • @user2953313 很高兴它有帮助。是的,+1 用于 nul 终止符
【解决方案4】:

如果您需要在堆上使用malloc,则需要为string3 分配空间,如果不需要,则将其声明为字符数组。

【讨论】:

    【解决方案5】:

    假设您将i 定义为int i;在这个级别中,您告诉我将存储integer number,但i 变量中仍然没有有意义的数字。 像这样当你定义char *string3 时你告诉string3 将存储字符指针但仍然没有有意义的地址。所以你必须为这个变量分配内存

    string3 =  malloc(strlen(string1)+strlen(stringX)+strlen(string2)+1);
    

    【讨论】:

      猜你喜欢
      • 2015-08-12
      • 2021-06-28
      • 1970-01-01
      • 2018-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-09
      • 1970-01-01
      相关资源
      最近更新 更多