Где параметры основной функции C/C++?

В C/C++ функция main получает параметры типа char*.

int main(int argc, char* argv[]){
  return 0;
}

argv представляет собой массив char* и указывает на строки. Где находятся эти строки? Находятся ли они в куче, стеке или где-то еще?


person remainn    schedule 16.11.2010    source источник
comment
Я не получаю отрицательных голосов. Нет голосов, чтобы закрыть, и это кажется совершенно разумным вопросом. Downvoters, пожалуйста, epxlain.   -  person John Dibling    schedule 16.11.2010
comment
Вероятно, отрицательные отзывы предназначены для того, чтобы задать вопрос, относящийся к реализации, без упоминания реализации. FWIW Я не один из минусовщиков.   -  person R.. GitHub STOP HELPING ICE    schedule 16.11.2010
comment
@R ..: Как узнать, что это зависит от реализации, пока не зададут вопрос и не получат ответ, в котором говорится, что это зависит от реализации?   -  person Fred Larson    schedule 16.11.2010
comment
@Fred: Вот почему я не присоединился к минусам. :-)   -  person R.. GitHub STOP HELPING ICE    schedule 16.11.2010
comment
Мне любопытно, почему вы хотите знать, где они. Праздное любопытство или какая-то веская причина?   -  person Lee-Man    schedule 16.11.2010


Ответы (10)


На самом деле это комбинация зависимости компилятора и зависимости от операционной системы. main() — это функция, такая же, как и любая другая функция C, поэтому расположение двух параметров argc и argv будет соответствовать стандарту для компилятора на платформе. например для большинства компиляторов C, ориентированных на x86, они будут в стеке сразу над адресом возврата и сохраненным базовым указателем (помните, стек растет вниз). В x86_64 параметры передаются в регистрах, поэтому argc будет в %edi, а argv будет в %rsi. Код в основной функции, сгенерированный компилятором, затем копирует их в стек, и именно на это указывают последующие ссылки. Это сделано для того, чтобы регистры можно было использовать для вызовов функций из main.

Блок char*s, на который указывает argv, и фактические последовательности символов могут быть где угодно. Они будут запускаться в некотором месте, определенном операционной системой, и могут быть скопированы кодом преамбулы, который генерирует компоновщик, в стек или куда-либо еще. Чтобы это выяснить, вам придется просмотреть код для exec() и преамбулу ассемблера, сгенерированную компоновщиком.

person JeremyP    schedule 16.11.2010
comment
main() - это функция, такая же, как и любая другая функция C. Не в C++, ее вызов из другой функции незаконен, и хотя она объявлена ​​как возвращающая int, на самом деле вам не нужно ничего возвращать. - person jcoder; 16.11.2010
comment
@John, @JeremyP, main также не похожа ни на одну другую функцию в C. По крайней мере, C99 также позволяет опустить оператор return и тогда четко определяет, что происходит. - person Jens Gustedt; 16.11.2010
comment
@John, @Jens, с точки зрения способа передачи аргументов main() такая же, как и любая другая функция, даже если другие специальные семантики также определены соответствующими стандартами. - person JeremyP; 17.11.2010
comment
Я не уверен, что они должны быть таковыми. Их вообще нельзя было передать в main, и компилятор мог вставить код в начало main, чтобы получить их. Поскольку вы не можете сами вызвать main, он может это сделать, и вы никогда не узнаете. Хотя на практике наверное нет - person jcoder; 17.11.2010

Это магия компилятора и зависит от реализации.

person Stu    schedule 16.11.2010
comment
+1: это почти так же близко к не очень подробному ответу, который вы получите... - person rubenvb; 16.11.2010
comment
Должен любить это, как кажется, что SO всегда поддерживает остроумный ответ, а не те, которые действительно предоставляют полезную информацию, фон или примеры. - person mkataja; 02.07.2013
comment
Ах, пожалуйста, не принимайте это на свой счет, я действительно не хотел оскорбить вас или ваш ответ. Я думаю, мне следовало сформулировать это более тщательно в моем предыдущем комментарии - извините за это. Мне просто было интересно, почему такие ответы, как правило, получают наибольшее количество голосов вместо более полных (и часто более полезных) ответов, объясняющих ситуацию более подробно, даже если полное объяснение невозможно, как здесь. - person mkataja; 12.07.2013
comment
Справедливо. Я могу дать вам свой личный ответ на это: во многих случаях правильный ответ (например, Джон Боуд, ниже) заставляет глаза среднего спрашивающего остекленеть - жестко. Мой первоначальный ответ был бы таким: какого черта ты хочешь знать?, но, похоже, это никогда не сработает — так что это мой компромисс. И для многих людей это, кажется, работает просто отлично. - person Stu; 25.07.2013
comment
Такие рассуждения могут быть длинными (почти не влезут в Комментарий). Например, для меня 1. ссылка, которая почти утверждает, что разработчики C++ написали бы public static void main(String[] args). 2. Я: не имеет значения для этой информации + мои знания С++, ограниченные g++ -std=c++11, не смогут скомпилироваться (требуется char **) -> заставили меня найти link 3. Я: Будет ли нехватка памяти для аргументов CLI вести себя так же, если нет mem для char * * param, vs std::vector распределение? - person johan mårtensson; 15.11.2017
comment
-1 за недостаточно конкретную информацию. Любая достаточно продвинутая технология рассматривается как магия тем, кто в ней не разбирается или не имеет терпения вникать в детали. Это ваша работа как ответчика объяснить нам эти детали. - person Galaxy; 29.12.2018

Вот что говорит стандарт 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.

