Чому для нульового вказівника використовується нульова адреса?


121

У C (або C ++ для цього питання) вказівники є особливими, якщо вони мають значення нуля: мені рекомендується встановлювати покажчики в нуль після звільнення їх пам'яті, оскільки це означає, що звільнення вказівника знову не небезпечно; коли я дзвоню malloc, він повертає вказівник зі значенням нуль, якщо він не може отримати мені пам'ять; Я if (p != 0)весь час використовую, щоб переконатися, що передані покажчики є дійсними тощо.

Але оскільки адресація пам’яті починається з 0, чи не 0 так само дійсна адреса, як і будь-яка інша? Як 0 можна використовувати для обробки нульових покажчиків, якщо це так? Чому замість цього не є від'ємне число?


Редагувати:

Купа хороших відповідей. Я підсумую сказане у відповідях, висловлених так, як мій власний розум інтерпретує це, і сподіваюся, що громада виправить мене, якщо я неправильно зрозумію.

  • Як і все в програмуванні, це абстракція. Просто константа, не дуже пов’язана з адресою 0. C ++ 0x підкреслює це, додаючи ключове слово nullptr.

  • Це навіть не абстракція адреси, це константа, визначена стандартом C, і компілятор може перевести її на якесь інше число, якщо це гарантує, що він ніколи не дорівнює "реальній" адресі, і дорівнює іншим нульовим покажчикам, якщо 0 не є найкраще значення для використання на платформі.

  • Якщо це не абстракція, як це було в перші дні, система 0 використовує систему 0 і виключає обмеження для програміста.

  • Зізнаюсь, моя негативна пропозиція про кількість була трохи дикою мозковою атакою. Використання підписаного цілого числа для адрес мало марно, якщо це означає, що крім нульового вказівника (-1 або будь-якого іншого) простір значень розділено рівномірно між позитивними цілими числами, які роблять дійсні адреси та негативними числами, які щойно втрачаються.

  • Якщо будь-яке число завжди представлене типом даних, воно дорівнює 0. (Можливо, це теж 1. Я думаю про однорозрядне ціле число, яке було б 0 або 1, якщо без знака, або просто підписаний біт, якщо він підписаний, або два бітові цілі числа, які було б [-2, 1]. Але тоді ви можете просто перейти до 0, коли нуль, а 1 - єдиний доступний байт у пам'яті.)

Все-таки є щось, що не вирішено в моїй свідомості. Питання Переповнення стека Вказівник на конкретну фіксовану адресу говорить мені, що навіть якщо 0 для нульового вказівника є абстракцією, інші значення вказівника необов'язково. Це змушує мене опублікувати ще одне запитання про переповнення стека. Чи можу я коли-небудь хочу отримати доступ до нульової адреси? .


11
Ви можете так само легко змінити if (p != 0)на if (p)який є загальним ідіома в C і C ++, хоча вам доведеться вийти зі звички , якщо ви берете на Java.
Пол Томблін

14
Видалення чогось два рази означає, що ваш код неправильний. Я раджу не встановлювати покажчики на нуль, щоб ви могли вийти з ладу та виправити проблему, а не придушувати її. У будь-якому випадку ви помилитесь, вважаючи, що адреса є деяким цілим числом. Це не обов'язково відповідає дійсності, і 0 просто представляє деяке фактичне значення вказівника, яке залежить від реалізації. Поняття "негативна адреса" не має сенсу.
GManNickG

6
@GMan: Можливо навіть гарна ідея встановити вказівник на адресу, яка призведе до збою, наприклад 0xDEADBEEF.
Біллі ONeal

5
Питання, яке ніколи не помре!

8
@Noah: Точка встановлена ​​на null -> приховати помилки програмування, не встановлювати null -> знайти помилки програмування. Я не знаю про вас, але я хотів би, щоб мій код був правильним.
GManNickG

Відповіді:


65

2 бали:

  • лише постійне значення 0 у вихідному коді є нульовим покажчиком - реалізація компілятора може використовувати будь-яке значення, яке воно хоче або потребує у запущеному коді. Деякі платформи мають спеціальне значення покажчика, яке є "недійсним", яке може використовуватись як нульовий покажчик. У FAQ часто виникає питання: "Серйозно, чи справді машини використовували ненульові нульові покажчики чи різні подання для покажчиків на різні типи?" , це вказує на кілька платформ, які використовували цю властивість 0 як нульовий вказівник у джерелі C, а під час виконання представлені по-різному. Стандарт C ++ містить примітку, в якій чітко видно, що перетворення "інтегрального постійного виразу зі значенням нуль завжди дає нульовий покажчик,

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

Єдині вимоги до нульового вказівника:

  • гарантовано порівняти нерівне вказівник з фактичним об’єктом
  • будь-які два нульові покажчики порівнятимуть рівними (C ++ уточнює це таким чином, що це потрібно лише для покажчиків на один і той же тип)

12
+1 Я підозрюю, що 0 було обрано лише з історичних причин. (Здебільшого 0 є початковою та недійсною адресою.) Звичайно, загалом таке припущення не завжди відповідає дійсності, але 0 працює досить добре.
GManNickG

8
Простір також може бути фактором, що сприяє. У часи, коли C був вперше розроблений, пам'ять була набагато дорожчою, ніж зараз. Число нуль можна зручно обчислити за допомогою інструкції XOR або без необхідності завантажувати безпосереднє значення. Залежно від архітектури, це потенційно може заощадити простір.
Спаркі

6
@GMan - Ви маєте рацію. На ранніх процесорах нульова адреса пам’яті була особливою і мала апаратний захист від доступу від запущеного програмного забезпечення (в деяких випадках це був початок вектора скидання, а його зміна може запобігти перезавантаженню або запуску центрального процесора). Програмісти використовували цей апаратний захист як форму виявлення помилок у своєму програмному забезпеченні, дозволяючи логіці декодування адреси процесора перевіряти неініціалізовані або недійсні покажчики, замість того, щоб витрачати інструкції CPU для цього. Конвенція зберігається і донині, хоча ціль адреси нуля, можливо, змінилася.
бта

10
16-бітний компілятор Minix використовував 0xFFFF для NULL.
Джошуа

3
У багатьох вбудованих системах 0 - це дійсна адреса. Значення -1 (усі біти один) також є дійсною адресою. Контрольні суми для ПЗУ важко підрахувати, коли дані починаються за адресою 0. :-(
Томас Меттьюз

31

Історично в тому випадку, що адресний простір, починаючи з 0, завжди був ПЗУ, який використовується для деякої операційної системи або низькорівневого режиму обробки переривань, в даний час, оскільки все є віртуальним (включаючи адресний простір), операційна система може відображати будь-яке виділення на будь-яку адресу, тому воно може конкретно НЕ виділяйте нічого за адресою 0.


6
Це майже все. Це за історичною умовою, і перші адреси використовувались для обробників переривань, тому вони непридатні для звичайних програм. Також 0 є "порожнім", що можна інтерпретувати як відсутність значення / немає вказівника.
TomTom

15

IIRC, значення "нульовий покажчик" не гарантовано дорівнює нулю. Компілятор переводить 0 у будь-яке "нульове" значення, що підходить для системи (що на практиці, ймовірно, завжди дорівнює нулю, але не обов'язково). Цей же переклад застосовується кожного разу, коли ви порівнюєте покажчик проти нуля. Оскільки ви можете порівнювати лише вказівники один проти одного та проти цього спеціального значення-0, це захищає програміста від того, щоб знати що-небудь про представлення пам'яті системи. Що стосується того, чому вони обрали 0 замість 42 або колись, я думаю, це тому, що більшість програмістів починають рахувати з 0 :) (Також у більшості систем 0 перша адреса пам'яті, і вони хотіли, щоб це було зручно, оскільки в практикуйте переклади, як я описую, рідко насправді відбуваються; мова їх просто дозволяє).


5
@Justin: Ви неправильно зрозуміли. Константа 0 завжди є нульовим покажчиком. Що говорить @meador, це те, що можливо, що нульовий покажчик (позначений постійною 0) не відповідає нулю адреси. На деяких платформах створення нульового вказівника ( int* p = 0) може створити покажчик, що містить значення 0xdeadbeefабо будь-яке інше значення, яке він надає перевагу. 0 - це нульовий покажчик, але нульовий покажчик не обов'язково є вказівником на адресу нуля. :)
jalf

Вказівник NULL - це зарезервоване значення, і залежно від компілятора може бути будь-який біт. Вказівник NULL не означає, що він вказує на адресу 0.
Шарджіл Азіз

3
Але @Jalf, константа 0 не завжди є нульовим покажчиком. Це ми пишемо, коли хочемо, щоб компілятор заповнив фактичний нульовий вказівник для нас. Практично кажучи, покажчик нульового зазвичай робить відповідний адресу нульового, хоча, і я інтерпретую питання Йоіла , як питати , чому це. Зрештою, за цією адресою є дійсний байт пам'яті, то чому б не використати неіснуючу адресу неіснуючого байта, а не видалити дійсний байт з гри? (Я пишу те, що я думаю, що Джоел думав, а не питання, яке я собі задаю.)
Роб Кеннеді,

@Rob: Сортування. Я знаю, що ви маєте на увазі, і ви маєте рацію, але я теж. :) Постійне ціле число 0 представляє нульовий покажчик на рівні вихідного коду. Якщо порівнювати нульовий покажчик з 0, то це відповідає дійсності. Присвоєння 0 вказівника встановлює цей покажчик на нуль. 0 - це нульовий покажчик. Але фактичне представлення в пам'яті нульового вказівника може відрізнятися від нульового бітового шаблону. (У всякому разі, мій коментар був у відповідь на видалений коментар @ Джастіна зараз, не на запитання @ Джоела. :)
jalf

@jalf @Rob Вам потрібні деякі терміни, щоб уточнити, я думаю. :) З §4.10 / 1: " Константа нульового покажчика - це інтегральне постійне вираження rvalue цілого типу, що оцінюється в нуль. Константа нульового вказівника може бути перетворена в тип вказівника; результат - значення нульового вказівника цього типу і відрізняється від будь-якого іншого значення вказівника на об'єкт або вказівника на тип функції. "
GManNickG

15

Ви повинні нерозуміти значення постійного нуля в контексті покажчика.

Ні в C, ні в C ++ покажчики не можуть "мати значення нуля". Покажчики не є арифметичними об’єктами. Вони не можуть мати числові значення, такі як "нуль" або "мінус", або щось подібне. Тож ваше твердження про "покажчики ... мають значення нуль" просто не має сенсу.

У C & C ++ вказівники можуть мати зарезервоване значення null-pointer . Фактичне подання значення нульового вказівника не має нічого спільного з будь-якими «нулями». Це може бути абсолютно все, що підходить для даної платформи. Це правда, що на більшості платформ нульове вказівне значення фізично представлене фактичним нульовим значенням адреси. Однак якщо на якійсь платформі адреса 0 використовується з якоюсь метою (тобто вам може знадобитися створити об’єкти за адресою 0), значення нульового вказівника на такій платформі, швидше за все, буде іншим. Наприклад, це може бути фізично представлене як 0xFFFFFFFFадресне значення або як 0xBAADBAADадресне значення.

Тим не менш, незалежно від того, як значення нульового вказівника представлено на даній платформі, у вашому коді ви все одно будете продовжувати позначати нульові покажчики постійними 0. Для того, щоб призначити значення нульового вказівника даному покажчику, ви продовжуватимете використовувати вирази типу p = 0. Відповідальність компілятора - усвідомити те, що ви хочете, і перекласти його у відповідне представлення значення нуля-вказівника, тобто перекласти його в код, який, наприклад, помістить адресне значення 0xFFFFFFFFу покажчик p.

Коротше кажучи, той факт, що ви використовуєте 0у своєму коді сортування для генерування нульових знаків-покажчиків, не означає, що значення нульового вказівника якось прив’язане до адреси 0. Те, 0що ви використовуєте у своєму вихідному коді, - це просто "синтаксичний цукор", який абсолютно не має відношення до фактичної фізичної адреси, на яку "вказує" значення нульового вказівника.


3
<quote> Покажчики не є арифметичними об’єктами </quote> Арифметика вказівника досить чітко визначена в C та C ++. Частина вимоги полягає в тому, що обидва вказівника вказують на один і той же композит. Нульовий покажчик не вказує на будь-який складений, тому використання його в арифметичних виразах покажчика є незаконним. Наприклад, це не гарантується (p1 - nullptr) - (p2 - nullptr) == (p1 - p2).
Бен Войгт

5
@Ben Voigt: Специфікація мови визначає поняття арифметичного типу . Я тільки говорю, що типи вказівників не належать до категорії арифметичних типів. Арифметика вказівника - це інша і абсолютно не пов'язана історія, простий мовний збіг.
ANT

1
Як хтось, читаючи арифметичні об'єкти, повинен вважати, що це означає "в значенні арифметичних типів", а не "в значенні арифметичних операторів" (декілька з яких можна використовувати в покажчиках) або "в значенні арифметики вказівника". Що стосується мовних збігів, то арифметичний об’єкт має більше букв спільного з арифметичними вказівниками, ніж арифметичними типами . У той же час стандарт говорить про значення вказівника . Оригінальний плакат, ймовірно, означав ціле подання покажчика, а не значення вказівника , і NULLявно не повинно бути представлене 0.
Бен Войгт

Ну, наприклад, термін скалярні об'єкти в термінології C / C ++ - це лише скорочення для об'єктів скалярних типів (подібно до об'єктів POD = об'єктів типів POD ). Я використовував термін арифметичні об'єкти точно так само, означаючи об'єкти арифметичних типів . Я очікую, що "хтось" зрозуміє це саме так. Хтось не завжди може просити роз'яснення.
ANT

1
Я працював у системі, де (що стосується обладнання) null було 0xffffffff і 0 - цілком дійсна адреса
pm100

8

Але оскільки адресація пам’яті починається з 0, чи не 0 є так само дійсною адресою, як і будь-яка інша?

У деяких / багатьох / всіх операційних системах адреса пам'яті 0 якось особлива. Наприклад, він часто відображається на недійсну / неіснуючу пам'ять, що викликає виняток, якщо ви намагаєтесь отримати доступ до неї.

Чому замість цього не є від'ємне число?

Я думаю, що значення вказівників зазвичай трактуються як безпідписані числа: інакше, наприклад, 32-бітний покажчик зможе адресувати лише 2 ГБ пам'яті, а не 4 ГБ.


4
Я кодував на пристрої, де нульова адреса була дійсною адресою, а захист пам’яті не було. Нульові покажчики також були всебітними нулями; якщо ви випадково записали на нульовий вказівник, то ви пропустили налаштування ОС, які були за нульовою адресою; веселість зазвичай не настала.
ММ

1
Так: наприклад, у процесорі x86 незахищеного режиму, наприклад, адреса 0 - це векторна таблиця переривань .
ChrisW

@ChrisW: У незахищеному режимі x86, зокрема, нульова адреса - вектор переривання розділення на нуль, який деякі програми можуть мати цілком законні причини для написання.
supercat

Навіть на платформах, де зручне зберігання розпочнеться за фізичною адресою, нуль, реалізація C може легко використовувати або нульову адресу для утримання об'єкта, адреса якого ніколи не приймається, або ж просто залишити перше слово пам'яті невикористаним. На більшості платформ порівняння з нулем економить інструкцію порівняно з порівнянням - ні з чим іншим, тому навіть марнувати перше слово зберігання було б дешевше, ніж використовувати ненульову адресу для нуля. Зауважте, що немає жодної вимоги, щоб адреси речей, не охоплених Стандартом C (наприклад, порти вводу / виводу або вектори переривання), порівнювали нерівне значення з нульовим, а також це ...
supercat

... системний процес null-pointer вказує будь-який відмінність від будь-якого іншого, тому all-біт-нуль, як правило, є чіткою адресою для "null" навіть у системах, де доступ до фізичного нуля буде корисним та значущим.
supercat

5

Я можу припустити, що магічне значення 0 було вибрано для визначення недійсного вказівника, оскільки його можна було перевірити за допомогою менших інструкцій. Деякі мови машини автоматично встановлюють нульові і підписують прапори відповідно до даних під час завантаження регістрів, щоб ви могли перевірити нульовий покажчик простим завантаженням та інструкціями гілки, не виконуючи окремої інструкції порівняння.

(Більшість ISA встановлюють прапори лише за інструкціями ALU, але не завантажують. І зазвичай ви не створюєте покажчики за допомогою обчислень, за винятком компілятора при аналізі джерела C. Але принаймні вам не потрібна довільна константа ширини вказівника для порівняти проти.)

На Commodore Pet, Vic20 та C64, які були першими машинами, над якими я працював, оперативна пам’ять почалася з розташування 0, тому цілком можна було читати та писати за допомогою нульового вказівника, якщо ви цього дуже хотіли.


3

Я думаю, що це просто умовність. Для позначення недійсного вказівника повинно бути якесь значення.

Ви просто втрачаєте один байт адресного простору, що рідко має бути проблемою.

Немає негативних покажчиків. Покажчики завжди не підписані. Також якщо вони можуть бути негативними, ваша умовна умова означає, що ви втрачаєте половину адресного простору.


Примітка: ви фактично не втрачаєте адресного простору; ви можете отримати покажчик на адресу 0, виконавши: char *p = (char *)1; --p;. Оскільки поведінка на нульовому покажчику не визначено стандартом, ця система може pнасправді читати та писати адресу 0, збільшуючи вказати адресу 1тощо.
ММ

@MattMcNabb: Реалізація, де нуль адреси є дійсною апаратною адресою, може абсолютно законно визначати поведінку char x = ((char*)0);читання адреси нуля і зберігати це значення у x. Такий код дасть Undefined Behavior для будь-якої реалізації, яка не визначає його поведінку, але той факт, що стандарт говорить, що щось є Undefined Behavior, жодним чином не забороняє реалізаціям пропонувати власні специфікації того, що вона буде робити.
supercat

@supercat ITYM *(char *)0. Це правда, але, на мою думку, реалізації не потрібно визначати поведінку *(char *)0чи будь-які інші операції з нульовим покажчиком.
ММ

1
@MattMcNabb: Поведінка char *p = (char*)1; --p;буде визначена стандартом лише в тому випадку, якщо ця послідовність виконується після того, як вказівник на щось інше, ніж перший байт об'єкта був переданий на intptr_t, і результат цього накипу мав отримати значення 1 , і в цьому конкретному випадку результат --pдасть вказівник на байт, що передує тому, чиє значення вказівника при передачі на intptr_tнього поступило 1.
supercat

3

Хоча C використовує 0 для представлення нульового вказівника, майте на увазі, що значення самого вказівника може бути не нульовим. Однак більшість програмістів використовуватимуть лише ті системи, де нульовий покажчик насправді дорівнює 0.

Але чому нуль? Ну, це одна адреса, якою ділиться кожна система. І найчастіше низькі адреси зарезервовані для цілей операційної системи, тому значення працює і як поза межами прикладних програм. Випадкове присвоєння цілого значення покажчику так само ймовірно, що в кінцевому підсумку вийде нуль, як і все.


3
Більш ймовірна причина цього полягає в тому, що дешево роздавати пам'ять, попередньо ініціалізовану до нуля, і зручно мати значення в цій пам'яті, які представляють щось значиме, як ціле число 0, плаваюча точка 0,0 та нульові покажчики. Статичні дані в C, ініціалізовані до нуля / null, не повинні займати будь-якого простору у виконуваному файлі і відображаються в заповненому нулем блоці при завантаженні. Нуль може також отримати спеціальну обробку на машинних мовах: легке порівняння нуля, наприклад, "гілка, якщо дорівнює нулю" тощо. MIPS навіть має фіктивний реєстр, який є лише нульовою постійною.
Каз

2

Історично мало пам'яті програми займали системні ресурси. Саме в ті дні нульове значення стало за замовчуванням.

Хоча це не обов'язково стосується сучасних систем, все-таки погана ідея встановлювати значення вказівника будь-що, крім того, що розподіл пам'яті вам передав.


2

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

Якщо ви насправді дуже переживаєте з цього приводу, тоді кращим підходом, який гарантовано працює, є використання твердження про ():


...
assert(ptr && "You're deleting this pointer twice, look for a bug?");
delete ptr;
ptr = 0;
...

Це вимагає додаткової набору тексту та однієї додаткової перевірки під час збирання налагодження, але це неодмінно дасть вам те, що ви хочете: зауважте, коли ptr буде видалено "двічі". Альтернатива, наведена в обговоренні коментарів, не встановлюючи покажчик на нуль, щоб ви отримали збій, просто не гарантується успіхом. Гірше, на відміну від вищезазначеного, це може спричинити збій (або набагато гірше!) У користувача, якщо один із цих «помилок» потрапить на полицю. Нарешті, ця версія дозволяє продовжувати запускати програму, щоб побачити, що насправді відбувається.

Я усвідомлюю, що це не відповідає на поставлене запитання, але я побоювався, що хтось, читаючи коментарі, може прийти до висновку, що вважається "хорошою практикою" НЕ встановлювати покажчики на 0, якщо можливо, їх відправляють безкоштовно () або видалити двічі. У тих кількох випадках, коли це можливо, НІКОЛИ не є хорошою практикою використовувати не визначене поведінку як інструмент налагодження. Ніхто, якому ніколи не довелося вишукувати помилку, яка в кінцевому рахунку була викликана видаленням недійсного вказівника, не запропонувала цього. Такі помилки займають години, щоб вистежити і майже завжди впливати на програму абсолютно несподіваним способом, який важко неможливо відстежити до початкової проблеми.


2

Важливою причиною, чому багато операційних систем використовують all-біт-нуль для представлення нульових покажчиків, є те, що цей засіб memset(struct_with_pointers, 0, sizeof struct_with_pointers)і подібне встановлять усі вказівники всередині struct_with_pointersнульових покажчиків. Це не гарантується стандартом С, але багато, багато програм припускають це.


1

На одній із старих машин DEC (PDP-8, я думаю) час виконання C захищав би пам'ять першої сторінки пам'яті, щоб будь-яка спроба отримати доступ до пам'яті в цьому блоці викликала б виняток.


PDP-8 не мав компілятора С. PDP-11 не захищав пам'ять, і VAX був сумно відомий тим, що мовчки повертав 0 до NULL-покажчиків. Я не впевнений, на яку машину йдеться.
FUZ

1

Вибір вартості довільного є довільним, і це насправді вирішується наступною версією C ++ (неофіційно відомою як "C ++ 0x", найімовірніше, в майбутньому стане відомою як ISO C ++ 2011) із впровадженням ключове слово nullptrдля відображення нульового значення вказівника. У C ++ значення 0 може використовуватися як ініціалізуючий вираз для будь-якого ПОД і для будь-якого об'єкта з конструктором за замовчуванням, і воно має особливе значення присвоєння дозорного значення у випадку ініціалізації вказівника. Щодо того, чому не було вибрано негативне значення, адреси зазвичай коливаються від 0 до 2 N-1 для деякого значення N. Іншими словами, адреси зазвичай трактуються як непідписані значення. Якщо максимальне значення використовувалося як дозорне значення, то воно повинно було б змінюватися від системи до системи залежно від розміру пам'яті, тоді як 0 - це завжди представницька адреса. Він також використовується з історичних причин, оскільки адреса пам'яті 0 зазвичай не використовується в програмах, і в наш час більшість ОС мають частини ядра, завантажені в нижню сторінку (-и) пам'яті, і такі сторінки, як правило, захищені таким чином, що якщо торкнувшись (відпустити) програмою (зберегти ядро), це призведе до помилки.


1

Це має мати певну цінність. Очевидно, що ви не хочете наступати на цінності, які може законно захотіти користувач. Я б міркував, що оскільки час виконання C забезпечує сегмент BSS для даних, ініціалізованих за допомогою нуля, це має певну міру інтерпретувати нуль як неініціалізоване значення вказівника.


0

Рідко ОС дає змогу писати на адресу 0. Зазвичай, для того, щоб приклеювати специфічні для ОС речі в низькій пам'яті; а саме IDT, таблиці сторінок тощо (таблиці повинні бути в оперативній пам’яті, і їх легше вставити внизу, ніж намагатися визначити, де знаходиться верхня частина оперативної пам’яті.) І жодна ОС з розумом не дозволить вам редагувати системні таблиці мимоволі.

Це, можливо, не було в голові K&R, коли вони робили C, але це (поряд з тим, що 0 == null досить легко запам’ятати) робить 0 популярним вибором.


Це не вірно в захищеному режимі, і насправді для деяких конфігурацій Linux ви можете писати на віртуальну адресу 0.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

0

Значення 0- це особливе значення, яке набуває різних значень у конкретних виразах. Що стосується покажчиків, як уже багато разів зазначалося, воно використовується, ймовірно, тому, що в той час це був найзручніший спосіб сказати "вставити сюди значення довідкового за замовчуванням". Як постійний вираз, він не має такого ж значення, як побітовий нуль (тобто всі біти, встановлені в нуль) в контексті вираження вказівника. У C ++ є кілька типів, які не мають побітового нульового представлення NULLтаких, як член вказівника та функція вказівника на член.

До щастя, C ++ 0x має нове ключове слово «вираз , що означає , що відомий неприпустимий покажчик , який також не зіставити побітовий нуль для цілочисельних виразів»: nullptr. Хоча є кілька систем, на які можна націлити C ++, які дозволяють перенаправити адресу 0 без барфінгу, тому програміст остерігайтеся.


0

У цій темі вже багато хороших відповідей; напевно є багато різних причин 0віддати перевагу значення нульових покажчиків, але я збираюся додати ще дві:

  • У C ++ нульовий ініціалізатор покажчика встановить його на нуль.
  • У багатьох процесорах ефективніше встановити значення 0 або перевірити його на рівні / не дорівнює 0, ніж для будь-якої іншої постійної.

0

Це залежить від реалізації покажчиків у C / C ++. Немає конкретної причини, чому NULL еквівалентний у присвоєнні покажчику.


-1

Для цього є історичні причини, але є й причини оптимізації.

В ОС звичайно забезпечувати процес із сторінками пам'яті, ініціалізованими до 0. Якщо програма хоче інтерпретувати частину цієї сторінки пам'яті як вказівник, то вона дорівнює 0, тому програмі досить просто визначити, що цей покажчик не ініціалізовано. (це не так добре, якщо застосовуватись до неініціалізованих флеш-сторінок)

Інша причина полягає в тому, що для багатьох процесорів дуже легко перевірити еквівалентність значення 0. Це іноді безкоштовне порівняння, яке проводиться без додаткових інструкцій, і, як правило, це можна зробити, не вказуючи нульове значення в іншому регістрі або як буквальний в потоці інструкцій для порівняння.

Дешеві порівняння для більшості процесорів мають підписані менше 0 і дорівнюють 0. (підписані більше 0 і не рівні 0 маються на увазі обох цих)

Оскільки 1 значення з усіх можливих значень потрібно зарезервувати як погане або неініціалізоване, то ви також можете зробити його таким, що має найдешевший тест на еквівалентність поганому значенню. Це також справедливо для символів, що закінчуються "\ 0".

Якби ви намагалися використовувати для цієї мети більше або менше 0, то в кінцевому підсумку ви розрізаєте наполовину свій діапазон адрес.


-2

Константа 0використовується замість NULLтому , що C було зроблено деякими Cavemen трильйони років тому, NULL, NIL, ZIP, або NADDAб все зробив набагато більше сенсу , ніж 0.

Але оскільки адресація пам’яті починається з 0, чи не 0 є так само дійсною адресою, як і будь-яка інша?

Справді. Хоча багато операційних систем забороняють вам відображати що-небудь за нульовою адресою, навіть у віртуальному адресному просторі (люди зрозуміли, що C - це незахищена мова, і, відображаючи, що помилки з перенапрямленням нуля є дуже поширеними, вирішили "виправити" їх, заборонивши Код простору користувача для відображення на сторінку 0; Таким чином, якщо ви викликаєте зворотний дзвінок, але вказівник зворотного дзвінка NULL, ви не зможете виконати якийсь довільний код).

Як 0 можна використовувати для обробки нульових покажчиків, якщо це так?

Оскільки 0використовується в порівнянні з покажчиком буде замінено деяким конкретним значенням реалізації , яке є поверненим значенням malloc при збої в malloc.

Чому замість цього не є від'ємне число?

Це було б ще більш заплутано.


Ваша думка про "печерних людей" тощо, ймовірно, лежить в корені цього, хоча я думаю, що специфіка інша. Найдавніші форми того, що перетворилося на C, були розроблені для роботи в одній конкретній архітектурі, де intрозмір не тільки того ж розміру, що й вказівник - у багатьох контекстах intі вказівник може використовуватися взаємозамінно. Якщо підпрограмма очікувала, що вказівник і один пройдений в ціле число 57, підпрограма використовує адресу з тим же бітовим шаблоном, що і число 57. На цих конкретних машинах бітовий шаблон для позначення нульового вказівника дорівнював 0, тому передаючи int 0 передасть нульовий покажчик.
supercat

З цього часу C розвинувся таким чином, що його можна використовувати для написання програм для величезної кількості інших машин з різним поданням чисел та покажчиків. Хоча ненульові числові константи рідко використовувались як покажчики, постійні числові нулі широко використовувались для представлення нульових покажчиків. Якщо заборонити таке використання, було б порушено існуючий код, тому очікується, що компілятори переведуть числовий нуль у те, що використовується для представлення нульового покажчика.
supercat

-4

( Будь ласка, прочитайте цей параграф перед читанням повідомлення. Я прошу всіх, хто зацікавився читати цю публікацію, спробувати прочитати її уважно, і, звичайно, не зволікайте, поки ви не зрозумієте її повністю, дякую.)

Зараз вікі спільноти, як такий, якщо хтось не погоджується з будь-яким із понять, будь ласка, модифікуйте його з чітким і детальним поясненням того, що не так і чому, і, якщо можливо, наведіть джерела або надайте докази, які можуть бути відтворені.

Відповідь

Ось кілька інших причин, які можуть бути основними факторами для NULL == 0

  1. Справа в тому, що нуль неправдивий, тому можна робити безпосередньо if(!my_ptr)замість цьогоif(my_ptr==NULL) .
  2. Той факт, що неініційовані глобальні цілі числа ініціалізуються за замовчуванням на всі нулі, і як такий покажчик усіх нулів вважатиметься неініціалізованим.

Тут я хотів би сказати слово про інші відповіді

Не через синтаксичний цукор

Сказати, що NULL дорівнює нулю через синтаксичний цукор, не має занадто великого сенсу, якщо так, чому б не використовувати індекс 0 масиву, щоб утримати його довжину?

Насправді C - це мова, яка найбільше нагадує внутрішню реалізацію, чи є сенс говорити, що C вибрав нуль саме через синтаксичний цукор? Вони скоріше нададуть ключове слово null (як це роблять багато інших мов), а не відображення нуля в NULL!

Тому що, як сьогодні, це може бути просто синтаксичний цукор, зрозуміло, що початковий задум розробників мови С був не для синтаксичного цукру, як я покажу далі.

1) Специфікація

