函数中的结构数组 realloc - 产生未定义的行为

Struct array realloc in function - produces undefined behaviour

我有一个包含 3 个字符指针(任意长度字符串)的结构,我用 malloc 创建了一个动态数组,因为它可以有任意数量的条目。 这是我的结构数据类型:

typedef struct student{
    char* name;
    char* phoneNumber;
    char* department;
}STUDENT;

我使用一个函数重新分配以在需要新条目时增加结构数组的大小,并使用另一个函数打印整个数组。

我遇到的问题是: 我能够在 addNumber 函数内打印新条目,但当我尝试在 addNumber 函数外打印时,情况并非总是如此。有时它有效,大多数时候我遇到分段错误:11 并且有一次在添加新条目时我收到“malloc:对象 0x7fc426c058f0 的 *** 错误:未分配正在重新分配的指针”

这是我的主要内容:

int nEntry = 0;
STUDENT* directory = malloc(nEntry * sizeof *directory);
if(directory == NULL){
    puts("Unable to allocate memory");
    exit(-1);
}
while(1){
    int choice = 0;
    scanf("%d", &choice);
    while(getchar() != '\n');
    switch(choice){
        case 1:
            printDirectory(directory, nEntry);
            break;
        case 2:
            addNumber(directory, &nEntry);
            break;
        default:
            printf("Unknown option!\n");
            break;
    }
}
return 0;

这是我的 addNumber 函数:

void addNumber(STUDENT* Array, int* nArray){
    *nArray += 1;
    int x = *nArray - 1;
    STUDENT* tempDirectory = realloc(Array, *nArray * sizeof *Array);
    if(tempDirectory == NULL){
        puts("Unable to allocate memory");
        exit(-1);
    }
    else{
        Array = tempDirectory;
        Array[x].name = (char*)malloc(sizeof(char*));
        Array[x].phoneNumber = (char*)malloc(sizeof(char*));
        Array[x].department = (char*)malloc(sizeof(char*));

        printf("Name: ");
        scanf("%[^\n]", Array[x].name);
        while(getchar() != '\n');

        printf("Number: ");
        scanf("%[^\n]", Array[x].phoneNumber);
        while(getchar() != '\n');

        printf("Department: ");
        scanf("%[^\n]", Array[x].department);
        while(getchar() != '\n');

        for(int i = 0; i < *nArray; i++){
        printf("%s\t%s\t(%s)\n", (Array + i)->name, (Array + i)->phoneNumber, (Array + i)->department);
        }
    }
}

这是我的打印功能:

void printDirectory(STUDENT* Array, int nArray){
    int i;
    for(i = 0; i < nArray; i++){
        printf("%s\t%s\t(%s)\n", (Array + i)->name, (Array + i)->phoneNumber, (Array + i)->department);
    }
}

如果我将条目硬编码到 main 中,打印函数工作正常,问题似乎是在 addNumber 函数中创建的内容没有正确传回?但是对于我通过引用传递的所有参数,我很困惑为什么我会得到一个未定义的行为。

正如评论中指出的那样,您的 malloc 大小不正确,因此您的代码还有其他问题,但要解决您对 realloc 的具体问题:

要存储 realloc 的 return 值以便可以在主函数中访问它,您需要传递指针的地址。

所以主要是:

addNumber(&directory, &nEntry); // Note the ampersand

并在 addNumber 中:

void addNumber(STUDENT** Array, int* nArray){ // Note the pointer to a pointer
...
STUDENT* tempDirectory = realloc(*Array, *nArray * sizeof *Array); // Note Array is dereferenced
...
*Array = tempDirectory;
...

如果您将 main 中的 directory 描绘为存储在位置 0x1 并指向内存位置 0x2 的变量,则在您当前的代码中,您将向该函数发送 0x2。该函数可以读取那里的数据,但它没有引用 0x1 来更新 main.

中的 directory 变量

通过引用传递的一般规则是:如果你想更新函数中的引用以便调用者可以读取它,你需要传递一个比调用者中的变量多一个星号的星号。因此,如果您在 main 中有一个 int,您可能希望在函数中更新一个 int*。如果您在 main 中有一个 int***,您可能想要更新一个 int****.

对于初学者来说,这个代码片段

int nEntry = 0;
STUDENT* directory = malloc(nEntry * sizeof *directory);
if(directory == NULL){
    puts("Unable to allocate memory");
    exit(-1);
}

没有意义。参数等于 0 的 malloc 调用的行为是实现定义的。也就是说,这样的调用可以 return NULL 或某个有效指针,具体取决于所使用的系统。

写就够了

int nEntry = 0;
STUDENT* directory = NULL;

函数addNumber处理传递参数值的副本。因此在函数内更改副本不会影响原始参数。

您必须通过引用传递原始参数。即通过指针指向指针。

void addNumber( STUDENT **Array, int *nArray );
                        ^^^^^^^

此外,ArraynArray 等类似的参数名称使函数代码不可读。

函数可以通过以下方式声明

int addNumber( STUDENT **directory, int *n )
{
    STUDENT *tempDirectory = realloc( *directory, ( *n + 1 ) * sizeof **directory );
    if ( !tempDirectory ) return 0;

    ++*n;
    *directory = tempDirectory;

    // and so on

    return 1;
}

并且调用者可以检查函数的 return 值是否以成功或失败结束,并在需要时发出相应的消息。

这些内存分配

    Array[x].name = (char*)malloc(sizeof(char*));
    Array[x].phoneNumber = (char*)malloc(sizeof(char*));
    Array[x].department = (char*)malloc(sizeof(char*));

也说不通。你需要分配字符数组,你将在这样的调用中存储输入的字符串

scanf("%[^\n]", Array[x].name);

您可以声明一个辅助字符数组,例如

char s[100];

并读取此数组中的字符串。例如

    printf("Name: ");
    fgets( s, sizeof( s ), stdin );
    s[ strcspn( s, "\n" ) ] = '[=17=]'; 

然后你可以为数据成员分配内存 name 知道读取字符串的长度

( *Array )[x].name = malloc( strlen( s ) + 1 );

并复制字符串

strcpy( ( *Array )[x].name, s );