文章目錄
- C++記憶體管理
- LINUX行程區分段及存盤資料
- GCC編譯流程
- 動態庫靜態庫區別及GCC加載庫
- extern-C的結果和CPP編譯的區別
- 多載的底層原理
- 編譯性語言和解釋性語言的本質區別和優缺點
C++記憶體管理
- 堆疊 存盤函式的回傳地址、引數、區域變數、回傳值,從高地址向低地址增長
- 堆 malloc/free開辟記憶體的空間,從低地址向高地址增長
- 自由存盤區 new/delete開辟記憶體空間
- 資料區
資料區包含全域/靜態存盤區和常量存盤區,存盤已初始化的全域變數和靜態變數、未初始化的全域變數和靜態變數及字串常量- 代碼區 存盤程式的機器代碼和程式指令
LINUX行程區分段及存盤資料
Linux的每個行程都有各自獨立的4G邏輯地址,其中03G是用戶態空間,34G是內核空間,不同行程相同的邏輯地址會映射到不同的物理地址中,
邏輯地址分段如下,自下而上:
- 代碼段,分為只讀存盤區和代碼區,存放字串是常量和程式機器代碼和指令
- 資料段,存盤已初始化的全域變數和靜態變數,
- bss段,存盤未初始化的全域變數和靜態變數,及初始化為0的全域變數和靜態變數
- 堆, 當行程未呼叫malloc時是沒有堆段的,malloc/free開辟的記憶體空間,向上生長
- 映射區,存盤元件以及呼叫mmap函式進行的檔案映射
- 堆疊,存盤函式的回傳地址、引數、區域變數、回傳值,向下生長,
GCC編譯流程
- 預處理階段:hello.c – “gcc -E預處理,頭檔案展開,宏替換” --> hello.i
- 編譯階段:hello.i – “gcc -s生成匯編檔案” --> hello.s
- 匯編階段:hello.s – “gcc -c生成二進制檔案” --> hello.o
- 鏈接階段:hello.o – “呼叫ld進行鏈接” --> a.out
動態庫靜態庫區別及GCC加載庫
靜態庫
- 編譯時期鏈接
- 浪費空間和資源,如果多個程式鏈接了同一個庫,則每一個生成的可執行檔案就都會有一個庫的副本,必然會浪費系統空間,
- 若靜態庫需修改,需重新編譯所有鏈接該庫的程式
動態庫
- 運行時鏈接
- 運行時被鏈接,故程式的運行速度稍慢
- 動態庫是在程式運行時被鏈接的,所以磁盤上只須保留一份副本,因此節約了磁盤空間,如果發現了bug或要升級也很簡單,只要用新的庫把原來的替換掉即可
GCC編譯加載靜態庫
-
將所有的.c檔案編譯成.o目標檔案
gcc -c add.c生成add.ogcc -c max.c生成max.o
-
對生成的.o目標檔案打包生成靜態庫
ar crv libfoo.a add.o max.o //libfoo.a是庫的名字- ar:做庫的命令
- c:創建庫
- r:將方法添加到庫里
- v:顯示程序,可以不要
-
使用靜態庫
gcc -o main main.c -static -L. -lfoo //這里寫的foo是去掉前后綴后庫的名字- -L:指定路徑 .代表當前路徑
- -l:指定庫名
GCC編譯加載動態庫
-
對生成的.o檔案處理生成共享庫,共享庫的名字為libfoo.so
gcc -shared -fPIC -o libfoo.so add.o max.o- -shared 表示輸出結果是共享庫型別的
- -fPIC 表示使用地址無關代碼(Position Independent Code)技術來生產輸出檔案
-
庫的使用
-
cp libfoo.so /usr/lib //將庫拷貝到系統庫路徑下(不推薦) -
export更改LD_LIBRARY_PATH當前終端的環境變數
-
修改/etc/ld.so.conf檔案,加入庫檔案所在目錄的路徑,然后
運行ldconfig 目錄名字,該命令會重建/etc/ld.so.cache檔案即可 -
上面三種選一個即可
gcc -o main main.c -lfoo
-
extern-C的結果和CPP編譯的區別
- 一個C語言檔案p.c
#include <stdio.h>
void print(int a,int b)
{
printf("這里呼叫的是C語言的函式:%d,%d\n",a,b);
}
- 一個頭檔案p.h
#ifndef _P_H
#define _P_H
void print(int a,int b);
#endif
- C++檔案呼叫C函式
#include <iostream>
using namespace std;
#include "p.h"
int main()
{
cout<<"現在呼叫C語言函式\n";
print(3,4);
return 0;
}
-
編譯后鏈接出錯:main.cpp對print(int, int)未定義的參考,
-
原因分析
- p.c我們使用的是C語言的編譯器gcc進行編譯的,其中的函式print編譯之后,在符號表中的名字為 _print
- 我們鏈接的時候采用的是g++進行鏈接,也就是C++鏈接方式,程式在運行到呼叫print函式的代碼時,會在符號表中尋找_print_int_int(是按照C++的鏈接方法來尋找的,所以是找_print_int_int而不是找_print)的名字,發現找不到,所以會t提示“未定義的參考”
- 此時如果我們在對print的宣告中加入 extern “C” ,這個時候,g++編譯器就會按照C語言的鏈接方式進行尋找,也就是在符號表中尋找_print,這個時候是可以找到的,是不會報錯的,
-
總結
- 編譯后底層決議的符號不同,C語言是_print,C++是_print_int_int
多載的底層原理
根據上面的編譯分析,可以知道C語言沒有多載,只有C++才有函式多載,因為函式多載通過引數串列的不同來實作,
- C語言沒有多載
"int __cdecl Add(int,int)" (?Add@@YAHHH@Z)
"double __cdecl Add(double,double)" (?Add@@YANNN@Z)
"long __cdecl Add(long,long)" (?Add@@YAJJJ@Z)
在C語言中被決議為_Add,三個一樣,所以不能進行區分,因此C語言不支持函式多載
-
C++多載
底層的重命名機制將Add函式根據引數的個數,引數的型別,回傳值的型別都做了重新命名,那么借助函式多載,一個函式就有多種命名機制, _Add_int_int,_Add_long_long,_Add_double_double -
C++中可以通過在函式宣告前加 extern “C” 將一個函式按照 C 語言的風格來進行編譯,
編譯性語言和解釋性語言的本質區別和優缺點
-
根本區別
- 計算機不能直接的理解高級語言,只能直接理解機器語言,所以必須要把高級語言翻譯成機器語言,計算機才能執行高級語言的撰寫的程式,翻譯的方式有兩種,一個是編譯,一個是解釋,兩種方式只是翻譯的時間不同,
- 解釋性語言不用編譯,在運行時翻譯
- 編譯性語言是編譯的時候直接編譯成機器可以執行的語言,編譯和運行是分開的,但是不能跨平臺,比如exe檔案,以后要運行的話就不用重新編譯了,直接使用編譯的結果就行了(exe檔案),因為翻譯只做了一次,運行的時不要翻譯,所以編譯型語言的程式執行效率高
-
編譯性語言的優缺點
- 優點
- 運行速度快,代碼效率高,編譯后程式不可以修改,保密性好
- 缺點
- 代碼需要經過編譯方可運行,可移植性差,只能在兼容的作業系統上運行,
- 優點
-
解釋性語言的優缺點
- 優點
- 可移植性好,只要有解釋環境,可以在不同的作業系統上運行,
- 缺點
- 運行需要解釋環境,運行起來比編譯的要慢,占用的資源也要多一些,代碼效率低,代碼修改后就可以運行,不需要編譯程序
- 優點
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/255620.html
標籤:其他
