Ошибка выполнения при печати массива символов с двойным указателем

Я работаю над созданием системы для ввода имен учеников и оценок в массивы и вывода той же информации на экран, к сожалению, я продолжаю получать странный вывод.

Я прошел через свою программу с помощью отладчика, и он показал, что все работает гладко, пока я не доберусь до функции, которая печатает информацию о студенте, там массивы символов с двойным указателем испортили значения.

Вот изображение того, что я вижу, когда запускаю программу. (http://s28.postimg.org/nv29feawt/Error.png)

Примечание. Хотя я знаю, что есть лучшие и более простые способы сделать это, я должен выполнить это задание, используя динамически выделяемую память и массивы.

int main(void)
{
    char **firstNames;
    char **lastNames;
    float *scores;

    int recordsLength;
    printf("Please indicate the number of records you want to enter: ");
    scanf("%d", &recordsLength);
    printf("\n\n");

    firstNames = (char **)malloc(recordsLength * sizeof(char *));
    lastNames = (char **)malloc(recordsLength * sizeof(char *));
    scores = (float *)malloc(recordsLength * sizeof(float));

    int i = 0;
    while(i < recordsLength)
    {
        createNewEntry(i, firstNames, lastNames, scores);
        i++;
    }

    printEntry(0, firstNames, lastNames, scores);
    free(firstNames);
    free(lastNames);
    free(scores);
    return 0;
}

void clearScreen()
{
#ifdef _WIN32 
    system("cls");
#elif _unix_ 
    system("clear");
#endif
}

void printEntry(int entryID, char *firstNames[], char *lastNames[], float scores[])
{
    clearScreen();
    printf("|-------------------------------------------------------------------------|\n");
    printf("|                           Student Entry                                 |\n");
    printf("|-------------------------------------------------------------------------|\n|\n");
    printf("|   First Name: %s   Last Name: %s   Score: %.1f\n|\n|\n|\n", firstNames[entryID], lastNames[entryID], scores[entryID]);
    printf("|-------------------------------------------------------------------------|\n");
    printf("|                                                                         |\n");
    printf("|-------------------------------------------------------------------------|\n\n");
}

void createNewEntry(int index, char *firstNames[], char *lastNames[], float scores[])
{
    printf("Please input the records of the new student.\n\n\n");
    char first[20];
    char last[20];
    float score = 100.0f;

    printf("Please enter the student's first name: ");
    scanf("%s", &first);
    printf("\n\n");

    printf("Please enter the student's last name: ");
    scanf("%s", &last);
    printf("\n\n");

    printf("Please enter the student's score: ");
    scanf("%f", &score);
    printf("\n\n");

    firstNames[index] = (char *)malloc((strlen(first)) * sizeof(char));
    firstNames[index] = first;

    lastNames[index] = (char *)malloc((strlen(last)) * sizeof(char));
    lastNames[index] = last;
    printf("first name: %s", firstNames[index]);
    printf("last name: %s", lastNames[index]);
    scores[index] = score;
}

person isbaran    schedule 14.11.2014    source источник
comment
Никогда не выполняйте возвраты от malloc realloc, calloc   -  person Ivan Ivanovich    schedule 14.11.2014
comment
firstNames[index] = first; и lastNames[index] = last; выглядят подозрительно. Вы копируете указатель, а не содержимое массива.   -  person bwinata    schedule 14.11.2014
comment
Надеюсь, вы включили stdio.h в свое первоначальное задание. Кроме того, было бы лучше, если бы вы сократили свой код и опубликовали более короткую версию кода (минимальный код, демонстрирующий вашу проблему. Например, версию только с именами и без фамилий.   -  person Mohit Jain    schedule 14.11.2014
comment
сначала вам нужно strcpy из char[20]; на firstNames[index] и так далее, потому что, когда вы вернетесь из createNewEntry(), ваш "firstNames[index]" станет недействительным   -  person Ivan Ivanovich    schedule 14.11.2014
comment
когда вы делаете это: lastNames[index] = (char *)malloc((strlen(last)) * sizeof(char)); lastNames[index] = last; вы просто удаляете свой указатель, который находился в предыдущей строке   -  person Ivan Ivanovich    schedule 14.11.2014
comment
Спасибо, Иван Иванович и Мохит Джейн! Теперь это работает как шарм! Могу ли я спросить, почему я никогда не должен приводить возвращаемые значения от malloc, realloc и calloc?   -  person isbaran    schedule 14.11.2014
comment
Подробнее об этом можно прочитать здесь   -  person Mohit Jain    schedule 14.11.2014


Ответы (2)


firstNames[index] = (char *)malloc((strlen(first)) * sizeof(char));
firstNames[index] = first;  /* You are missing your allocated memory block and assigning local */

Верхние строки неверны. Вы не можете назначить c-strings оператором назначения =. Вы должны использовать strcpy для этого.

Вы назначаете локальный массив именам, которые не имеют жизни после завершения функции. Это вызывает неопределенное поведение. (Вы видите странные символы, но бывает и хуже).

Должно быть переписано как (аналогично и для фамилии)

firstNames[index] = malloc((strlen(first) + 1) * sizeof(char)); /* +1 for \0 */
if(firstNames[index] == NULL) {
  /* Malloc failed, error handling */
  ...
}
/* else */
strcpy(firstNames[index], first);  /* Use strcpy to copy contents */

Живой пример здесь

Прежде чем освобождать firstNames и lastNames, вы должны освободить всех членов firstNames и lastNames в цикле.

person Mohit Jain    schedule 14.11.2014
comment
Или вы можете использовать firstNames[index] = strdup(first); — при необходимости напишите strdup(). Это функция POSIX, но не стандартная функция C. Кроме того, код должен проверять успешность malloc() перед использованием выделенной памяти. - person Jonathan Leffler; 21.03.2015

Я согласен с ответом Мохита Джейна, добавив, что вы даже можете использовать sprintf.

person Achyuta Aich    schedule 14.11.2014
comment
Хотя вы могли бы использовать sprintf() — или даже snprintf() — неясно, зачем вам это нужно. Вы также можете использовать memcpy() или memmove() вместо strcpy(), если вы тщательно фиксируете длину, найденную strlen() — это может быть лучше. - person Jonathan Leffler; 21.03.2015