І хоча це правда, що специфікація C говорить від константи 0 як нульовий покажчик (розділ 6.3.2.3), а також визначає NULL, що визначається реалізацією (розділ 7.19 в специфікації C11 і 7.17 в специфікації C99), Фактом залишається факт, що у книзі "Мова програмування на C", яку написали винахідники C, у розділі 5.4 зазначено наступне:

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

Покажчик і цілі числа не є взаємозамінними, нуль - єдиний виняток: постійний нуль може бути призначений покажчику, а вказівник може порівнюватися з постійним нулем. Символічна константа NULL часто використовується замість нуля, як мнемоніка, щоб більш чітко вказати, що це особливе значення для вказівника. NULL визначається в. Ми відтепер будемо використовувати NULL.

Як видно (зі слів "нульова адреса"), принаймні початковий намір авторів С був адресовим нулем, а не постійним нулем. Більше того, з цього уривку випливає, що причина специфікації говорить від постійний нуль, ймовірно, не виключає вираз, який оцінює нулю, а замість того, щоб включити цілий постійний нуль, щоб бути єдиною цілою постійною дозволеною для використання в контексті покажчика без кастингу.

2) Підсумок

Хоча специфікація прямо не говорить про те, що нульову адресу можна розглядати інакше, ніж нульову константу, вона не говорить про те, що ні, і той факт, що при роботі з константою з нульовим покажчиком вона не вимагає, щоб її реалізація була визначена як робить за NULL визначеною постійною, замість того, щоб стверджувати, що вона дорівнює нулю, показує, що може бути різниця між нульовою постійною та нульовою адресою.

