Определяется это поведение или нет?
Не.
long (*ptr)[1] = NULL;
long v = (long) *ptr;
printf("%ld\n", v);
Это работает, потому что разыменовывая указатель на массив, мы получаем сам массив затем этот массив распадается на указатель на его первый элемент.
Нет, вы путаете тип со значением. Это правда, что выражение *ptr
во второй строке имеет тип type long[1]
, но вычисление этого выражения приводит к неопределенному поведению независимо от типа данных и независимо от автоматического преобразования c, которое было бы применяется к результату, если он был определен.
Соответствующий раздел спецификации c представляет собой пункт 6.5.2.3/4:
Унарный * Оператор обозначает косвенность. Если операнд указывает на функцию, результатом является обозначение функции; если он указывает на объект, результатом является lvalue, обозначающее объект. Если операнд имеет тип «указатель на тип», результат имеет тип «тип». Если указателю было присвоено недопустимое значение, поведение унарного оператора * не определено.
Сноска продолжает разъяснять, что
[...] Среди недопустимых значений для разыменования указателя с помощью унарного оператора * есть нулевой указатель [...]
Это может «работать» для вас в эмпирическом смысле, но с точки зрения языка, любой вывод вообще или ничего не является соответствующим результатом.
Обновление:
Может быть интересно отметить, что ответ будет другим для явно взяв адрес *ptr
, чем для предположения, что распад массива преодолеет неопределенность разыменования. Стандарт предусматривает, что в особом случае, когда операнд унарного оператора &
является результатом унарного оператора *
, ни один из этих операторов не оценивается. При условии, что все соответствующие ограничения выполнены, результат будет таким, как если бы они оба были полностью опущены, за исключением того, что он никогда не является lvalue.
Таким образом, это нормально:
long (*ptr)[1] = NULL;
long v = (long) &*ptr;
printf("%ld\n", v);
На многих реализации он будет надежно печатать 0, но учтите, что C не указывает, что оно должно быть 0.
Ключевое различие здесь в том, что в этом случае операция *
не оценивается (на c). Операция *
в исходном коде оценивается , несмотря на тот факт, что если бы значение указателя было допустимым, результирующий массив был бы преобразован обратно в указатель (другого типа, но в то же место). Это предполагает очевидное сокращение, которое реализации могут использовать с исходным кодом, и они могут использовать его, если они будут sh, независимо от того, допустимо ли значение ptr
, потому что, если оно равно в тогда они могут делать все, что хотят.