多维指针阵列导致分段故障的分配

时间:2015-04-15 03:22:34

标签: c pointers multidimensional-array segmentation-fault c-strings

首先,我会解释为什么我这样做是这样的。我正在上一门计算机编程课程,我的教授给了我们一个作业,我们必须制作一系列记录(每个记录包含名字,姓氏和分数),然后允许用户操作记录使用菜单选项。所有这些必须仅使用指针数组来完成,并且不允许使用结构。我知道这是一件令人头痛的事。我知道这可能是实现这一目标的最困难的方法之一,但它是教授想要的。

有了这个,下面就是我到目前为止我的主要功能。大多数长printf函数只是我打印调试信息。请注意char ***变量的声明。它用作3D数组,其中nameRecords[0]将是第一个记录,nameRecords[0][0]将是第一个记录的第一个名称,nameRecords[0][1]是第一个记录的姓氏。第三个维度是nameRecords[0][0][21],因为字符串只需要20个字符加上空字符。

int main(void)
{
    char ***nameRecords = NULL;
    float *scores = NULL;
    int size = 0; // total number of records
    int usrInt = 0;
    while(usrInt < 1)
    {
        printf("\nEnter the number of records to record(min 1):  ");
        scanf("%d", &usrInt);
        inpurge();

        if(usrInt < 1) printf("\nMust be integer greater than 1.\n");
    }

    nameRecords = (char***)calloc((size), sizeof(char**));
    scores = (float*)calloc(size, sizeof(float));

    int i;
    for(i = 0; i < usrInt; i++)
    {
        addRecord(&nameRecords, &scores, &size);
        printf("\nnameRecords@%p :: nameRecords[%d]@%p :: nameRecords[%d][0]=%s :: nameRecords[%d][1]=%s\n", nameRecords, size - 1, nameRecords[size - 1], size - 1, nameRecords[size - 1][0], size - 1, nameRecords[size - 1][1]);
    }

    printf("\nnameRecords[0]@%p\n", nameRecords[0]);

    prntRecords(nameRecords, scores, size);

    printf("\n\n\n");
    return 0;
}

在我将 SECOND TIME &nameRecords传递到下面定义的addRecord函数后,麻烦来了。为了澄清,如果用户选择在主函数的开头只输入1个条目,并且程序实际运行并按预期终止,则不会收到分段错误。

void addRecord(char ****records, float **scores, int *size)
{   
    printf("\t(*records)[0]%p\n", (*records)[0]);
    ++*size; // increment total number of records by 1
    int index = (*size) - 1;

    char ***tempNames = (char***)realloc(*records, (*size) * sizeof(char**)); // reallocate larger space.
    if(tempNames != *records)
        *records = tempNames; // set original pointer to new value.

    printf("\n\tsize - 1 = %d\n", index);

    float *tempScores = (float*)realloc(*scores, (*size) * sizeof(float)); // reallocate larger space.
    if(tempScores != *scores)
        *scores = tempScores; // set original pointer to new value.

    printf("\ttempNames[0]@%p\n", tempNames[0]);

    tempNames[index] = (char**)calloc(tempNames[index], 2 * sizeof(char*));
    enterRecord(tempNames[index], scores[index]);

    printf("\n\ttempNames@%p :: tempNames[0]@%p :: tempNames[%d][0]=%s :: tempNames[%d][1]=%s\n", tempNames, tempNames[0], index, tempNames[index][0], index, tempNames[index][1]);
    printf("\n\t*records@%p :: *records[0]@%p :: *records[%d][0]=%s :: *records[%d][1]=%s\n", *records, (*records)[0], index, (*records)[index][0], index, (*records)[index][1]);

    return;
}

以下是该程序的示例输出。没有花太多时间来解释发生的事情,标签线是addRecord函数内的输出线。具体来说,指向第一条记录record[0]的指针在addRecord函数的第二次传递中变为垃圾值,就在enterRecord函数之后。

Enter the number of records to record(min 5):  2
        (*records)[0](nil)

        size - 1 = 0
        tempNames[0]@(nil)

Enter first name: 1

Enter last name: 1

Enter score: 1

COMPLETE enterRecord

        tempNames@0x6387010 :: tempNames[0]@0x6387050 :: tempNames[0][0]=1 :: tempNames[0][1]=1

        *records@0x6387010 :: *records[0]@0x6387050 :: *records[0][0]=1 :: *records[0][1]=1

nameRecords@0x6387010 :: nameRecords[0]@0x6387050 :: nameRecords[0][0]=1 :: nameRecords[0][1]=1
        (*records)[0]0x6387050

        size - 1 = 1
        tempNames[0]@0x6387050

Enter first name: 2

Enter last name: 2

Enter score: 2

COMPLETE enterRecord

        tempNames@0x6387010 :: tempNames[0]@0x40000000 :: tempNames[1][0]=2 :: tempNames[1][1]=2

        *records@0x6387010 :: *records[0]@0x40000000 :: *records[1][0]=2 :: *records[1][1]=2

