2017-01-25 3 views
0

У меня есть функция, которая возвращает указатель несколько разыменования, как результат, как так:Проверка NULL Указатель В C не работает

typedef struct { 
    int id; 
    char *name; 
} user; 

user **myfn(int users_count) { 
    user **a; 
    a = malloc(sizeof(user) * user_counts); 
    for(int i = 0 ; i<user_counts ; i++) { 
     *(a+i) = malloc(sizeof(user)); 
     (*(a+i))->id = 1; 
     (*(a+i))->name = malloc(sizeof(char) * 25); 
     strncpy((*(a+i))->name, "Morteza", 25); // just for example 
    } 
    return a; 
} 

Теперь я, когда я хочу, чтобы ползти в этом результате в основной функции, его покажет имя всех пользователей, но в конце он столкнется с ошибкой Segmentation fault.

int main() { 
    user **a = myfn(10); 
    int i = 0; 
    while((*(a+i)) != NULL) { 
     printf("ID: %d \t %s\n", (*(a+i))->id, (*(a+i))->name); 
     i++; 
    } 
} 

Результаты:

ID: 1 Morteza 
ID: 2 Morteza 
... 
... 
ID: 10 Morteza 
Segmentation fault (core dumped) 

Почему condition из while не работает нормально?

+2

'a = malloc (sizeof (user) * user_counts);' неверно. '* a' является' user * ', а не' user'. – melpomene

+1

Не используйте 'strncpy'. Когда-либо. – melpomene

+2

'* (a + i)' обычно записывается 'a [i]'. – melpomene

ответ

2

Прежде всего,

a = malloc(sizeof(user) * user_counts); 

есть проблема - вы хотите выделить user_counts экземпляры указателей к user, не экземпляры user, так что линия должна быть

a = malloc(sizeof(user *) * user_counts); 

Однако , есть более простой способ - оператор sizeof может принимать выражения как аргументы а также имена типов. Таким образом, вы можете переписать эту строку как

a = malloc(sizeof *a * user_counts); 

экспрессии*a имеет тип user *, поэтому sizeof *a эквивалентно sizeof (user *). Это делает вашу жизнь немного проще, поскольку вам не нужно разгадывать тип, который указывает a - заставить компилятор выполнять тяжелую работу.

Вы должны всегда проверить результат звонка malloc.

a = malloc(sizeof *a * users_count); 
if (a) 
{ 
    // do stuff with a 
} 

Во-вторых, не следует использовать *(a+i) индексировать в a - использовать a[i] вместо этого. Делает вещи немного легче читать. Так,

*(a+i) = malloc(sizeof(user)); 
    (*(a+i))->id = 1; 
    (*(a+i))->name = malloc(sizeof(char) * 25); 

становится

a[i] = malloc(sizeof *a[i]); 
    if (a[i]) 
    { 
    a[i]->id = 1; 
    a[i]->name = malloc(sizeof *a[i]->name * 25); 
    } 
    else 
    { 
    /* deal with memory allocation failure */ 
    } 

Теперь к вашей конкретной проблемы.Ваш код сбой в одной из следующих причин:

  • начальная malloc из a не удалось, так что вы врезаться на первой линии, использующей a[i];
  • malloc одного из ваших a[i] не удалось, поэтому вы рушитесь на a[i]->id = 1; не
  • вы успешно распределяемой памяти для всех users_count элементов a - неa[i] является NULL, поэтому петли последнего элемента вашего массива и попытаться разыменования объекта сразу после его, что скорее всего не действительный указатель.

В дополнение к добавлению проверки после каждого malloc вызова, вы должны, вероятно, петля на основе users_count:

for (i = 0; i < users_count; i++) 
    printf("ID: %d \t %s\n", a[i]->id, a[i]->name); 

Или вам нужно выделить один дополнительный элемент для a и установить его на NULL:

a = malloc(sizeof *a * (users_count + 1)); 
if (a) 
{ 
    a[users_count] = NULL; 
    for (i = 0; i < users_count; i++) 
    ... 
} 

Обратите внимание, что calloc инициализирует всю выделенную память равным 0, поэтому вы можете использовать это и не беспокоиться о явной настройке e lement to NULL:

a = calloc(users_count + 1, sizeof *a); 
if (a) 
{ 
    for (i = 0; i < users_count; i++) 
    ... 
} 
+1

Технически установка указателя на все бит-ноль (как 'calloc' does) не гарантирует получение нулевого указателя. – melpomene

+0

@melpomene У вас есть ссылка на это? C Standard, * 6.3.2.3 Указатели *, параграф 3, гласят: «Целочисленное константное выражение со значением 0 или такое выражение, отличное от типа ' void * ', называется константой нулевой указатель *. ..." Я бы подумал, что это будет, по крайней мере, зависящим от реализации значением указателя «NULL» на большинстве платформ. . –

+1

@AndrewHenle Я не вижу, как это применимо здесь. 'calloc (...)' не содержит никакого целочисленного константного выражения со значением 0. – melpomene