Последняя пуля наиболее интересна тем, где хранятся строковые значения. Он не определяет кучу или стек, но требует, чтобы строки были доступны для записи и имели статический размер, что накладывает некоторые ограничения на то, где может быть расположено содержимое строки. Как уже говорили другие, точные детали будут зависеть от реализации.

person John Bode    schedule 16.11.2010
comment
Интересная вещь, которую я никогда не замечал... в то время как argv (аргумент указателя на указатель) и строки, на которые указывают, изменяются, стандарт, похоже, не указывает, что массив указателей можно изменить. Как следствие, использование GNU getopt (с его перестановкой argv) в системе, где явно не разрешено изменять массив argv, скорее всего, будет неопределённым поведением. - person R.. GitHub STOP HELPING ICE; 16.11.2010
comment
@R: Это просто не упоминается, и поэтому в стандарте не указано, что произойдет, когда вы его измените, да. Но это не означает, что это UB, а только то, что это зависит от реализации. И поскольку спецификация char** argv, а не char*const* argv, можно подумать, что ее можно изменить. Это отличается от ситуации со строковыми литералами, где стандарт явно указывает, что их изменение является UB. - person Jens Gustedt; 16.11.2010

Ответ на этот вопрос зависит от компилятора. Это означает, что это не рассматривается в стандарте C, поэтому любой может реализовать это по своему усмотрению. Это нормально, так как операционные системы также не имеют общепринятого стандартного способа запуска процессов и их завершения.

Давайте представим простой сценарий, почему бы и нет.

Процесс каким-то механизмом получает аргументы, записанные в командной строке. argc — это просто int, который помещается в стек функцией начальной загрузки, которую компилятор помещает в качестве точки входа для процесса программы (часть среды выполнения). Фактические значения получаются из операционной системы и могут быть, скажем, записаны в блок памяти кучи. Затем строится вектор argv, и адрес его первой позиции также помещается в стек.

Затем вызывается функция main(), которая должна быть предоставлена ​​программистом, и ее возвращаемое значение сохраняется для последующего (почти немедленного) использования. Структуры в куче освобождаются, а код выхода, полученный для main, экспортируется в операционную систему. Процесс завершается.

person Baltasarq    schedule 16.11.2010

Эти параметры ничем не отличаются от параметров любой другой функции. Если последовательность вызовов архитектуры требует, чтобы параметры проходили через стек, они находятся в стеке. Если, например, на x86-64 некоторые параметры идут в регистры, то они тоже идут в регистры.

person horsh    schedule 16.11.2010
comment
Не уверен, что это обязательно верно в С++. Вы не можете вызывать main как обычную функцию в С++, в отличие от C, и поэтому компилятор может по-разному организовать передачу параметров, если он хочет. - person jcoder; 16.11.2010
comment
Однако строки не являются параметрами, параметр является указателем на массив указателей на строки. - person Mark Ransom; 16.11.2010
comment
Вероятно, это верно для самих argc и argv, но я думаю, что вопрос больше касается argv[0] и друзей. - person aschepler; 16.11.2010

Как упоминает pmg, когда main вызывается рекурсивно, вызывающая сторона определяет, на что указывают аргументы. В основном ответ тот же, что и при первоначальном вызове main, за исключением того, что «вызывающий» — это реализация/ОС C.

В системах UNIX-y строки, на которые указывает argv, сами указатели argv и начальные переменные среды процесса почти всегда хранятся на самом верху стека.

person R.. GitHub STOP HELPING ICE    schedule 16.11.2010
comment
+1 за реальный ответ, хотя, конечно, частичный. + это относится к freeBSD/gcc. - person log0; 16.11.2010

Как указывают многие другие ответы здесь, точный механизм, который реализация компилятора использует для передачи аргументов в main, не указан стандартом (как и механизм, который компилятор использует для передачи любых аргументов в функцию). Строго говоря, компилятору даже не нужно передавать ничего полезного в этих параметрах, поскольку значения определяются реализацией. Но ни один из них не является особенно полезным ответом.

Типичная программа на C (или C++) компилируется для так называемой "размещенной" среды выполнения (использование функции main() в качестве отправной точки вашей программы является одним из требований к размещенной среде). Главное, что нужно знать, это то, что компилятор упорядочивает все так, что когда исполняемый файл запускается операционной системой, изначально управление получает среда выполнения компилятора, а не функция main(). Код инициализации среды выполнения выполняет любую необходимую инициализацию, включая выделение памяти для аргументов main(), а затем передает управление main().

Память для аргументов main() может поступать из кучи, может быть выделена в стеке (возможно, с использованием методов, недоступных для стандартного кода C), или может использоваться статически выделенная память, хотя это менее вероятный вариант, поскольку он менее гибкий. Стандарт требует, чтобы память, используемая для строк, на которые указывает argv, была поддающейся изменению, и чтобы изменения, внесенные в эти строки, сохранялись на протяжении всего жизненного цикла программы.

Просто имейте в виду, что до того, как выполнение достигнет main(), уже было выполнено довольно много кода, который настраивает среду для запуска вашей программы.

person Michael Burr    schedule 16.11.2010

Список аргументов является частью среды процесса, аналогичной переменным среды (но отличным от них).

person Chris    schedule 16.11.2010
comment
Не совсем. Стандарт C не знает слова процесс. (Хотя это касается многих реализаций C) - person Billy ONeal; 16.11.2010

Обычно неизвестно, где они.

#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 */
}
person pmg    schedule 16.11.2010

Хотя вы можете получить доступ к фактическим параметрам, я думаю, что их фактическое местоположение не имеет никакого значения.

person ebasconp    schedule 16.11.2010