Можно ли инициализировать указатель C на NULL?



я писал такие вещи, как



char *x=NULL;


исходя из предположения, что



 char *x=2;


создать char указатель на адрес 2.



а, в учебник по программированию GNU C он говорит, что int *my_int_ptr = 2; хранит целочисленное значение 2 на любой случайный адрес в my_int_ptr когда оно выделяется.



это, казалось бы, означает, что мой собственный char *x=NULL присваивает любое значение NULL бросил в char это какой-то случайный адрес в памяти.



пока



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

int main()
{
char *x=NULL;

if (x==NULL)
printf("is NULLn");

return EXIT_SUCCESS;
}


действительно, печатает




равно NULL




когда я компилирую и запускаю его, я обеспокоен тем, что я полагаюсь на неопределенное поведение или, по крайней мере, недоопределенное поведение, и что я должен написать



char *x;
x=NULL;


.
610   9  

9 ответов:

можно ли инициализировать указатель C на NULL?

TL; DR Да, очень много.


The фактическая претензия к руководству читается как

С другой стороны, если вы используете только одно начальное назначение,int *my_int_ptr = 2; программа будет пытаться заполнить содержимое ячейки памяти, на которую указывает my_int_ptr со значением 2. С my_int_ptr заполнена мусором, это может быть любой адрес. [...]

Ну, они are неправильно, вы правы.

на заявлении (игнорируя, на данный момент, тот факт, что указатель на целочисленное преобразование является определяемым реализацией поведением)

int * my_int_ptr = 2;

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

теперь my_int_ptr, будучи типом указателя, мы можем сказать, что это указывает на значение "type" в ячейке памяти указал на значение удерживается в my_int_ptr. Таким образом, вы по существу присваиваете значение на переменная указатель, а не значение ячейки памяти, на которую указывает указатель.

так, по заключению

 char *x=NULL;

инициализирует переменная указателя x до NULL, а не значение по адресу памяти, на который указывает указатель.

это то же самое как

 char *x;
 x = NULL;    

расширения:

теперь, будучи строго соответствует, заявление, как

 int * my_int_ptr = 2;

является незаконным, так как это связано с нарушением ограничений. Чтобы было понятно,

  • my_int_ptr является переменной указателя, тип int *
  • целое число константа,2 типа int по определению.

и они не являются "совместимыми" типами, поэтому эта инициализация недопустима, поскольку она нарушает правила простого назначения, упомянутые в главе §6.5.16.1/P1, описанные в ответ Лундина.

если кому-то интересно, как инициализация связана с простыми ограничениями назначения, цитируя C11, глава §6.7.9, P11

инициализатор для скаляра должно быть одно выражение, необязательно заключенное в фигурные скобки. Этот начальное значение объекта-это значение выражения (после преобразования);один и тот же тип ограничения и преобразования как для простого назначения применяются, принимая тип скаляра быть безусловной версией его объявленного типа.

учебник неверен. В ISO C,int *my_int_ptr = 2; ошибка. В GNU C это означает то же самое, что и int *my_int_ptr = (int *)2; . Это преобразует целое число 2 к адресу памяти, в некотором роде, как определено компилятором.

Он не пытается хранить что-либо в месте, адресованном этим адресом (если таковые имеются). Если бы вы продолжили писать *my_int_ptr = 5;, то он будет пытаться сохранить число 5 в месте, адресованном по этому адресу.

чтобы выяснить, почему учебник неверен,int *my_int_ptr = 2; - Это "нарушение ограничений", это код, который не разрешается компилировать, и компилятор должен дать вам диагностику при его обнаружении.

в соответствии с 6.5.16.1 простое назначение:

ограничения

должно выполняться одно из следующих условий:

  • левый операнд имеет атомарный, квалифицированный или неквалифицированный арифметический тип, а правый-арифметический тип;
  • левый операнд имеет атомарную, квалифицированную или неквалифицированную версию структуры или типа объединения, совместимого с типом правого;
  • левый операнд имеет атомарный, квалифицированный или неполный тип указателя и (учитывая тип, который будет иметь левый операнд после lvalue преобразование) оба операнда являются указателями на квалифицированные или неквалифицированные версий совместимых типов, и тип, на который указывает левая все квалификаторы типа, указанного справа;
  • левый операнд имеет атомарный, квалифицированный или неполный тип указателя и (учитывая тип, который будет иметь левый операнд после lvalue преобразования) один операнд является указателем на тип объекта, а другой является указателем на квалифицированную или неквалифицированную версию void, а также тип, на который указывает слева, имеет все квалификаторы указанного типа купить права;
  • левый операнд является атомарным, квалифицированным или неквалифицированным указателем, а правый - константа нулевого указателя; или
  • левый операнд имеет тип atomic, qualified или unqualified _Bool, а правый-указатель.

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

