В C/C++ функция main получает параметры типа char*
.
int main(int argc, char* argv[]){
return 0;
}
argv
представляет собой массив char*
и указывает на строки. Где находятся эти строки? Находятся ли они в куче, стеке или где-то еще?
В C/C++ функция main получает параметры типа char*
.
int main(int argc, char* argv[]){
return 0;
}
argv
представляет собой массив char*
и указывает на строки. Где находятся эти строки? Находятся ли они в куче, стеке или где-то еще?
На самом деле это комбинация зависимости компилятора и зависимости от операционной системы. main()
— это функция, такая же, как и любая другая функция C, поэтому расположение двух параметров argc
и argv
будет соответствовать стандарту для компилятора на платформе. например для большинства компиляторов C, ориентированных на x86, они будут в стеке сразу над адресом возврата и сохраненным базовым указателем (помните, стек растет вниз). В x86_64 параметры передаются в регистрах, поэтому argc
будет в %edi
, а argv
будет в %rsi
. Код в основной функции, сгенерированный компилятором, затем копирует их в стек, и именно на это указывают последующие ссылки. Это сделано для того, чтобы регистры можно было использовать для вызовов функций из main
.
Блок char*
s, на который указывает argv, и фактические последовательности символов могут быть где угодно. Они будут запускаться в некотором месте, определенном операционной системой, и могут быть скопированы кодом преамбулы, который генерирует компоновщик, в стек или куда-либо еще. Чтобы это выяснить, вам придется просмотреть код для exec()
и преамбулу ассемблера, сгенерированную компоновщиком.
main
также не похожа ни на одну другую функцию в C. По крайней мере, C99 также позволяет опустить оператор return
и тогда четко определяет, что происходит.
- person Jens Gustedt; 16.11.2010
main()
такая же, как и любая другая функция, даже если другие специальные семантики также определены соответствующими стандартами.
- person JeremyP; 17.11.2010
Это магия компилятора и зависит от реализации.
Вот что говорит стандарт C (n1256) :
5.1.2.2.1 Program startup
...
2 If they are declared, the parameters to the main function shall obey the following constraints:
- The value of argc shall be nonnegative.
- argv[argc] shall be a null pointer.
- If the value of argc is greater than zero, the array members argv[0] through argv[argc-1] inclusive shall contain pointers to strings, which are given implementation-defined values by the host environment prior to program startup. The intent is to supply to the program information determined prior to program startup from elsewhere in the hosted environment. If the host environment is not capable of supplying strings with letters in both uppercase and lowercase, the implementation shall ensure that the strings are received in lowercase.
- If the value of argc is greater than zero, the string pointed to by argv[0] represents the program name; argv[0][0] shall be the null character if the program name is not available from the host environment. If the value of argc is greater than one, the strings pointed to by argv[1] through argv[argc-1] represent the program parameters.
- The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination.
Последняя пуля наиболее интересна тем, где хранятся строковые значения. Он не определяет кучу или стек, но требует, чтобы строки были доступны для записи и имели статический размер, что накладывает некоторые ограничения на то, где может быть расположено содержимое строки. Как уже говорили другие, точные детали будут зависеть от реализации.
argv
(аргумент указателя на указатель) и строки, на которые указывают, изменяются, стандарт, похоже, не указывает, что массив указателей можно изменить. Как следствие, использование GNU getopt
(с его перестановкой argv
) в системе, где явно не разрешено изменять массив argv
, скорее всего, будет неопределённым поведением.
- person R.. GitHub STOP HELPING ICE; 16.11.2010
char** argv
, а не char*const* argv
, можно подумать, что ее можно изменить. Это отличается от ситуации со строковыми литералами, где стандарт явно указывает, что их изменение является UB.
- person Jens Gustedt; 16.11.2010
Ответ на этот вопрос зависит от компилятора. Это означает, что это не рассматривается в стандарте C, поэтому любой может реализовать это по своему усмотрению. Это нормально, так как операционные системы также не имеют общепринятого стандартного способа запуска процессов и их завершения.
Давайте представим простой сценарий, почему бы и нет.
Процесс каким-то механизмом получает аргументы, записанные в командной строке. argc — это просто int, который помещается в стек функцией начальной загрузки, которую компилятор помещает в качестве точки входа для процесса программы (часть среды выполнения). Фактические значения получаются из операционной системы и могут быть, скажем, записаны в блок памяти кучи. Затем строится вектор argv, и адрес его первой позиции также помещается в стек.
Затем вызывается функция main(), которая должна быть предоставлена программистом, и ее возвращаемое значение сохраняется для последующего (почти немедленного) использования. Структуры в куче освобождаются, а код выхода, полученный для main, экспортируется в операционную систему. Процесс завершается.
Эти параметры ничем не отличаются от параметров любой другой функции. Если последовательность вызовов архитектуры требует, чтобы параметры проходили через стек, они находятся в стеке. Если, например, на x86-64 некоторые параметры идут в регистры, то они тоже идут в регистры.
argc
и argv
, но я думаю, что вопрос больше касается argv[0]
и друзей.
- person aschepler; 16.11.2010
Как упоминает pmg
, когда main
вызывается рекурсивно, вызывающая сторона определяет, на что указывают аргументы. В основном ответ тот же, что и при первоначальном вызове main
, за исключением того, что «вызывающий» — это реализация/ОС C.
В системах UNIX-y строки, на которые указывает argv
, сами указатели argv
и начальные переменные среды процесса почти всегда хранятся на самом верху стека.
Как указывают многие другие ответы здесь, точный механизм, который реализация компилятора использует для передачи аргументов в main, не указан стандартом (как и механизм, который компилятор использует для передачи любых аргументов в функцию). Строго говоря, компилятору даже не нужно передавать ничего полезного в этих параметрах, поскольку значения определяются реализацией. Но ни один из них не является особенно полезным ответом.
Типичная программа на C (или C++) компилируется для так называемой "размещенной" среды выполнения (использование функции main()
в качестве отправной точки вашей программы является одним из требований к размещенной среде). Главное, что нужно знать, это то, что компилятор упорядочивает все так, что когда исполняемый файл запускается операционной системой, изначально управление получает среда выполнения компилятора, а не функция main()
. Код инициализации среды выполнения выполняет любую необходимую инициализацию, включая выделение памяти для аргументов main()
, а затем передает управление main()
.
Память для аргументов main()
может поступать из кучи, может быть выделена в стеке (возможно, с использованием методов, недоступных для стандартного кода C), или может использоваться статически выделенная память, хотя это менее вероятный вариант, поскольку он менее гибкий. Стандарт требует, чтобы память, используемая для строк, на которые указывает argv
, была поддающейся изменению, и чтобы изменения, внесенные в эти строки, сохранялись на протяжении всего жизненного цикла программы.
Просто имейте в виду, что до того, как выполнение достигнет main()
, уже было выполнено довольно много кода, который настраивает среду для запуска вашей программы.
Список аргументов является частью среды процесса, аналогичной переменным среды (но отличным от них).
Обычно неизвестно, где они.
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char **foo;
char *bar[] = {"foo", "bar"};
(void)argv; /* avoid unused argv warning */
foo = malloc(sizeof *foo);
foo[0] = malloc(42);
strcpy(foo[0], "forty two");
/* where is foo located? stack? heap? somewhere else? */
if (argc != 42) main(42, foo); else return 0;
/* where is bar located? stack? heap? somewhere else? */
if (argc != 43) main(43, bar); else return 0;
/* except for the fact that bar elements
** point to unmodifiable strings
** this call to main is perfectably reasonable */
return 0;
/* please ignore memory leaks, thank you */
}
Хотя вы можете получить доступ к фактическим параметрам, я думаю, что их фактическое местоположение не имеет никакого значения.