Обязательно ли вызывать указатель = NULL при инициализации?

Когда я создаю указатель на определенную структуру, я должен установить его в NULL, затем выделить его, а затем использовать его? и почему?

6 ответов

Решение

Нет, применительно к языку не требуется (применительно к языку) инициализировать переменную-указатель при объявлении. таким образом

T* ptr;

является допустимым объявлением, которое вводит переменную с именем ptr с неопределенным значением. Вы даже можете использовать переменную определенным образом без предварительного выделения чего-либо или установки какого-либо определенного значения:

func(&ptr);

Нет, вам не нужно устанавливать его NULL, но некоторые считают это хорошей практикой, так как он дает новому указателю значение, которое делает его явным, он не указывает ни на что (пока).

Если вы создаете указатель и затем сразу присваиваете ему другое значение, тогда на самом деле не так уж много значения для его установки в NULL,

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

Согласно стандарту C, не инициализация автоматической переменной хранения оставляет ее значение неопределенным.

Вам определенно рекомендуется устанавливать указатели на NULL всякий раз, когда альтернативой является указатель, имеющий неопределенное значение. Использование указателей с неопределенным значением - это неопределенное поведение, концепция, которую вы можете не понять должным образом, если вы привыкли к программированию на C и пришли из языков более высокого уровня.

Неопределенное поведение означает, что все может произойти, и это является частью стандарта C, поскольку философия C позволяет программисту нести ответственность за управление вещами, а не защищать его от своих ошибок.

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

Если вы делаете что-то вроде этого:

char *p;
if(!p) puts("Pointer is NULL");

Вы на самом деле не знаете, что это if контроль будет истинным или ложным. Это чрезвычайно опасно в больших программах, где вы можете объявить свою переменную и затем использовать ее где-то очень далеко в пространстве и времени.

Та же концепция, когда вы ссылаетесь на освобожденную память.

free(p);
printf("%s\n", p);
p = q;

Вы на самом деле не знаете, что вы получите с двумя последними утверждениями. Вы не можете проверить это должным образом, вы не можете быть уверены в своем результате. Может показаться, что ваш код работает, потому что освобожденная память используется повторно, даже если она была изменена... вы можете даже перезаписать память или повредить другие переменные, которые используют ту же самую память. Таким образом, у вас все еще есть ошибка, которая рано или поздно проявится и может быть очень трудно ее отладить.

Если вы установите указатель на NULL, вы защитите себя от этих опасных ситуаций: во время ваших тестов ваша программа будет иметь детерминированное, предсказуемое поведение, которое быстро и дешево даст сбой. Вы получаете segfault, вы ловите ошибку, вы исправляете ошибку. Чисто и просто:

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

int main(){
  char* q;
  if(!q) puts("This may or may not happen, who knows");
  q = malloc(10);
  free(q);
  printf("%s\n", q); // this is probably don't going to crash, but you still have a bug

  char* p = NULL;
  if(!p) puts("This is always going to happen");
  p = malloc(10);
  free(p);
  p = NULL;
  printf("%s\n", p); // this is always going to crash

  return 0;
}

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

Нет, не забывайте, что инициализация должна вообще быть нулевым указателем. Очень удобная идиома в современном C - объявлять переменные при их первом использовании.

T * ptr = malloc(sizeof *ptr);

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

Так что, как правило, всегда инициализируйте переменные соответствующим значением. "Правильный 0 для типа "всегда хороший выбор, если у вас под рукой нет лучшего. Для всех типов C сделан так, чтобы он работал.

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

Вы не должны инициализироваться в NULL. Если вы намерены выделить его немедленно, вы можете пропустить его.

Может быть полезно для обработки ошибок, как в следующем примере. В этом случае разрыв до конца после p распределение оставит q неинициализированный.

int func(void)
{
    char *p;
    char *q;

    p = malloc(100);
    if (!p)
        goto end;

    q = malloc(100);
    if (!q)
        goto end;

    /* Do something */

end:
    free(p);
    free(q);

    return 0;
}

Кроме того, и это мой личный вкус, всегда выделяйте структуры calloc, Строки могут быть выделены неинициализированными с помощью malloc

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

Другие вопросы по тегам