В настоящее время я изучаю указатели в c. Я узнал, что если я не назначу адрес указателю и попытаюсь напечатать значение содержимого указателя, программа не будет работать должным образом.
#include <stdio.h>
int main()
{
double *q;
printf("Hello World\n");
printf("*q = %lf", *q);
return 0;
}
Например, приведенный выше код напечатает «Hello World», а затем вернет значение, отличное от 0, полностью игнорируя вторую функцию printf(). Вот результат.
Однако все становится интереснее, если в код включен нулевой указатель.
#include <stdio.h>
int main()
{
double *p = NULL, *q;
printf("Hello World\n");
printf("*q = %lf", *q);
return 0;
}
Вот это результат. Честно говоря, этот вывод не имеет для меня никакого смысла. Я ожидал получить тот же результат, что и раньше, но этого явно не произошло. Как включение нулевого указателя может сделать *q = 0,000000? Я очень растерян. Пожалуйста, помогите мне понять, что здесь происходит.
Кроме того, отличается ли вывод от устройства к устройству? У меня есть подозрение, что устройства с ОС Linux будут выдавать другой результат. На моем устройстве в качестве ОС установлена Windows 11. Отличается ли результат и для других пользователей Windows (11 или других)? А может быть, значение 0,000000 — это мусорное значение? Мне любопытно.
Заранее спасибо.
Неопределённое поведение не определено. Но может случиться так, что q каким-то образом получит значение, указывающее на местоположение, в котором выделено p. Всё равно фигня, но оказывается ноль.
«Я ожидал получить тот же результат, что и раньше…» --> Любые ожидания подразумевают определенный уровень определенного поведения. *q остается неопределенным поведением или, может быть, неопределенным.
Совет: при исследовании результатов FP, подобных этому, используйте %g, %e или %a, а не %f. Они более информативны, когда значения малы. (например, значение может отличаться от 0,0)
Локальные нестатические переменные не инициализируются, их значения будут неопределенными. Неважно, указатель это или любой другой тип.
@Akib Aftermath, Различия между Linux и Windows: это скорее проблема компилятора, чем ОС.
@chux-ReinstateMonica Вероятно, и то, и другое. От того, как инициализируется память для процесса, до того, как обрабатывается доступ к неверному местоположению.
@ЕвгенийШ. Я бы сказал, что комментарий уже подразумевает и то, и другое.
0xC0000005 — это код NTSTATUS STATUS_ACCESS_VIOLATION.
Не анализировать УБы Почему?:godbolt.org/z/5rY5zs5Kc





Ваша первая программа не вернулась; оно разбилось.
Вы не инициализировали q. В printf("*q = %lf", *q);*q говорит загрузить double из того места, на которое указывает q. Куда указывает q? Мы не знаем, потому что вы не задали значение. Здесь могут произойти самые разные вещи, в том числе:
q, а затем использует это загруженное значение в качестве адреса для загрузки double. Это значение, загружаемое для q, может быть адресом, который не отображается в памяти вашего процесса, поэтому оборудование сигнализирует об ошибке памяти, и ваш процесс выходит из строя.q, но оно указывает на адрес, который находится в памяти вашего процесса, но он не выровнен, поэтому оборудование сигнализирует об ошибке выравнивания, и ваш процесс выходит из строя.double и печатает полученное значение.q не инициализирован и, следовательно, printf("*q = %lf", *q); не имеет смысла, поэтому он помечает его как код с неопределенным поведением и позволяет оптимизатору исключить его из семантического представления вашей программы. (Это может привести к исчезновению целых частей вашей программы из сгенерированного кода.)q не инициализирован, и печатает об этом диагностическое сообщение.Какое из этих или других событий произойдет, зависит от обстоятельств, в том числе от того, включена ли оптимизация в компиляторе, какие предупреждения в компиляторе включены, где ваша программа загружается в память, как компилятор размещает переменные в памяти и более. (В отличие от утверждения некоторых комментариев, утверждение «нельзя ожидать каких-либо последовательных результатов» неверно. То, что вы можете или не можете ожидать, зависит от обстоятельств. Стандарт C не определяет поведение, но это не означает, что поведение на него не влияют другие вещи, о которых вы можете узнать.)
Когда вы изменили программу, вы изменили некоторые из этих факторов, например, то, что оказалось в памяти, из которой программа пыталась загрузить значение q. Если вы перейдете на запуск этого в Linux вместо Windows, вы измените другие факторы, и поэтому результаты могут (и, вероятно, будут) отличаться от системы к системе.
Это неопределенное поведение, вы не можете ожидать каких-либо последовательных результатов.