簡體   English   中英

C / C ++主函數的參數在哪里?

[英]Where are C/C++ main function's parameters?

在C / C ++中,main函數接收char*類型的參數。

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

argv是一個char*數組,指向字符串。 這些字符串在哪里? 它們是堆,堆棧還是其他地方?

它們是編譯器魔術,並且依賴於實現。

這是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的值大於1,則argv [1]通過argv [argc-1]指向的字符串表示

  • 參數argcargv以及argv數組指向的字符串應該可由程序修改,並在程序啟動和程序終止之間保留它們最后存儲的值。

最后一個項目符號是存儲字符串值的最有趣的文件。 它沒有指定堆或堆棧,但它確實要求字符串是可寫的並且具有靜態范圍,這對字符串內容的位置設置了一些限制。 正如其他人所說,具體細節將取決於實施情況。

它實際上是編譯器依賴和操作系統依賴的組合。 main()是一個函數,就像任何其他C函數一樣,因此兩個參數argcargv將遵循平台上編譯器的標准。 例如,對於大多數針對x86的C編譯器,它們將位於返回地址上方的堆棧上以及保存的基指針(堆棧向下增長,請記住)。 在x86_64上,參數在寄存器中傳遞,因此argc將在%ediargv將在%rsi 編譯器生成的main函數中的代碼然后將它們復制到堆棧中,這是后面引用指向的位置。 這樣寄存器就可以用於main函數調用。

argv指向的char* s塊和實際的字符序列可以在任何地方。 它們將在某個操作系統定義的位置啟動,並且可以通過鏈接器生成到堆棧或其他位置的前導碼復制。 您將不得不查看exec()的代碼和鏈接器生成的匯編器pre-amble以查找。

這個問題的答案是編譯器依賴的。 這意味着它沒有在C標准中處理,因此任何人都可以按照他或她的意願實現。 這是正常的,因為操作系統也沒有通用的標准方法來啟動進程並完成它們。

讓我們想象一個簡單的,為什么不是場景。

該進程通過某種機制接收在命令行中寫入的參數。 然后argc只是一個int,它被編譯器作為程序進程(運行時的一部分)的入口點的引導函數推送到堆棧。 實際值是從操作系統獲得的,並且可以寫在堆的內存塊中。 然后構建argv向量,並將其第一個位置的地址也推入堆棧。

然后調用必須由程序員提供的函數main(),並保存其返回值以供稍后(幾乎中間)使用。 釋放堆中的結構,並將為main獲取的退出代碼導出到操作系統。 該過程結束。

這些參數與任何其他函數的參數沒有什么不同。 如果體系結構的調用序列需要參數通過堆棧,則它們處於堆棧狀態。 如果,像on,x86-64一些參數進入寄存器,這些參數也進入寄存器。

正如許多其他答案所指出的那樣,標准未指定編譯器實現用於將參數傳遞給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 */
}

正如pmg提到的那樣,當main被遞歸調用時,它取決於參數所指向的調用者。 基本上,在main的原始調用中答案是相同的,除了“調用者”是C實現/ OS。

在UNIX-Y系統,該字符串argv點時, argv指針本身,以及該進程的初始環境變量幾乎總是存儲在堆棧的最頂端。

雖然您可以訪問實際參數,但我認為它們的實際位置根本不重要。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM