Я прочитал (часть) стандарта C99, чтобы очистить свое мнение. Я нашел разделы, которые представляют интерес для моего собственного вопроса, и пишу это для справки.
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ
Я абсолютный новичок, 90% или более того, что я написал, неверно, не имеет смысла или может сломать тостер. Я также пытаюсь обосновать стандарт, часто с катастрофическими и наивными результатами (как указано в комментарии).
Не читайте.
Проконсультируйтесь с @Olaf, чтобы получить официальный и профессиональный ответ.
В дальнейшем термин архитектурный адрес обозначает адрес памяти, который видит процессор (логический, виртуальный, линейный, физический или шинный). Другими словами, адреса, которые вы бы использовали при сборке.
В разделе 6.3.2.3. это читается
Целочисленное постоянное выражение со значением 0 или такое выражение, приведенное к типу void *
, называется константой нулевого указателя. Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно не будет сравниваться с указателем на любой объект или функцию.
и относительно преобразования целого числа в указатель
Целое число можно преобразовать в любой тип указателя. За исключением случаев, указанных ранее [т.е. для случая константы нулевого указателя], результат определяется реализацией, может быть неправильно выровнен, может не указывать на сущность указанного типа и может быть представлением ловушки †.
Это подразумевает, что компилятору, чтобы быть совместимым, нужно только реализовать функцию int2ptr от целого числа до указателей, которые
- int2ptr (0) по определению является нулевым указателем.
Обратите внимание, что int2ptr (0) не обязательно равняется 0. Это может быть любое битовое представление.
- * int2ptr (n! = 0) не имеет ограничений.
Обратите внимание, что это означает, что int2ptr не обязательно должна быть функцией идентификации или функцией, возвращающей действительные указатели !
Учитывая приведенный ниже код
char* p = (char*)241;
Стандарт не дает никаких гарантий, что выражение *p = 56;
будет записывать по архитектурному адресу 241.
И поэтому он не дает прямого доступа к любому другому архитектурному адресу (включая int2ptr (0), адрес, созданный нулевым указателем, если он действителен).
Проще говоря, стандарт касается не архитектурных адресов, а указателей, их сравнения, преобразований и их операций ‡.
Когда мы пишем код типа char* p = (char*)K
, мы не говорим компилятору сделать p
указателем на архитектурный адрес K, мы говорим ему сделать указатель из целого числа K, или, другими словами, чтобы p
указывал на (C аннотация) адрес K.
Нулевой указатель и (архитектурный) адрес 0x0 - это не одно и то же (цит.), и это верно для любого другого указателя, созданного из целого числа K и (архитектурного) адреса. К.
По некоторым причинам из детского наследия, я думал, что целочисленные литералы в C могут использоваться для выражения архитектурных адресов, вместо этого я был неправ, и это было (вроде) правильным только в компиляторах, которые я использовал .
Ответ на мой собственный вопрос прост: Нет стандартного способа, потому что в стандартном документе C нет (архитектурного) адреса. Это верно для любого (архитектурного) адреса, а не только для int2ptr (0) одного 1.
Примечание о return *(volatile char*)0;
В стандарте сказано, что
Если указателю присвоено недопустимое значение [значение нулевого указателя является недопустимым], поведение унарного оператора * не определено.
и это
Следовательно, любое выражение, относящееся к такому объекту [volatile], должно оцениваться строго в соответствии с правилами абстрактной машины.
Абстрактная машина говорит, что *
не определено для значений нулевого указателя, поэтому этот код не должен отличаться от этого
return *(char*)0;
который также не определен.
На самом деле они не отличаются, по крайней мере, с GCC 4.9, оба компилируются в соответствии с инструкциями, указанными в моем вопросе.
Определяемый реализацией способ доступа к архитектурному адресу 0 для GCC - это использование флага -fno-isolate-erroneous-paths-dereference, который производит "ожидаемый" ассемблерный код.
† Функции преобразования для преобразования указателя в целое число или целого числа в указатель предназначены для согласования со структурой адресации среды выполнения.
‡ К сожалению, он говорит, что &
дает адрес своего операнда, я считаю, что это немного неправильно, я бы сказал, что он дает указатель на свой операнд. Рассмотрим переменную a
, которая, как известно, находится по адресу 0xf1 в 16-битном адресном пространстве, и рассмотрим компилятор, который реализует int2ptr (n) = 0x8000 | п. &a
даст указатель с битовым представлением 0x80f1, который не является адресом a
.
1 Что было особенным для меня, потому что в моих реализациях он был единственным, к которому нельзя было получить доступ.
person
Margaret Bloom
schedule
21.02.2016
0
- это всего лишь константа нулевого указателя; это отличается от нулевого указателя. - person too honest for this site   schedule 21.02.20160
, присвоенного целочисленному типуzero
, в указатель уже является неопределенным поведением. Стандарт разрешает преобразование только указателя в этот тип и обратно. Даже использование другого типа указателя - это уже UB. - person too honest for this site   schedule 21.02.20160
, если он используется в условии. Странно, что теперь вы пишете полную противоположность тому, что писали несколько комментариев назад: значение нулевого указателя всегда равно 0, но его битовое представление нет. - person too honest for this site   schedule 21.02.2016if( pointer )
, где указатель является нулевым указателем? - person 2501   schedule 21.02.20160
- это константа нулевого указателя, только контекст указателя. Но нулевой указатель - это переменная-указатель, которая равна константе нулевого указателя. (Я действительно ненавижу это C11 здесь не следовал C ++ 11 и предоставил конкретное ключевое слово, например _Nullptr - с заголовком + макросnullptr
). Другие языки, такие как Паскаль, с самого начала были более интеллектуальными. - person too honest for this site   schedule 21.02.2016_Bool b = 5;
? - person too honest for this site   schedule 21.02.2016int i = null_pointer; i== 0
сравнение может дать что угодно, но сравнениеnull_pointer == 0
всегда вернет истину. - person 2501   schedule 21.02.2016int i = null_pointer
определяется реализацией, еслиnull_pointer
является типом указателя. Если вы имеете в виду0
, это только константа нулевого указателя в контексте указателя, в противном случае это целочисленная константа (в других языках это называется целочисленным литералом i >). Я избавляю нас от еще одной напыщенной речи об этой ненужной двойственности. Обратите внимание, что C ++ 11 ввелnullptr
именно для того, чтобы избавиться от этого взлома (который хуже в C ++, поскольку вам нужно преобразоватьvoid *
в указатель, поэтому не может быть#define NULL ((void *)0)
, как в C. К вашему сведению: gcc использует встроенное имя для больше времени в этом макросе уже. - person too honest for this site   schedule 21.02.2016<type> * p= 0;
, каждая реализация должна заполнять указательnull
вp
независимо от реализации или битовых шаблонов. Все остальное попадет под УБ. - person Henk Holterman   schedule 29.11.2016p++
может уменьшитьp
. Пока все операторы, включая сравнение, участвуют в этом. - person Henk Holterman   schedule 29.11.2016volatile
. Некоторым модераторам не нравятся ответы, основанные на этом, и они удаляют их. - person curiousguy   schedule 25.05.2019