nameRecords@0x6387010 :: nameRecords[1]@0x63870b0 :: nameRecords[1][0]=2 :: nameRecords[1][1]=2

nameRecords[0]@0x40000000

records@0x6387010 :: records[0]@0x40000000
Segmentation fault

所有调试信息都指向enterRecord函数作为罪魁祸首。所以这就是邪恶的enterRecord函数......

void enterRecord(char **names, float *score)
{
    names[0] = (char*)calloc(21, sizeof(char)); // allocate first name string
    names[1] = (char*)calloc(21, sizeof(char)); // allocate last name string

    printf("\nEnter first name: ");
    fgets(names[0], 21, stdin);
    if(strlen(names[0]) == 20) // IGNORE. just handles overflow from fgets.
        inpurge();
    remNewLine(names[0]); // removes '\n' character at end of string

    printf("\nEnter last name: ");
    fgets(names[1], 21, stdin);
    if(strlen(names[1]) == 20) // IGNORE. just handles overflow from fgets.
        inpurge();
    remNewLine(names[1]); // removes '\n' character at end of string

    printf("\nEnter score: ");
    scanf("%f", score);
    inpurge();

    printf("\nCOMPLETE enterRecord\n");
    return;
}

仅...没有尝试改变受影响的指针。记录数组(records[1])的第二个元素的指针值被传递到函数中,我看不到任何东西正在改变记录数组的第一个元素的指针值(records[0] ),虽然records[0]的值是导致段错误的原因。

对于篇幅和所有混淆代码,我感到非常抱歉。再次,这似乎是编写这个程序的一种可怕的方法,但它的情况需要它。我只是为这位可怜的老师的助手感到难过,他必须对这些作业中的30多个进行评分。

欢迎任何帮助。

2 个答案:

答案 0 :(得分:1)

这个问题似乎更好地实现为

#define MAX_FIRST_NAME_LEN (21)
#define MAX_LAST_NAME_LEN  (21)
#define MAX_SCORES         (10)

// in file global memory...
static char **ppFirstNames = NULL; 
static char **ppLastName   = NULL;
static int  **ppScores     = NULL;
static int    numOfEntries = 0;

// in the record input function, which needs NO parameters
scanf ( "%d", &numOfEntries );
if scanf fails, exit

ppFirstNames = malloc (numOfEntries*sizeof char*);
if malloc fails, exit

memset (ppFirstName, '\0', numOfEntries*sizeof char* );

ppLastName = malloc (numOfEntries*sizeof char*);
if malloc fails, free all, exit

memset (ppLastName, '\0', numOfEntries*sizeof char* );

ppScores = malloc (numOfEntries *sizeof int* );
if malloc fails, free all, exit

for(int i=0; i<numOfEntries; i++ )
    ppFirstNames[i] = malloc( MAX_FIRST_NAME_LEN );
    if malloc fails free all, exit

    memset ( ppFirstNames[i], '\0', MAX_FIRST_NAME_LEN );

    ppLastName[i] = malloc (MAX_LAST_NAME_LEN);
    if malloc fails free all, exit

    memset ( ppLastName[i], '\0', MAX_LAST_NAME_LEN );

    ppScores[i] = malloc (MAX_SCORES *sizeof int);-1
    if malloc fails, free all, exit

    memset (ppScores[i], '\0', MAX_SCORES *sizeof int );
end for

for ( int i=0; i < numOfEntries; i++ )
    now read each record
    scanf( "%(MAX_FIRST_NAME_LEN-1)s", ppFirstNames[i] );
    if scanf fails, free all, exit

    scanf( "%(MAX_LAST_NAME_LEN-1)s", ppLastNames[i] );
    if scanf fails, free all exit

    for( int j=0; j< MAX_SCORES; j++ )
        now read this students scores
        int tempScore;  
        scanf( "%d", tempScore );
        if scanf fails, free all, exit

        if -1 == tempScore ) break;

        ppScores[i][j] = tempScore;
    end for
end for

以上是输入记录的伪代码

并且应该足以使输入正确。

此后打印信息应该很容易。

答案 1 :(得分:0)

对多维数组使用realloc的示例:

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

int main()
{
    int size_initial = 10;
    int **ptr;
    int i, j;

    ptr = (int**) malloc(sizeof (int*) * size_initial);

    for (i = 0; i < size_initial; i++) {

    ptr[i] = (int*) malloc(sizeof (int) * 10);

    for (j = 0; j < 10; j++)
        ptr[i][j] = i+j;
}

/* realloc +10 */

ptr = (int**) realloc(ptr, sizeof (int*) * (size_initial * 2));

for (i = size_initial; i < size_initial * 2; i++) {

    ptr[i] = (int*) malloc(sizeof (int) * 10);

        for (j = 0; j < 10; j++) {
            ptr[i][j] = i+j;
    }
}

/* print values */
for (i = 0; i < 20; i++) {
    for (j = 0; j < 10; j++) {
        printf("ptr[%d][%d] = %d\n", i, j, ptr[i][j]);
    }
}
    return 0;
}