-std=c11 -pedantic-errors, он правильно даст диагностику, как это должно быть сделано.

int *my_int_ptr = 2

сохраняет целочисленное значение 2 для любого случайного адреса в my_int_ptr, когда он выделяется.

это совершенно неверно. Если это действительно написано, то, пожалуйста, получите лучшую книгу или учебник.

int *my_int_ptr = 2 определяет целочисленный указатель, который указывает на адрес 2. Вы, скорее всего, получите сбой, если попытаетесь получить доступ к адресу 2.

*my_int_ptr = 2, т. е. без int в строке, магазины значение два для любого случайного адреса my_int_ptr указывает на. Сказав это, вы можете назначить NULL к указателю, когда он определен. char *x=NULL; совершенно справедливо C.

Edit: при написании этого я не знал, что преобразование целого числа в указатель является реализацией определенного поведения. Пожалуйста, смотрите хорошие ответы от @M. M и @SouravGhosh для деталей.

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

int *x = NULL; правильно C, но это очень вводит в заблуждение, я бы даже сказал бессмысленно, и это затруднило понимание языка для многих новичков. Это заставляет думать, что позже мы могли бы сделать *x = NULL; что, конечно, невозможно. Вы видите, тип переменной не является int, а имя переменной не *x, ни * в декларации играют любую функциональную роль в сотрудничестве с =. Это чисто декларативно. Итак, что имеет гораздо больше смысла это:

int* x = NULL; который также является правильным C, хотя он не придерживается оригинального стиля кодирования K&R. Это делает его совершенно ясно, что тип int*, а переменная-указатель x, так что даже непосвященным становится ясно, что значение NULL в настоящее время хранится в x, который является указателем на int.

кроме того, это упрощает вывод правила: когда звезда находится вдали от имени переменной, это объявление, а звезда, прикрепленная к имени, является разыменованием указателя.

Итак, теперь становится намного более понятно, что дальше вниз мы можем либо сделать x = NULL; или *x = 2; другими словами, это делает его легче для новичка, чтобы увидеть, как variable = expression ведет к pointer-type variable = pointer-expression и dereferenced-pointer-variable = expression. (Для инициированных под "выражением" я подразумеваю "rvalue".)

неудачный выбор в синтаксисе языка заключается в том, что при объявлении локальных переменных вы можете сказать int i, *p; который объявляет целое число и указатель на целое число, поэтому он приводит к тому, что * является полезной частью имени. Но это не так, и этот синтаксис - просто причудливый частный случай, добавленный для удобства, и, на мой взгляд, он никогда не должен был существовать, потому что он аннулирует правило это я предложил выше. Насколько я знаю, нигде в языке этот синтаксис не имеет смысла, но даже если это так, он указывает на несоответствие в том, как типы указателей определяются в C. везде, в объявлениях с одной переменной, в списках параметров, в элементах структуры и т. д. вы можете объявить свои указатели как type* pointer-variable вместо type *pointer-variable; это совершенно законно и имеет больше смысла.

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

int * p = NULL;
...
if (...) {
    p = (int*) malloc(...);
    ...
}
...
free(p);

так как согласно стандарт ISO-IEC 9899free является nop, когда аргумент NULL, код выше (или что-то более значимое в тех же строках) является законным.

это нулевой указатель

int * nullPtr = (void*) 0;

Это правильно.

int main()
{
    char * x = NULL;

    if (x==NULL)
        printf("is NULL\n");

    return EXIT_SUCCESS;
}

эта функция верна для того, что она делает. Он присваивается адрес 0 на char указатель х. То есть, он указывает указатель х к адресу памяти 0.

альтернатива:

int main()
{
    char* x = 0;

    if ( !x )
        printf(" x points to NULL\n");

    return EXIT_SUCCESS;
}

Я думаю, что вы хотели:

int main()
{
    char* x = NULL;
    x = alloc( sizeof( char ));
    *x = '2';

    if ( *x == '2' )
        printf(" x points to an address/location that contains a '2' \n");

    return EXIT_SUCCESS;
}

x is the street address of a house. *x examines the contents of that house.

помните:

В C,левостороннее значение всегда вычисляется в ячейке памяти, в то время как правая сторона всегда оценивает значение(будь то int или адрес или класс Foo).

Comments

    Ничего не найдено.