(Однак якщо це так, я просто задаюся питанням, чому NULL визначена реалізацією, оскільки в такому випадку NULL також може бути постійним нулем, оскільки компілятор все одно повинен перетворити всі нульові константи у фактичну реалізовану NULL?)

Однак я не бачу цього в реальній дії, і на загальних платформах адреса нуль і постійний нуль трактуються однаково і кидають одне і те ж повідомлення про помилку.

Крім того, факт полягає в тому, що сьогоднішні операційні системи фактично зберігають всю першу сторінку (діапазон від 0x0000 до 0xFFFF), щоб запобігти доступу до нульової адреси через NULL вказівника C (див. Http://en.wikipedia.org/wiki/ Zero_page , а також "Windows Via C / C ++ Джефрі Ріхтер та Крістоф Насарр (опубліковано Microsoft Press)".

Таким чином, я просив би всіх, хто претендує на те, що він насправді бачив, вказувати платформу та компілятор та точний код, який він насправді робив (хоча через розпливче визначення у специфікації [як я показав] будь-який компілятор і платформа вільна робити все, що він хоче).

Однак, мабуть, здається, що автори C цього не мали на увазі, і вони говорили про "нульову адресу", і що "C гарантує, що це ніколи не дійсна адреса", а також "NULL - це просто мнемонічний ", чітко показуючи, що його первісний намір був не для" синтаксичного цукру ".

Не через операційну систему

Також заявляючи, що операційна система забороняє доступ до нульової адреси з кількох причин:

1) Коли було написано С, такого обмеження не було, як це можна побачити на цій вікі-сторінці http://en.wikipedia.org/wiki/Zero_page .

