Можно ли инициализировать указатель 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;
.
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 9899
freeявляется nop, когда аргументNULL, код выше (или что-то более значимое в тех же строках) является законным.
Это правильно.
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