最简单的字符串数组c程序上的分段错误(核心转储)

Posted

技术标签:

【中文标题】最简单的字符串数组c程序上的分段错误(核心转储)【英文标题】:Segmentation fault(core dumped) on simplest string array c program 【发布时间】:2021-06-06 12:37:45 【问题描述】:

我在尝试使用 c 中的字符串时遇到问题。我有这个代码:

#include <stdio.h>
#include <string.h>

int main()

    char *result[2];
    strcpy(result[0], "String 1");
    strcpy(result[1], "String 2");

    printf("%s\n", result[0]);
    printf("%s\n", result[1]);

它编译时没有警告但它不运行。它说分段错误,执行时核心转储

如何在 C 中为字符串数组赋值?

P.D.在此示例中,我尝试分配文字字符串以制作最简单的可重现代码,我知道我可以使用 "String 1", "String 2" 或类似的方式直接分配文字字符串,但实际上我需要将一个变量分配给 result[0] 并将另一个变量分配给result[1]

【问题讨论】:

result[0] 是一个指针,但是它指向哪里?除非你让它指向某个有效的地方,否则你不能将它用作复制数据的目的地。 这两个程序的行为似乎不同,即使它们都有相同的错误,是因为undefined behavior 长度为 1 的数组称为变量。第一个代码“有效”真的是完全侥幸。 @Someprogrammerdude。如何让它指向某个地方? 为什么不创建一个 arrays 数组呢?如char result[2][30];? 【参考方案1】:

char *result[2] 是一个由 2 个指针组成的数组,它们是未初始化的,如果你想为它们分配字符串文字,你不能使用 strcpy,该函数将字符串复制到这些指针指向的内存位置,但是因为它们是未初始化的,所以它们不指向任何内存位置。这种构造的行为是未定义的。

如果这些只是为了打印,你可以使用赋值运算符:

const char *result[2];
result[0] = "String 1";
result[1] = "String 2";

或者

const char *result[2] = "String 1", "String 2";

你会注意到我使用了const,那是因为那些字符串是只读的,你不能改变它们。


如果你想改变它们,你需要初始化指针,要么分配内存,要么让它们指向某个有效的内存位置:

#include <stdlib.h>

char *result[2];

result[0] = malloc(/*length of the string + null byte*/);
result[1] = malloc(/*length of the string + null byte*/);

或者

char str1[/*length of the string + null byte*/];
char str2[/*length of the string + null byte*/];

char *result[2] = str1, str2;

strcpy(result[0], "String 1");
strcpy(result[1], "String 2");

有了这个,因为您复制了字符串,您现在可以更改它们,它们不再是只读的。


脚注:

事实上,默认情况下没有警告,但在编译器中启用额外警告会警告你,例如使用 gcc,使用-Wall 标志会产生以下警告:

main.c:10:5: 警告:'result[0]' 在此函数中未初始化 [-Wuninitialized] 10 | printf("%s\n", result[0]);

您应该始终使用 char 数组, char result[2][10] 如果有机会,则在不需要时使用内存分配对您的程序来说是不必要的开销。查看这些线程以获取更多信息:

When and why to use malloc?

When do I need dynamic memory?

【讨论】:

【参考方案2】:

char *result[1];

C 不会自动分配空间来存储字符串的内容——你必须自己做。在这种情况下,您只分配了足够的空间来存储指针值(即地址)1。由于它是在没有初始化器的情况下声明的,因此该指针值是indeterminate - 它可以是0,它可以是0xdeadbeef,它可以是任何其他值。在这种情况下,不确定的指针值恰好指向可写的内存,因此操作成功。

但是……

由于它不是在对象的生命周期内通过对该对象使用&amp; 运算符或通过malloccallocrealloc 调用获得的,因此该指针值无效 并且尝试通过无效指针写入的行为未定义。不幸的是,未定义行为的症状之一是按预期工作 - 只要您不破坏任何“重要”的东西,您的代码就会正常运行。

char *result[2];

与上面的处理相同,尽管这次不确定指针值中的一个或两个指向不可写的内存,因此出现运行时错误。

字符串(包括字符串文字)最终存储在字符类型的数组中,因此您必须分配足够长的数组来存储整个字符串和终止符。 "String 1" 有 8 个字符长,因此您需要分配一个 至少 9 个字符宽的数组来存储字符串和终止符:

char result[9];
strcpy( result, "String 1" );

或者如果你的实现支持可变长度数组2,你可以这样做:

size_t len = strlen( "String 1" );
char result[len + 1];
strcpy( result, "String 1" );

或者如果你想动态分配内存:

size_t len = strlen( "String 1" );
char *result = malloc( len + 1 );
if ( result )
  strcpy( result, "String 1" );

如果你想要一个字符串数组,你必须使用 char 的二维数组:

char result[2][9];
strcpy( result[0], "String 1" );
strcpy( result[1], "String 2" );

或指向其他数组或动态内存的char的指针数组:

char *result[2];

result[0] = malloc( strlen( "String 1" ) + 1 );
result[1] = malloc( strlen( "String 2" ) + 1 );

if ( result[0] )
  strcpy( result[0], "String 1" );

if ( result[1] )
  strcpy( result[1], "String 2" );

    数组不是指针,指针也不是数组。数组 expressions 根据需要“衰减”为指针表达式,但它们最终是两种不同的动物。 尽管有名称,但可变长度数组的大小是不可调整的 - 它们的大小在其生命周期内是固定的。 “变量”是指它们的大小可以随定义而变化。

【讨论】:

非常感谢约翰花时间写下如此详细的答案。现在,根据答案和 cmets,我可以让我的演示代码正常工作。但是现在我有新的问题,因为我尝试在我的真实代码中实现你向我解释的内容它仍然不起作用。如果标题和主题仍然适用,我必须创建一个新问题还是编辑这个问题? @AlejoDev:如果是新问题,请创建一个新问题。

以上是关于最简单的字符串数组c程序上的分段错误(核心转储)的主要内容,如果未能解决你的问题,请参考以下文章

什么原因导致C中出现分段错误(核心转储)?

尝试声明大数组时出现分段错误和核心转储[重复]

c中的分段错误(核心转储)

为啥我在 C 中收到警告“分段错误,核心转储”

使用动态二维数组时的 C++ 分段错误(核心转储)

RPC:分段错误(核心转储)