2) Справа в тому, що компілятори C отримали доступ до адреси пам'яті до нуля.

Це, мабуть, факт із наступного документа BellLabs ( http://www.cs.bell-labs.com/who/dmr/primevalC.html )

Два компілятори відрізняються деталями у тому, як вони справляються з цим. У попередній - початок знаходимо, називаючи функцію; пізніше початок просто вважається рівним 0. Це вказує на те, що перший компілятор був написаний ще до того, як у нас була машина з картою пам'яті, тому походження програми було не в розташуванні 0, тоді як до моменту другого, у нас був PDP-11, який надав картування.

(Насправді станом на сьогодні (як я цитував посилання з wikipedia та microsoft press), причина обмеження доступу до нульової адреси - через NULL-покажчики C! Отже, врешті-решт, виявляється навпаки!)

3) Пам'ятайте, що C також використовується для запису операційних систем і навіть компіляторів C!

Насправді C був розроблений для того, щоб записати на нього операційну систему UNIX, і як таке, мабуть, немає причин, чому вони повинні обмежуватися від нульової адреси.

(Обладнання) Пояснення про те, як комп’ютери (фізично) здатні отримати доступ до нульової адреси

Тут я хочу пояснити ще один момент, як взагалі можна посилатись на нуль?

Подумайте про це на секунду, адреси надходить процесором, а потім надсилаються як напруги на шині пам'яті, яка потім використовується системою пам'яті, щоб дістатись до фактичної адреси, і все ж адреса нуля означатиме відсутність напруги , то як фізичне обладнання системи пам'яті отримує доступ до адреси нульовим?

