Подумав немного, я могу с некоторой долей уверенности сказать, почему 2-й пример не рухнул.
Когда программа выполняется, crt (c runtime) помещает в стек 3 значения: количество аргументов (int
), аргументы как char **
и строки среды также как char **
, затем звонит main
.
Теперь, когда вы пишете свою функцию main
, насколько я знаю, она всегда читает первые 2 значения и передает их в аргументы функции, если они есть.Если вы включите третий аргумент, он также передаст третье значение, иначе он останется в стеке.Таким образом, стек в начале программы выглядит следующим образом:
+--------+
| # args |
+--------+
| args |
+--------+ <------ stack pointer
| envs |
+--------+
В первом примере вы выделяете int
и структуру в стеке, затем указатель, поэтому полный стек вПервый пример выглядит так:
+--------+
| # args |
+--------+
| args |
+--------+ <------ stack pointer
| arg | <------ your integer, initialized in code
+--------+
| ob | <------ your struct, initialized in code
+--------+
| sample | <------ your pointer, uninitalized = garbage
+--------+
Так что sample
- это чистый мусор, и попытка разыменования приводит к сбою вашей программы.
Теперь во втором примере стек выглядит так:
+--------+
| # args |
+--------+
| args |
+--------+ <------ stack pointer
| sample | <------ pointer, uninitalized (!)
+--------+
Указатель по-прежнему не инициализирован, но значение, которое он перезаписал, равно envp
, что является фактическим указателем на массив: char **
.Когда вы разыменовываете его, вы возвращаете массив «указателей на символ», так что вы можете перезаписать его безопасно (при условии, что вы больше не пытаетесь рассматривать его как исходный указатель).Память распределена, и вы можете ее использовать.
Теперь это, конечно, сильно зависит от реализации, но, похоже, подходит вашему компилятору, верно?С помощью gdb
вы можете проверить, действительно ли (char**)sample
указывает на массив переменных среды, прежде чем перезаписать его.
Снимок экрана из MSVC ++ 10 в 32-разрядном режиме выпуска (режим отладки принудительно инициализирует все переменные, чтобы предотвратитьтакие вещи):
указатель в действии http://img651.imageshack.us/img651/5918/69916340.png