Как вывести значение указателя без иннициализации?
Есть задание - узнать, что будет, если вывести на экран значения разыменованных указателей без инициализации, и что будет если записать значения по адресу этих указателей
Visual Studio очевидно выдает ошибку, так как переменные неинициализированны
Код ниже, по словам препода должны выводиться значения, но я уже не знаю, что сделать, чтобы увидеть ответ
#include <stdio.h>
int main(){
int *ptr0, *ptr1;
printf("%d %d\n",ptr0,ptr1);
*ptr0= 21;
printf("%d %d ",ptr0,ptr1);
return 0;
}
Взять компилятор другой/постарше, крутить опции компилятора на предмет отключения проверок.
Чтобы не ставить к себе можно попробовать онлайн компиляторы.
Хорошо бы увидеть оригинальный текст задания, а не ваш пересказ.
Вопрос по заданию:
"без инициализации" относится к указателям или к памяти на которую ссылаются указатели?
В обоих случаях ваша реализация не корректна.
Но если речь о не инициализированных указателях, то можно ничего не писать, т.к. результат изначально ясен:
Если указатели (да и любые автоматические переменные) не инициализировать, то в них будет содержаться какой-то мусор, который когда-то был записан по этим адресам памяти.
Если обратится к памяти на которую указывает такой "мусорный" указатель то в любых современных ОС, где есть виртуальная память, вы получите ошибку. В линуксе это будет SIGFAULT, в винде Access Denied, программа аварийно завершится на попытке доступа к не выделенной памяти.
Иногда может и не быть ошибок, если вдруг, совершенно случайно, мусор в указателе образует какой-то адрес, который уже был выделен процессу. Тогда вы прочитаете оттуда что-нибудь. Но шансы на такой исход крайне не велики. Но если даже каким-то чудом это случится и вы успешно прочитаете значение, то после записи новых значений с программой может происходить ВСЕ ЧТО УГОДНО (UB), т.к. вы не знаете куда вы записали это значение, какие процессы используют это значение (ведь та память для чего-то была выделена), что должно реально находится по этим адресам и т.д. и т.п.
И это при том, что надо будет еще побороть ошибки на этапе компиляции, т.к. компилятор то же следит за тем, чтоб вы не обращались к не инициализированным переменным.
Если указатели (да и любые автоматические переменные) не инициализировать, то в них будет содержаться какой-то мусор, который когда-то был записан по этим адресам памяти.
я всегда думал что это зависит от ОС, и в последнее время на Windows 11 никакого мусора там не видел.
printf("%d %d\n",ptr0,ptr1);
Только в этой строчке выводится не значение разыменованного указателя, а значение, если размер целого и указателя на платформе совпадает, самого указателя (адрес, который содержит указатель). В общем-то ничего страшного, но корректнее printf("%p %p\n",ptr0,ptr1);
А если именно разыменованного, то printf("%d %d\n", *ptr0, *ptr1);
А вот тут уже вероятнее всего программа упадет. Потому что попробует читать по неинициализированному указателю со случайного адреса.
Эх, затейник преподаватель :) Но веселее все это делать под DOS'ом или на микроконтроллере :)
всегда думал что это зависит от ОС, и в последнее время на Windows 11 никакого мусора там не видел.
От ОС не зависит.
Автоматические переменные (ваш случай) хранятся в стеке. Стек постоянно используемая вашим приложением область памяти, выделяется 1 раз при старте приложения и освобождается (весь стек) при выходе из приложения. Да, первоначально при выделении стека, возможно, память под ним обнуляется (тут я точно не знаю). Но по мере использования освобождение стековых переменных не приводит к обнулению памяти занимаемой переменной. Таким образом новые переменные, которые будут занимать место в памяти там же где были предыдущие (освобожденные) переменные будут иметь первоначальное значение то, что осталось от предыдущих переменных.
В вашей примитивной программе, возможно, вы будете всегда получать в переменных нули. Но стоит сделать программу чуть сложнее, например, функционал выделить в отдельную функцию, а в main вызывать эту функцию в цикле несколько раз. Можно будет увидеть, что переменные в функции без инициализации будут содержать мусор, точнее в этом примере будут содержать значения оставшиеся от предыдущего запуска функции.
Реальные программы содержат десятки (сотни/тысячи) функций и их вызовов, поэтому ваш пример не показателен в этом отношении и на нем не стоит делать далеко идущих выводов.
И еще момент. Поведение может зависеть от того собрана ли программа с отладочной информацией или нет. В случае наличия отладочной информации поведение может зависеть от компилятора. Например некоторые компиляторы могут принудительно вставлять код очистки при выходе из функций или например проверку на выход за границы памяти и т.п. Очень частая ситуация, когда в отладочном режиме программа вроде бы работает, а в релизном не работает. И это является показателем наличия в программе ошибок, которые в отладочном режиме не проявляются, а в релизе проявляются, из-за особенностей данного конкретного компилятора при сборке программы в отладочном режиме. С этими ошибками надо бороться.
Для правильного вопроса надо знать половину ответа
Первый printf должен выдать адреса, которые находятся в ptr1 и ptr2.
До второго printf скорее всего дело не дойдёт, будет выдана ошибка доступа к чужой памяти.
Но, в системах, где нет контроля доступа, например в DOS, значение 21 будет записано по случайному адресу и дальнейшее поведение как программы, так и системы непредсказуемо.
Если запускаешь в дебаге, то указатели инициализированы нулями, то есть и в консоль будут выведены нули и при попытке туда записать значение будет падение программы - запрещено записывать в адреса от 0 до какого-то там значения зарезервированного. Возможно, преподователь хочет услышать именно то, что программа падает.
В релизе в указателе может быть любой набор битов и там уже можно считать везением, если программа сразу упадёт из-за ошибки доступа к памяти.
В целом, это UB и так делать не надо.
Ну, поехали. int *ptr0, *ptr1;
Обьявили два указателя на int. Поскольку переменные на куче, там может быть все, что угодно. printf("%d %d\n",ptr0,ptr1);
Указатели так не печатают. Ты получишь бессмысленное число, которое не использовать нигде. Обычно указатели печатают так: printf("%0X %0X\n", ptr0, ptr1); *ptr0= 21;
Дальше этого оператора программа работать не будет - в этом месте в 99% возникнет прерывание защиты памяти - то есть попытка обращения к области памяти, которая не принадлежит программе. При этом как правило возникает SIGSEGV и все. Если программа выполняется в однозадачной среде, где нет защиты памяти - такой оператор приведет к зависанию, поскольку в нижних адресах обычно расположены системные таблицы.