В C/С++ основная функция получает параметры типа char*
.
int main(int argc, char* argv[]){
return 0;
}
argv
- это массив из char*
и указывает на строки. Где находятся эти строки? Являются ли они в куче, стеке или где-то еще?
В C/С++ основная функция получает параметры типа 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*
, на который указывает argv, и фактические последовательности символов могут быть в любом месте. Они будут запускаться в определенной операционной системе и могут быть скопированы с помощью кода pre-amble, который компоновщик генерирует в стек или где-то еще. Вам нужно будет посмотреть код для exec()
и ассемблер pre-amble, сгенерированный компоновщиком, чтобы узнать.
Это магия компилятора и зависящая от реализации.
Здесь в стандарте C (n1256) говорится:
5.1.2.2.1 Запуск программы
2 Если они объявлены, параметры функции main должны подчиняться следующим Ограничения:
<уль > Значение argc должно быть неотрицательным. argv [argc] должен быть нулевым указателем.
Если значение argc больше нуля, элементы массива argv [0] через argv [argc-1] содержит указатели на строки, которые указаны определяемые реализацией значения среды хоста перед запуском программы. Цель состоит в том, чтобы предоставить информацию о программе, определенную до запуска программы из другого места в размещенной среде. Если хост-среда не способна снабжая строки буквами в верхнем и нижнем регистре, реализация должен гарантировать, что строки получены в нижнем регистре.
Если значение argc больше нуля, строка, на которую указывает argv [0] представляет собой имя программы; argv [0] [0] должен быть нулевым символом, если имя программы недоступно из среды хоста. Если значение argc равно больше единицы, строки, на которые указывает argv [1] через argv [argc-1] представляют программные параметры .
Параметры argc и argv, а строки, на которые указывает массив argv, должны могут быть модифицированы программой и сохраняют свои последние сохраненные значения между программой запуск и завершение программы.
Последняя пуля является наиболее интересной, когда хранятся строковые значения. Он не указывает кучу или стек, но для этого требуется, чтобы строки были доступны для записи и имели статическую степень, которая ограничивает пределы содержимого строк. Как говорили другие, точные детали будут зависеть от реализации.
Ответ на этот вопрос зависит от компилятора. Это означает, что он не рассматривается в стандарте C, поэтому любой может реализовать это, как ему хотелось бы. Это нормально, так как и операционные системы не имеют общепринятого стандартного способа запуска процессов и их завершения.
Представьте себе простой сценарий, почему бы и нет.
Процесс получает некоторым механизмом аргументы, написанные в командной строке. argc - это просто int, который помещается в стек с помощью функции bootstrap, которую компилятор помещает в качестве точки входа для процесса программы (часть времени выполнения). Фактические значения получаются из операционной системы и могут быть, скажем, записаны в блоке памяти кучи. Затем вектор argv строится и адрес в его первое положение также помещается в стек.
Затем вызывается функция main(), которую должен предоставить программист, и ее возвращаемое значение сохраняется для более позднего (почти промежуточного) использования. Структуры в куче освобождаются, и код выхода, полученный для основного, экспортируется в операционную систему. Процесс завершается.
Эти параметры не отличаются от любых других параметров функции. Если последовательность вызова архитектуры требует, чтобы параметры проходили через стек, они находятся в стеке. Если, например, на x86-64, некоторые параметры входят в регистры, они также поступают в регистры.
Как и многие другие ответы здесь, точный механизм реализации компилятора для передачи аргументов main не указан стандартом (как и механизм, используемый компилятором для передачи любых аргументов функции). Строго говоря, компилятор не должен даже передавать ничего полезного в этих параметрах, так как значения определяются реализацией. Но ни один из них не является особенно полезным ответом.
Типичная программа 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 */
}
В качестве pmg
упоминается, когда main
вызывается рекурсивно, это до вызывающего объекта, на который указывают аргументы. В основном ответ одинаков при исходном вызове main
, за исключением того, что "вызывающий" является реализацией C/OS.
В системах UNIX-y строки, на которые указывает argv
, указатели argv
сами, а начальные переменные среды процесса почти всегда хранятся в самой верхней части стека.
Пока вы можете получить доступ к фактическим параметрам, я думаю, что их фактическое местоположение вообще не имеет значения.