Указатель и проблема с malloc - PullRequest
       23

Указатель и проблема с malloc

3 голосов
/ 13 февраля 2011

Я довольно новичок в C и застреваю с массивами и указателями, когда они ссылаются на строки. Я могу попросить ввести 2 числа (целые числа), а затем вернуть нужное мне (первое число или второе число) без каких-либо проблем. Но когда я запрашиваю имена и пытаюсь их вернуть, программа вылетает после того, как я ввожу имя и не знаю почему.

Теоретически я хочу зарезервировать память для первого имени, а затем расширить ее, добавив второе имя. Кто-нибудь может объяснить, почему это ломается?

Спасибо!

#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;

}

Ответы [ 4 ]

6 голосов
/ 13 февраля 2011

Я думаю, что ваша проблема в этом коде:

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

Проблема здесь в том, что scanf требует, чтобы аргумент, который вы указываете в качестве местоположения для хранения результата, должен быть указателем.Если вы предоставляете что-то, что не является указателем, scanf будет обрабатывать его так, как если бы оно было, и, по сути, записывать память в случайное место, что приведет к сбою программы.

Исправление этого требует двух шагов.Во-первых, вы захотите изменить объявление NameList, чтобы оно больше не char *.Причина в том, что char * является одиночной строкой, тогда как вам нужен массив строк.Это будет определено как char **, указатель на массив char * s.Это может выглядеть так:

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 * s, каждый из которых необходимо инициализировать, чтобы он указывал на свой собственный буфер, если вы выполняете инкрементное распределение, вам необходимо обязательно инициализировать все новые char * s, которые вы выделяете, чтобы указать где-нибудь буфер.Если вы делаете это по одному шагу за раз, есть хороший шанс, что вы забудете установить указатели и вызвать сбой, тогда как если вы сделаете это заранее, такого риска нет.

Когда придет времяОсвободив динамически распределенную память, вам нужно будет выполнить процесс выделения в обратном порядке, сначала освободив динамически распределенные буферы для строк, а затем освободив буфер верхнего уровня.Например:

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

Это необходимо, потому что free не работает рекурсивно.Вам нужно явно освободить всю память, которую вы выделяете.

Обратите внимание, что вы не пишете код следующим образом:

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

Это вызовет все видыПлохие вещи во время выполнения, потому что если вы сначала освободите массив верхнего уровня, а затем попытаетесь перебрать его содержимое, освобождая указатели, вы будете читать память, которой вы больше не владеете.

Надеюсь, это поможет!

1 голос
/ 13 февраля 2011

Если вам нужен двумерный массив символов (массив, в котором каждый элемент является массивом символов), вы неправильно распределяете память.Правильный путь будет:

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;

}
1 голос
/ 13 февраля 2011

Ваша переменная NameList представляет собой символ *, который является указателем на одну строку.(char - это один символ, char * - это отдельная строка, char ** - это массив строк.)

Когда вы используете NameList [1], вы фактически индексируете второй символ строки, а не самой второй строки.

Вместо этого вам следует выделить массив строк, примерно так:

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

Редактировать: Исправлена ​​некоторая компиляцияошибки. (Пример кода.) Обратите внимание, что sizeof (char) на самом деле не нужен, так как он всегда 1. Хотя приятно быть явным.

0 голосов
/ 13 февраля 2011

Также рассмотрите возможность использования стекового пространства вместо 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]);

Как правило, если вам не нужны массивы динамического размера, гораздо проще использовать простые массивы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...