Здається, що відповідь, що нульова адреса є типовим, а іншими словами, адресна нуль завжди доступна системою пам'яті, коли шина пам'яті повністю відключена, і як такий будь-який запит на читання або запис, не вказуючи фактичну адресу (яка у випадку з нульовою адресою) автоматично отримує доступ до нуля адреси.


1
Я вас не порушив, але ваш пост має кілька фактичних неточностей, наприклад. що до фізичної пам’яті при зміщенні 0 неможливо отримати доступ (через те, що всі вимикачі відключені? дійсно?), 0 і постійні 0 є взаємозамінними (їх може не бути) та інші.
Hasturkun

Щодо 0 та постійного нуля, про це говорить оригінальна книга, і це те, що показує фактичне тестування, чи виявили ви справжню різницю між ними? Якщо так, то який компілятор і платформа? Хоча багато відповідей говорять про те, що є різниця, я цього не знайшов, і вони не мають посилання, щоб показати різницю. Насправді за даними en.wikipedia.org/wiki/Zero_page А також "Windows Via C / C ++ Джеффрі Ріхтера та Крістофа Насарра (опубліковано Microsoft Press)" вся перша сторінка! захищено в сучасних комп’ютерах лише для того, щоб запобігти нулю (фактично витрачаючи більше одного байта!)
yoel halb

Звичайно, бітовий шаблон адреси використовується для вибору того, що читається. Це взагалі так. так чи інакше, я не хочу з вами сперечатися, я просто вказував, чому ви, можливо, були зняті з посади.
Hasturkun

Я не згоден з вашими претензіями. Мені також не цікаво продовжувати цю дискусію.
Гастуркун

6
Препаратні претензії - це нісенітниця. Щоб прочитати нульову адресу, введіть! Чіп Виберіть низький,! RAS високий,! CAS низький,! Ми високий, а всі адресні рядки низькі. Коли автобус вимкнений,! CS високий.
MSalters
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.