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.
中没有写的每个函数的文档(如calloc
、fgets
等...)
下一步应该是循环填充 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 *
类型的指针数组,并为每个指针分配一个已分配字符数组的地址。
此外,通常您应该检查 calloc
或 malloc
.
调用的 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
我正在尝试为字符串数组动态分配内存,但遇到了分段错误。如果你能告诉我一些方法,那将非常有帮助。
据我所知,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.
中没有写的每个函数的文档(如calloc
、fgets
等...)
下一步应该是循环填充 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 *
类型的指针数组,并为每个指针分配一个已分配字符数组的地址。
此外,通常您应该检查 calloc
或 malloc
.
您的代码还有另一个问题。 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