C和C++文件混合編譯

一個項目中若同時存在.c文件和.cpp文件,該如何編譯呢?

首先,來看一下編譯的一些小知識:

1.gcc編譯*.c/*.cpp文件依據各自的文件類型各自編譯爲C型目標文件和C++型目標文件

2.g++編譯*.c/*.cpp文件,都是編譯爲C++類型的目標文件

3.在第2點基礎上,使用g++時,不論是*.c還是*.cpp都將鏈接std c++庫。而gcc是一句各自文件類型鏈接相應的std c庫或者是std c++庫

4.gcc編譯*.c之後預定義的宏比較少

5.gcc編譯*.cpp文件或者g++編譯之後,預定義的宏比較多。

定義如下文件:

hw.c  hw.h  main.cpp  test.cpp  test.h

main模塊調用test模塊,test模塊調用hw模塊

首先,列出各個文件最初始的內容:

hw.h

#ifndef HW_H
#define HW_H

int get_hw();

#endif
hw.c

#ifndef HW_H
#	include "hw.h"
#endif


int get_hw()
{
	return 100;
}
test.h

#ifndef TEST_H
#define TEST_H

void do_test();

#endif
test.cpp

#ifndef TEST_H
#	include "test.h"
#endif

#ifndef HW_H
#	include "hw.h"
#endif
#include <stdio.h>
void do_test()
{
	printf("hw: %d\n", get_hw());
}
main.cpp

#ifndef TEST_H
#	include "test.h"
#endif


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

一.用g++編譯

Makefile

myexpect : main.o hw.o test.o
	g++ -o myexpect main.o hw.o test.o

main.o : main.cpp
	g++ -c main.cpp

hw.o : hw.c
	g++ -c hw.c

test.o : test.cpp
	g++ -c test.cpp
在鍵入make後可以成功生成myexpect程序。這體現了文章開頭的小知識1。使用nm 命令查看hw.o中的符號

root@hu-virtual-machine:/home/hu/project/c_# nm hw.o 
0000000000000000 T _Z6get_hwv
可以看到get_hw符號前,後都加了一些東西,先記在這兒,和下文做對比

二.hw.h中的符號由extern "C"保護

將hw.h中的內容改變爲下文

#ifndef HW_H
#define HW_H

#ifdef __cplusplus
extern "C"{
#endif

	int get_hw();

#ifdef __cplusplus
}
#endif

#endif
沿用上面的Makefile,執行make後,也能正常生成myexpect程序。這時,使用nm工具,觀察hw.o

root@hu-virtual-machine:/home/hu/project/c_# nm hw.o 
0000000000000000 T get_hw
多麼的明瞭,多麼的乾淨,顯然,get_hw沒有經過修改,所以,hw.o就是C型目標文件。和上面的比較起來,一中的hw.o就是C++型目標文件

三.test.cpp中的#include "hw.h" 由extern “C” 保護

現在,移除調二中的對hw.h的修改,恢復爲原始的沒有extern "C"的內容。將test.cpp改變爲如下

#ifndef TEST_H
#	include "test.h"
#endif

#ifndef HW_Hi
extern "C" {
#	include "hw.h"
}
#endif
#include <stdio.h>
void do_test()
{
	printf("hw: %d\n", get_hw());
}
依然沿用上面的Makefile,執行make後輸出

g++ -c main.cpp
g++ -c hw.c
g++ -c test.cpp
g++ -o myexpect main.o hw.o test.o
test.o: In function `do_test()':
test.cpp:(.text+0x5): undefined reference to `get_hw'
collect2: error: ld returned 1 exit status
make: *** [myexpect] Error 1
這是因爲在test.cpp中的extern "C" { #include "hw.h" } 表示的意思爲:test.cpp期望一個C型hw.o的目標文件,然而在Makefile中使用了g++去編譯hw.c生成了C++型目標文件,這就導致了問題產生。針對Makefile做如下修改

myexpect : main.o hw.o test.o
	g++ -o myexpect main.o hw.o test.o

main.o : main.cpp
	g++ -c main.cpp

hw.o : hw.c
	gcc -c hw.c

test.o : test.cpp
	g++ -c test.cpp
執行make後可以正常生成myexpect程序。這是因爲使用gcc去編譯*.c文件,肯定會生成C型目標文件

四.所有模塊由gcc編譯,鏈接時使用g++進行鏈接

將所有文件內容恢復到原始狀態

先看一下所有模塊由gcc編譯,再由gcc進行鏈接

myexpect : main.o hw.o test.o
	gcc -o myexpect main.o hw.o test.o

main.o : main.cpp
	gcc -c main.cpp

hw.o : hw.c
	gcc -c hw.c

test.o : test.cpp
	gcc -c test.cpp

PHONY : clean
clean :
	rm -rf *.o myexpect
執行make後,可以正常生成myexpect程序。似乎gcc完全可以勝任g++的工作嘛。其實不然,那是因爲我們的例子代碼中沒有使用libstdc++中的任何內容,比如將main.cpp做如下簡單修改

#ifndef TEST_H
#	include "test.h"
#endif

#include <iostream>

using std::cout;
using std::endl;
int main(int argc, char** argv)
{
	do_test();

	cout<<"hi"<<endl;

	return 0;
}
這行上面這個Makefile後,會輸出

gcc -c main.cpp
gcc -c hw.c
gcc -c test.cpp
gcc -o myexpect main.o hw.o test.o
main.o: In function `main':
main.cpp:(.text+0x1a): undefined reference to `std::cout'
main.cpp:(.text+0x1f): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)'
main.cpp:(.text+0x24): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)'
main.cpp:(.text+0x2c): undefined reference to `std::ostream::operator<<(std::ostream& (*)(std::ostream&))'
main.o: In function `__static_initialization_and_destruction_0(int, int)':
main.cpp:(.text+0x5a): undefined reference to `std::ios_base::Init::Init()'
main.cpp:(.text+0x69): undefined reference to `std::ios_base::Init::~Init()'
collect2: error: ld returned 1 exit status
make: *** [myexpect] Error 1
這一大段的錯誤,這是因爲編譯過程鏈接了libstdc的內容,而iostream在libstdc++中。爲了解決這個問題,這樣修改Makefile

myexpect : main.o hw.o test.o
	g++ -o myexpect main.o hw.o test.o

main.o : main.cpp
	gcc -c main.cpp

hw.o : hw.c
	gcc -c hw.c

test.o : test.cpp
	gcc -c test.cpp

PHONY : clean
clean :
	rm -rf *.o myexpect
用gcc去依據各文件類型生成相應的目標文件,最後用g++來指定鏈接libstdc++的內容。使用nm命令觀察hw.o的內容

root@hu-virtual-machine:/home/hu/project/c_# nm hw.o 
0000000000000000 T get_hw
雖然hw.o是C型目標文件,不過對於myexpect程序來說,這個符號是唯一的,可以正常尋址,程序可以正常工作


以上就是C和C++混合編程總結的一些規律,nm工具是一個很好的調試工具,用它來觀察編譯後的目標文件中的符號有助於加深對C和C++處理符號的理解,謝謝觀賞。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章