C語言中沒有main函式生成可執行程式的幾種方法
轉自:http://www.linuxidc.com/Linux/2013-09/90061.htm
1、define預處理指令
這種方式很簡單,只是簡單地將main字串用巨集來代替,或者使用##拼接字串。示例程式如下:
#include <stdio.h>
#define begin main
int begin(void)
{
printf("Hello, World!\n");
return 0;
}
#include <stdio.h>
#define begin m##a##i##n
int begin(void)
{
printf("Hello, World!\n");
return 0;
}
嚴格來說,這種方式只算是一種技巧......
2、_start函式
_start函式是C程式的入口函式,會呼叫main函式。在呼叫main函式之前,會先執行_start函式分配必要的資源,然後再呼叫main函式。但是在用gcc編譯程式時可以使用-nostartfiles選項來重寫_start函式。示例程式如下:
#include <stdio.h>
#include <stdlib.h>
_start(void) {
printf("Hello, World!\n");
exit(0);
}
編譯上面的程式的命令為:
gcc -nostartfiles _start.c -o a.out
反彙編生成的可執行程式,如下所示:
a.out: file format elf64-x86-64
Disassembly of section .plt:
0000000000400320 <[email protected]>:
400320: ff 35 ea 01 20 00 pushq 0x2001ea(%rip) # 600510 <_GLOBAL_OFFSET_TABLE_+0x8>
400326: ff 25 ec 01 20 00 jmpq *0x2001ec(%rip) # 600518 <_GLOBAL_OFFSET_TABLE_+0x10>
40032c: 0f 1f 40 00 nopl 0x0(%rax)
0000000000400330 <[email protected]>:
400330: ff 25 ea 01 20 00 jmpq *0x2001ea(%rip) # 600520 <_GLOBAL_OFFSET_TABLE_+0x18>
400336: 68 00 00 00 00 pushq $0x0
40033b: e9 e0 ff ff ff jmpq 400320 <[email protected]>
0000000000400340 <[email protected]>:
400340: ff 25 e2 01 20 00 jmpq *0x2001e2(%rip) # 600528 <_GLOBAL_OFFSET_TABLE_+0x20>
400346: 68 01 00 00 00 pushq $0x1
40034b: e9 d0 ff ff ff jmpq 400320 <
Disassembly of section .text:
0000000000400350 <_start>:
400350: 55 push %rbp
400351: 48 89 e5 mov %rsp,%rbp
400354: bf 68 03 40 00 mov $0x400368,%edi
400359: e8 d2 ff ff ff callq 400330 <[email protected]>
40035e: bf 00 00 00 00 mov $0x0,%edi
400363: e8 d8 ff ff ff callq 400340 [email protected]
上面的結果是完整的反彙編結果,我們可以看到_start函式中只有我們呼叫printf和exit函式相關的一些指令,並且.txt段中只有_start函式,沒有看到main函式。如果將原始碼中的_start替換為main,重新編譯程式,反彙編的結果中會看到_start函式會呼叫到main。
另外還有一點需要注意,因為這裡重寫了_start函式,所以gcc為預設的main函式準備的清理動作就沒用上,所以如果退出的時候直接使用return,會導致程式崩潰。所以這裡要使用exit()來退出程式。具體的原因可以參見這篇文章。
3、gcc的-e選項
示例程式如下:
#include <stdio.h>
#include <stdlib.h>
int nomain(int i, int j, int k) {
printf("Hello, World!\n");
exit(0);
}
將上面的程式儲存為m.c,編譯命令如下所示:
gcc -nostartfiles -e nomain m.c -o a.out
繼續使用objdump反彙編生成的可執行程式,結果如下:
a.out: file format elf64-x86-64
Disassembly of section .plt:
0000000000400320 <[email protected]>:
400320: ff 35 f2 01 20 00 pushq 0x2001f2(%rip) # 600518 <_GLOBAL_OFFSET_TABLE_+0x8>
400326: ff 25 f4 01 20 00 jmpq *0x2001f4(%rip) # 600520 <_GLOBAL_OFFSET_TABLE_+0x10>
40032c: 0f 1f 40 00 nopl 0x0(%rax)
0000000000400330 <[email protected]>:
400330: ff 25 f2 01 20 00 jmpq *0x2001f2(%rip) # 600528 <_GLOBAL_OFFSET_TABLE_+0x18>
400336: 68 00 00 00 00 pushq $0x0
40033b: e9 e0 ff ff ff jmpq 400320 <[email protected]>
0000000000400340 <[email protected]>:
400340: ff 25 ea 01 20 00 jmpq *0x2001ea(%rip) # 600530 <_GLOBAL_OFFSET_TABLE_+0x20>
400346: 68 01 00 00 00 pushq $0x1
40034b: e9 d0 ff ff ff jmpq 400320 <[email protected]>
Disassembly of section .text:
0000000000400350 <nomain>:
400350: 55 push %rbp
400351: 48 89 e5 mov %rsp,%rbp
400354: 48 83 ec 10 sub $0x10,%rsp
400358: 89 7d fc mov %edi,-0x4(%rbp)
40035b: 89 75 f8 mov %esi,-0x8(%rbp)
40035e: 89 55 f4 mov %edx,-0xc(%rbp)
400361: bf 75 03 40 00 mov $0x400375,%edi
400366: e8 c5 ff ff ff callq 400330 <[email protected]>
40036b: bf 00 00 00 00 mov $0x0,%edi
400370: e8 cb ff ff ff callq 400340 <[email protected]>
從上面我們可以看到指定的nomain函式位於.text段的開始位置,同樣在函式結束的時候沒有gcc為main函式準備的清理動作,所以在這裡也只能使用exit()來退出程式,而不能使用return。
4、nostartfiles選項
前面已經多次使用了該選項,不過都是配合其他選項使用的,這個選項也可以單獨使用,其含義為"Do not use the standard system startup files when linking"。
示例程式如下:
#include <stdio.h>
#include <stdlib.h>
void func() {
printf("I am func....\n");
}
int nomain1(int i, int j, int k) {
func();
printf("%s: Hello, World!\n", __func__);
exit(0);
}
上面的程式儲存為k.c,然後使用下面的命令編譯:
[[email protected]CentOS_190 ~]# gcc -nostartfiles p.c
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400398
在單獨使用nostartfiles選項時會報警告,生成的可執行程式可以執行,但是會產生段錯誤,去掉對func()函式的呼叫就不會產生段錯誤了。將生成的可執行程式反彙編,和使用前面的方法生成可執行程式的反彙編結果比較,發現除了函式名不一樣外,沒有其他區別,不知道為什麼會產生段錯誤。知道的麻煩告知一聲,拜謝!
相關閱讀:
相關推薦
C語言中沒有main函式生成可執行程式的幾種方法
轉自:http://www.linuxidc.com/Linux/2013-09/90061.htm 1、define預處理指令 這種方式很簡單,只是簡單地將main字串用巨集來代替,或者使用##拼接字串。示例程式如下: #include <stdio.h>
C語言筆記19--main函式的引數
main函式有兩個引數,一般的寫法為int main(int argc,char *argv[])或者int(int argc,char **argv)。第一個引數是說明main函式有多少個引數,這個值至少為1,因為在執行exe檔案時,檔名會作為main函式的一個引數。char *argv[]是一個
c++可以把任意基本型別轉換為string, 類似於c 語言中的 sprintf函式
//c++中按照格式輸入輸出 類似於c 語言中的 sprintf函式 #include<iostream> #include<sstream>//std::stringstream 標頭檔案 int main() { std::string str = "高海文"
C語言中變數和函式的宣告與定義
一、變數在將變數前,先解釋一下宣告和定義這兩個概念。宣告一個變數意味著向編譯器描述變數的型別,但並不為變數分配儲存空間。定義一個變數意味著在宣告變數的同時還要為變數分配儲存空間。在定義一個變數的同時還可以對變數進行初始化。 區域性變數通常只定義不宣告,而全域性變數多在原始檔中定義,在標頭檔案中宣告。 區域性變
C語言中返回字串函式的四種實現方法
其實就是要返回一個有效的指標,尾部變數退出後就無效了。 使用分配的記憶體,地址是有效 char *fun() { char* s = (char*)calloc(100, sizeof(char*) ); if (s)
C語言中沒有定義bool型別
今天才發現C語言中原來沒有定義bool型別。 如果要在C語言中使用bool型別,可以自己定義。 定義方式如下: #ifndef __cplusplus typedef char bool; #define false 0 #define true 1 #endif
C語言中沒有String型別
C 是靜態弱型別語言。意味著型別(包括 size 等資訊,你使用 sizeof 就是編譯時候得到的)在編譯的時候就能確定下來。 它的原生簡單型別有 char int float 這樣的,原生複合型別有 struct,以及“偉大”的 指標型別 primitive_type * . 這些型別的設計是在編譯時可以
關於C語言中一些常用函式的說明
1.I/O函式 (1)scanf函式: int scanf(const char *format…..); 從標準輸入流stdin中按格式format將資料寫到引數表中;若操作成功,返回寫到引數表中的引數個數,否則返回EOF; 注意以下幾點: ①scanf函式沒
C語言中使用靜態函式的好處
靜態函式會被自動分配在一個一直使用的儲存區,直到退出應用程式例項,避免了呼叫函式時壓棧出棧,速度快很多。 關鍵字“static”,譯成中文就是“靜態的”,所以內部函式又稱靜態函式。但此處“static”的含義不是指儲存方式,而是指對函式的作用域僅侷限於本
C語言中有關外部函式呼叫的問題
首先指出一點,我們通常所說的編譯器並非僅指編譯器,確切來說是編譯工具鏈,裡面包括了預編譯器、編譯器、彙編器和聯結器。 對於外部函式實體(處於呼叫函式所在原始檔之外的其他原始檔中的函式),是在連結過程中,才會被尋找和新增程序序,一旦沒有找到函式實體,就會報錯,無法成功連結。
C語言中常用的函式
字串函式 複製函式strcpy() 格式 strcpy(字串陣列名,"字串"); 連線函式strcat(字串陣列1,字串陣列2) 格式 strcat(字串1,字串2); 比較函式strcmp() 格式 strcmp(字串1,字串2);
C語言中static的作用及C語言中使用靜態函式有何好處
在C語言中,static的字面意思很容易把我們匯入歧途,其實它的作用有三條,分別是: 一是隱藏功能,對於static修飾的函式和全域性變數而言 二是保持永續性功能,對於static修飾的區域性變數而言。 三是因為存放在靜態區,全域性和區域性的static修飾的變數,都預設初始化為0 下面我逐一給
C語言中檔案操作函式彙總
#include <stdio.h> #include <stdlib.h> int main() { FILE* fd = fopen("test.txt","r"); if(NULL == fd)//檔案開啟失敗 { perror("fope
C語言中的內部函式與外部函式
內部函式: 如果一個函式只能被本檔案中其它函式所呼叫,它稱為內部函式。在定義內部函式時,在函式名和函式型別的前面加static。即 static 型別識別符號 函式名 (形參表) 如: static int fun (int a, int b) 內部函式又稱靜態函
C語言:在main函式之前和之後執行函式
前段時間,需要寫個簡單的用C語言實現的輕量級httpd服務,想實現在新增api的時候,只要在Makefile中新增對應的api C檔案,就能做到將此api加入到api列表以提供響應的功能,類似於linux的驅動程式編寫,在編寫驅動的時候,只要編譯我們寫的C程式,
C語言中的子函式和主函式有什麼聯絡啊?它們是怎麼編寫的?
函式定義的一般形式1.無參函式的一般形式 型別說明符 函式名() { 型別說明 語句 } 其中型別說明符和函式名稱為函式頭。 型別說明符指明瞭本函式的型別,函式的型別實際上是函式返回值的型別。 該型別說明符與第二章介紹的各種說明符相同。 函式名是由使用者定義的識別符號,函式名後有一個空括號,其中無引數,但括
C語言中的靜態函式的作用
轉載 在C語言中為什麼要用靜態函式(static function)? 如果不用這個static關鍵字,好象沒有關係。那麼,用了static以後,有什麼作用呢? 我們知道,用了static的變數,叫做靜態變數,其意義是,該變數的值在下次呼叫時,還繼續保留前次呼叫時的值。
C語言中通過sprintf()函式構造sql語句
一、C語言如何構造sql 做專案時,由嵌入式開發慢慢涉及到後臺開發,接觸資料庫慢慢就多了,一般情況下,sql定義成一個char *,或者一個字元陣列,裡面就寫sql語句就行了。例如: char *sql; sql = "create ta
沒有main函式時的Java程式的執行
Java初學者大都是從main函式在控制檯列印HelloWorld來開始Java學習的, 其形式為:public static void main(String[ ] arg)。 首先,main方法是JVM(java虛擬機器)自動呼叫,JVM呼叫main方法的位置自然不會在某個類中、或某個包中,
C++中函式返回陣列指標的幾種方法總結
因為陣列不能被拷貝,所以函式不能返回陣列。不過,函式可以返回陣列的指標或引用。返回陣列指標的方法有以下幾種: 1、方法一:使用類型別名。如下 typedef int arrt[10];//arrT是一個類型別名,它表示的型別是含有10個整數的陣列