char 的动态内存分配**

Dynamic memory allocation for char**

我正在尝试为字符串数组动态分配内存,但遇到了分段错误。如果你能告诉我一些方法,那将非常有帮助。

据我所知,char* 是一个字符串,char** 是一个二维字符串数组。 如果 s[i] 是一个字符串,那么 *(s + i) 不应该也是一样的吗?我对这个话题感到困惑。

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

int n;

int main(void)
{
    scanf("%i", &n);  //number of strings being inputted

    char **s = calloc(n, 10 * (sizeof(char) + 1));

    for (int i = 0; i < n; i++) //get strings
    {
        fgets(*(s + i), 10 * (sizeof(char) + 1), stdin);
    }

    for (int i = 0; i < n; i++) //print the strings
    {
        fputs(*(s + i), stdout);
    }

    return 0;
}
scanf("%i", &n);  //number of strings being inputted

不完整,因为 scanf(3) 可能会失败(例如,如果您的用户输入 hello)。您需要添加更多代码来检查 scanf 是否成功。我建议:

if (scanf("%i", &n) < 0) {
  perror("number of strings expected");
  exit(EXIT_FAILURE);
}

然后

char **s = calloc(n, 10 * (sizeof(char) + 1));

错了。数组的每个元素都是一个 char*,在大多数机器上有 4 或 8 个字节(sizeof(void*))。

所以将其替换为

char **s = calloc(n, sizeof(char*));

或等效的 char**s = calloc(n, sizeof(*s));,在我看来可读性较差。

此外,calloc 可能会失败。您需要添加一个测试,例如

if (s == NULL) {
   perror("calloc of s");
   exit(EXIT_FAILURE);
}

仔细阅读你在this C reference.

中没有写的每个函数的文档(如callocfgets等...)

下一步应该是循环填充 s 的每个元素。使用另一个 calloc,或者调用 strdup(3)(如果您的系统提供)。当然,calloc(或strdup)也可能会失败,您需要针对失败进行测试。

如果您的系统有,您也可以使用 getline(3) (or even readline(3))。与您的经理核实您是否在法律上和技术上允许使用它们。

如果您的编译器GCC, invoke it with all warnings and debug info: gcc -Wall -Wextra -g. Once you have no warnings, use a debugger like GDB了解您的可执行文件的行为。

也考虑使用 Clang static analyzer(在获得使用许可后)。

在板上(或纸上)画一个带箭头的图形(如维基百科上的 doubly linked-list 图形)以了解程序的行为。

在此声明中

char **s = calloc(n, 10 * (sizeof(char) + 1));

您分配了一个内存,其地址分配给了指针s。由于调用函数calloc.

,内存被零初始化

所以在这个声明中

fgets(*(s + i), 10 * (sizeof(char) + 1), stdin);

指针 s 被取消引用并且要么是空指针(因为指向的内存是零初始化的),要么如果表达式 s + i 指向分配的内存之外则具有不确定的值。

您需要分配一个 char * 类型的指针数组,并为每个指针分配一个已分配字符数组的地址。

此外,通常您应该检查 callocmalloc.

调用的 return 值

您的代码还有另一个问题。 scanf

调用后
scanf("%i", &n);  //number of strings being inputted

输入缓冲区包含新行字符 '\n',将由以下 fgets 调用读取。所以 fgets 的第一次调用实际上会读取一个空字符串。

另一个问题是你应该删除可以通过fgets附加到读取字符串的换行符。例如,一些读取的字符串可以包含新的 .line 字符,而其他的则可以不包含它,具体取决于用户键入的字符数。

如果你想写程序而不用带指针的下标运算符,那么可以看下面的例子。

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

int main(void) 
{
    size_t n = 0;
    
    if ( scanf( "%zu", &n ) == 1 )
    {
        scanf( "%*[^\n]" );
        scanf( "%*c" );
    }

    char **s = NULL; 
    
    if ( n != 0 ) s = calloc( n, sizeof( char * ) );
    
    if ( s != NULL )
    {
        size_t len = 11;
        
        size_t m = 0;
        
        while ( m < n && ( *( s + m ) = malloc( len  * sizeof( char ) ) ) != NULL ) m++;
        
        size_t i = 0;
        
        while ( i < m && fgets( *( s + i ), len, stdin ) != NULL ) i++;

        m = i;
        
        for ( i = 0; i < m; i++ ) //print the strings
        {
            ( *( s + i ) )[ strcspn( *( s + i ), "\n" )] = '[=13=]';
            // or without the subscript operator
            // *( *( s + i ) + strcspn( *( s + i ), "\n" ) ) = '[=13=]';

            puts( *( s + i ) );
        }
        
        for ( i = 0; i < n; i++ ) free( *( s + i ) );
    }
    
    free( s );
    
    return 0;
}

程序输出可能看起来像

10
one
two
three
four
five
six
seven
eight
nine
ten
one
two
three
four
five
six
seven
eight
nine
ten