Любая локальная переменная, которая не инициализирована, содержит неопределенное значение. Независимо от типа. Здесь нет очевидной разницы между, например, неинициализированными int
, int*
или int**
.
Однако в C есть правило, согласно которому, если вы не получаете доступ к адресу такого неинициализированная локальная переменная, но используя ее значение, вы вызываете неопределенное поведение - что означает ошибку, возможно, cra sh et c. Обоснование вероятно, что такие переменные могут быть размещены в регистрах и не иметь адресуемой области памяти. Подробнее см. { ссылка }.
Итак, все эти примеры ниже плохие и неправильные, поскольку адреса самих локальных переменных никогда не используются:
{
int i;
int* ip;
int** ipp;
printf("%d\n, i); // undefined behavior
printf("%p\n, (void*)ip); // undefined behavior
printf("%p\n, (void*)ipp); // undefined behavior
}
Однако, если вы берете где-нибудь адрес переменной, C будет менее строгим. В таком случае вы получите переменную неопределенное значение , что означает, что она может содержать что угодно, и значение может быть несовместимым, если вы обращаетесь к нему несколько раз. Это может быть «случайный адрес» в случае указателей, но не обязательно.
Неопределенное значение может быть так называемым «представлением ловушки», запрещенной двоичной последовательностью для этого типа. В таких случаях доступ к переменной (чтение или запись) вызывает неопределенное поведение. Это вряд ли произойдет для простого int
, если только у вас нет очень экзотической системы c, в которой не используется дополнение 2, потому что в стандартных системах дополнения 2 все комбинации значений int
действительны, и нет биты заполнения, отрицательный ноль и т. д. c.
Пример (при условии дополнения до 2):
{
int i;
int* ip = &i;
printf("%d\n", *ip); // unspecified behavior, might print anything
}
Неопределенное поведение означает, что компилятору не нужно документировать поведение. Вы можете получить любой результат, и он не обязательно должен быть последовательным. Но, по крайней мере, программа не будет взламывать sh и сгорать, как это могло бы произойти в случае неопределенного поведения .
Но представления ловушек, скорее всего, относятся к переменным-указателям. Специфический c CPU может иметь ограниченное адресное пространство или при инициализации низкого уровня MMU может быть настроен на то, чтобы определенные регионы были виртуальными, некоторые регионы содержали только данные или некоторые регионы содержали только код et c. Возможно, такой ЦП генерирует аппаратное исключение, даже когда вы считываете недопустимое значение адреса в индексный регистр. Очень вероятно, что это произойдет, если вы попытаетесь получить доступ к памяти через недопустимый адрес.
Например, MMU может заблокировать неконтролируемый код, который пытается выполнить код из сегмента данных в памяти, или доступ к содержимому памяти кода, как к данным.