Кратък отговор:
Както в C, така и в C++, (int *)0
е постоянен израз, чиято стойност е нулев указател. Това обаче не е константа с нулев указател. Единствената видима разлика между константен израз-чиято-стойност-е-нулев-указател и константа-нулев-указател, за която знам, е, че константата-нулев-указател може да бъде присвоена на l-стойност на всяка тип указател, но константен-израз-чиято-стойност-е-нулев-указател има специфичен тип указател и може да бъде присвоен само на lvalue със съвместим тип. В C, но не и в C++, (void *)0
също е константа с нулев указател; това е специален случай за void *
в съответствие с общото правило за C-но не и C++, че void *
е присвояване, съвместимо с всеки друг тип указател към обект.
Например:
long *a = 0; // ok, 0 is a null pointer constant
long *b = (long *)0; // ok, (long *)0 is a null pointer with appropriate type
long *c = (void *)0; // ok in C, invalid conversion in C++
long *d = (int *)0; // invalid conversion in both C and C++
И ето случай, в който разликата между константата нулев указател (void *)0
и константен израз-чиято-стойност-е-нулев указател с тип void *
е видима дори в C:
typedef void (*fp)(void); // any pointer-to-function type will show this effect
fp a = 0; // ok, null pointer constant
fp b = (void *)0; // ok in C, invalid conversion in C++
fp c = (void *)(void *)0; // invalid conversion in both C and C++
Освен това в днешно време това е спорно, но тъй като го повдигнахте: Без значение какво е битовото представяне на нулевия указател на long *
, всички тези твърдения се държат, както е посочено от коментарите:
// 'x' is initialized to a null pointer
long *x = 0;
// 'y' is initialized to all-bits-zero, which may or may not be the
// representation of a null pointer; moreover, it might be a "trap
// representation", UB even to access
long *y;
memset(&y, 0, sizeof y);
assert (x == 0); // must succeed
assert (x == (long *)0); // must succeed
assert (x == (void *)0); // must succeed in C, unspecified behavior in C++
assert (x == (int *)0); // invalid comparison in both C and C++
assert (memcmp(&x, &y, sizeof y) == 0); // unspecified
assert (y == 0); // UNDEFINED BEHAVIOR: y may be a trap representation
assert (y == x); // UNDEFINED BEHAVIOR: y may be a trap representation
„Неуточнените“ сравнения не провокират недефинирано поведение, но стандартът не казва дали оценяват вярно или невярно и не се изисква внедряването да документира кое от двете е или дори да избере едно и да се придържа към него. Би било напълно валидно горното memcmp
да редува връщането на 0 и 1, ако сте го извикали много пъти.
Дълъг отговор със стандартни кавички:
За да разберете какво е константа нулев указател, първо трябва да разберете какво е целочислен константен израз, а това е доста странно -- пълното разбиране изисква да прочетете раздели 6.5 и 6.6 от C99 в детайли. Това е моето резюме:
Константен израз е всеки C израз, който компилаторът може да изчисли като константа, без да знае стойността на който и да е обект (const
или друго; обаче, enum
стойностите са честна игра), и който няма странични ефекти. (Това е драстично опростяване на приблизително 25 страници стандартен текст и може да не е точно.)
- #P10#
#P11# #P12#
#P13# #P14#
#P15#
Дефиницията на C++98 изглежда повече или по-малко еквивалентна, модулни характеристики на C++ и отклонения от C. Например, по-силното разделяне на символни и булеви типове от цели числа в C++ означава, че стандартът C++ говори за "интеграл константни изрази“, а не „целочислени константни изрази“, и след това понякога изисква не просто интегрален константен израз, а интегрален константен израз от целочислен тип, с изключение на char
, wchar_t
и bool
(а може и signed char
и unsigned char
? не ми става ясно от текста).
Сега, дефиницията на C99 за константа на нулев указател е това, за което се отнася този въпрос, така че ще го повторя: 6.3.2.3p3 казва
Цялочислен постоянен израз със стойност 0 или такъв израз, преобразуван към тип void *
, се нарича константа с нулев указател. Ако константата на нулев указател се преобразува в тип указател, полученият указател, наречен нулев указател, е гарантирано, че ще се сравни неравно с указател към който и да е обект или функция.
Standardese е много, много буквален. Тези две изречения означават абсолютно същото като:
Целочислен постоянен израз със стойност 0 се нарича константа с нулев указател.
Целочислен постоянен израз със стойност 0, преобразуван към тип void *
, е също < em>константа на нулев указател.
Когато която и да е константа на нулев указател се преобразува в тип указател, полученият указател се нарича нулев указател и е гарантиран да сравнявам неравностойно...
(Курсив - дефиниция на термина. Удебелен шрифт - моето ударение.) Това означава, че в C (long *)0
и (long *)(void *)0
са два начина за писане на абсолютно едно и също нещо, а именно нулевия указател с тип long *
.
C++ е различен. Еквивалентният текст е C++98 4.10 [conv.ptr]:
Константа на нулев указател е интегрален константен израз (5.19) rvalue от целочислен тип, който се оценява на нула.
Това е всичко. „Интегрален константен израз rvalue от целочислен тип“ е почти същото нещо като „целочислен константен израз“ на C99, но има няколко неща, които отговарят на условията в C, но не и в C++: например в C символният литерал '\x00'
е целочислена константа израз и следователно константа с нулев указател, но в C++ той не е интегрален константен израз от тип цяло число, така че също не е константа с нулев указател.
По-важно обаче е, че C++ няма клаузата "или подобен израз, преобразуван към void *
". Това означава, че ((void *)0)
не е константа с нулев указател в C++. Той все още е нулев указател, но не е присвояване, съвместимо с друг тип указател. Това е в съответствие с обикновено по-придирчивата система за типове на C++.
C++11 (но не, AFAIK, C11) ревизира концепцията за "нулев указател", добавяйки специален тип за тях (nullptr_t
) и нова ключова дума, която оценява константа на нулев указател (nullptr
). Не разбирам напълно промените и няма да се опитвам да ги обясня, но съм почти сигурен, че голото 0
все още е валидна константа на нулев указател в C++11.
person
zwol
schedule
27.01.2014
(int*)0
е а нулев указател, а не - person David Rodríguez - dribeas   schedule 27.01.2014int*
същото ли е като nulldouble*
? Предполагам, че в C е така... - person David Rodríguez - dribeas   schedule 27.01.2014