2011-02-12 162 views
3

我对C相当陌生,在引用字符串时遇到了数组和指针。我可以要求输入2个数字(整数),然后返回我想要的数字(第一个数字或第二个数字),没有任何问题。但是,当我请求名称并尝试返回它们时,程序在输入名字后崩溃,但不知道为什么。指针和malloc问题

从理论上讲,我希望为第一个名字保留内存,然后将其扩展为包含第二个名称。任何人都可以解释为什么这打破

谢谢!

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



void main() 
{ 
    int NumItems = 0; 

    NumItems += 1; 
    char* NameList = malloc(sizeof(char[10])*NumItems); 
    printf("Please enter name #1: \n"); 
    scanf("%9s", NameList[0]); 
    fpurge(stdin); 

    NumItems += 1; 
    NameList = realloc(NameList,sizeof(char[10])*NumItems); 
    printf("Please enter name #2: \n"); 
    scanf("%9s", NameList[1]); 
    fpurge(stdin); 

    printf("The first name is: %s",NameList[0]); 
    printf("The second name is: %s",NameList[1]); 

    return 0; 

} 
+0

你不能有一个`void`函数返回0;但是`main()`应该总是首先声明返回`int`。 – 2011-02-12 22:54:15

+0

谢谢乔纳森。我做了这个改变。 – Andy 2011-02-12 23:09:56

回答

6

我觉得你的问题是在此代码:

scanf("%9s", NameList[0]); 

这里的问题是,scanf要求变量您提供为存储结果必然是一个位置一个指针。如果你提供的东西不是指针,scanf会将它看作是一个随机位置,并将内存写入随机位置,导致程序崩溃。

解决这个问题需要两个步骤。首先,您需要更改您的NameList声明,以便它不再是char *。原因是一个char *是一个单个字符串,而您想要一个数组字符串。这将被定义为char **,指向数组char *的指针。这可能看起来像这样:

char** NameList; 

接下来,您需要为字符串分配存储空间。这是棘手的,有点微妙,因为你必须做两个分配。首先,你需要为阵列本身,你可以做这样的分配空间:

NameList = malloc (sizeof(char*) * NumItems); 

该分配指针字符数组,但它实际上并没有建立在指针数组的那些点到有效的内存位置。为了解决这个问题,你会想然后在查看此数组并设置所有元素是指向大到足以容纳你的字符串缓冲区 - 在这种情况下,长度为10的缓冲区:

int i; 
for (i = 0; i < NumItems; ++i) 
    NameList[i] = malloc (10); // Space for 10 characters 

现在,您可以拨打

scanf("%9s", NameList[0]); 

因为NameList[0]char *指向到其中的角色应该被写入缓冲区。

对您的代码有更多评论 - 而不是分配一个元素的数组,然后将它重新分配给两个元素的数组,然后考虑只是预先分配所有空间。它更清晰一点。另外,由于您现在正在处理一个char *的缓冲区,每个缓冲区都需要初始化以指向其自己的缓冲区,如果您执行增量分配,则需要确保初始化所有新的char *分配指向某处的缓冲区。如果你一次只做这一步,那么你很可能会忘记设置指针并导致崩溃,而如果你事先做好了,就没有这种风险。

当需要释放动态分配的内存时,您需要反向运行分配过程,首先释放动态分配的字符串缓冲区,然后释放顶层缓冲区。例如:

for (i = 0; i < NumItems; ++i) 
    free (NameList[i]); 
free (NameList); 

这是必要的,因为free功能不能递归调用。您需要显式释放所有分配的内存。

注意,你写这样的代码:

free (NameList); 
for (i = 0; i < NumItems; ++i) 
    free (NameList[i]); 

,因为如果你先释放顶层数组这将导致各种坏事在运行时,那么当你试图迭代在释放指针的内容上,你将会阅读你不再拥有的内存。

希望这会有所帮助!

1

您的NameList变量是一个char *,它是一个指向单个字符串的指针。 (char是单个字符,字符*是一个字符串,字符**是一个字符串数组。)

当使用NameList中[1]时,实际上是索引字符串的第二个字符,不第二个字符串本身。

而应该分配的字符串数组,这样的事情:

char (*NameList)[10]; 
    NameList = malloc(10*sizeof(char)*NumItems); 

编辑:修正了一些编译错误。 (Sample code.)请注意,sizeof(char)并不是真的需要,因为它总是1.很高兴明确,但。

+0

谢谢,我会给它一个镜头。我做的一个快速编辑是将&运算符添加到我的scanf中,它实际上防止它给我一个错误,但正如你所说,输出包括名字的第一个字母,然后是全名。所以当我的意图直接放在后面时,第二个scanf覆盖了第一个scanf。 – Andy 2011-02-12 22:48:38

+1

@Eric Pi-我可能是错的,但这不是非法的代码? `NameList`是一个数组,你不能为数组赋值。 – templatetypedef 2011-02-12 22:51:59

1

如果您需要一个2d字符数组(每个元素都是一个char数组)的数组,则您以错误的方式分配内存。正确的方法是:

int main(){ 

int i; 
int NumItems = 2; 

/* Alocate a variable which every position points to an array of character */ 
char ** NameList = (char **) malloc(sizeof(char *) * NumItems); 

/* For each position, allocate an array of 10 characters */ 
for(i = 0; i < NumItems; i++){ 
    NameList[i] = (char *) malloc(sizeof(char) * 10); 
} 

printf("Please enter name #1: \n"); 
scanf("%s", NameList[0]); 

printf("Please enter name #2: \n"); 
scanf("%s", NameList[1]); 

printf("The first name is: %s",NameList[0]); 
printf("The second name is: %s",NameList[1]); 

/* Free allocated memory. Always a good practice and prevents memory leaks. */ 
for(i = 0; i < NumItems; i++){ 
    free(NameList[i]); 
} 
free(NameList); 

return 0; 

} 
0

也可以考虑只用栈空间,而不是使用malloc和动态分配的堆空间:

#define NumItems 2 
char NameList[NumItems][10]; 

printf("Please enter name #1: \n"); 
scanf("%9s", NameList[0]); 

printf("Please enter name #2: \n"); 
scanf("%9s", NameList[1]); 

printf("The first name is: %s",NameList[0]); 
printf("The second name is: %s",NameList[0]); 

一般来说,除非你有一个需要动态大小的数组,这是非常非常容易使用普通的数组。