linux c++开发记录


g++使用

1. 编译过程

  • 预处理(-E)
  • 编译(-S)
  • 汇编(-c)
  • 链接(-O)

1.1 预处理(-E)

为了直观的了解预处理,理解预处理做了哪些工作,不说那么多,直接上代码,创建一个main.cpp文件,代码如下:

/* main.cpp */
int main()
{
    int a = 1;
    int b = 2;
    return a + b;
}

一个main函数,没有引用头文件,下面使用g++ -E预处理:

g++ -E main.cpp > main.i

预处理,并将预处理结果保存到main.i文件中,打开main.i文件如下:

# 1 "main.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "main.cpp"
int main()
{
    int a = 1;
    int b = 2;
    return a + b;
}

生成的预处理文件main.i在前几行添加了一些注释,也将main.cpp内容复制过来了。
下面我们写个复杂一些的,有头文件引用,有宏定义,有注释,还有未定义参数赋值的错误:

/* main.h */
#define VALUES 100
int test();
// 空一行
void test2(){}

// 空两行
void test3(){}


// 空三行
void test4(){}
/* main.cpp */
#include <main.h>

int main()
{
    // 参数未定义
    a = 100;
    return test();
}

int test()
{
    return VALUES;
}

预处理:

g++ -E main.cpp -I. > main.i

看看main.i:

# 1 "main.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "main.cpp"
# 1 "./main.h" 1

int test();

void test2(){}


void test3(){}



void test4(){}
# 2 "main.cpp" 2

int main()
{

    a = 100;
    return test();
}

int test()
{
    return 100;
}

生成很顺利,可以看出,预处理工作:

  1. 不关注语法错误
  2. 宏替换
  3. 消除注释
  4. 将头文件整个包括进来,连空行都是完整的复制过来

理解了预处理,c/c++新手阶段时遇到的重定义等等错误就能够理解了。

1.2 编译(-S)

编译将会把预处理生成的.i文件生成为.s的汇编代码文件。
继续上面的代码,当我们尝试编译上面生成的main.i文件时,会提示语法错误:
在这里插入图片描述
使用回最开始的简单代码来测试,直接执行编译:

/* main.cpp */
int main()
{
    int a = 1;
    int b = 2;
    return a + b;
}

编译:

g++ -S main.cpp

默认生成main.s汇编文件,打开如下:

	.file	"main.cpp"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$1, -8(%rbp)
	movl	$2, -4(%rbp)
	movl	-8(%rbp), %edx
	movl	-4(%rbp), %eax
	addl	%edx, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0"
	.section	.note.GNU-stack,"",@progbits

从这开始就和汇编是一样的了,再深入就需要去了解汇编的原理,后面两个过程不再详细描述。

1.3 汇编(-c)

汇编的工作是将汇编代码转成机器代码,注意“-c”是小写字母,而预处理和编译都是大写字母。汇编后生成.o目标文件。

g++ -c main.i

生成main.o

1.4 链接(-o)

将.o、.a、.so等文件链接生成可执行文件。

g++ main.o -o main

生成main可执行文件

g++ main.o

生成默认的a.out可执行文件

2. 编译单文件

g++ main.cpp [-o target_file_name]

生成单个文件很简单,可通过-o参数指定生成文件名称。不指定则默认生成a.out文件。

3. 编译同目录下多个文件

目录结构:

├── proj
│   ├── main.cpp
│   ├── test.cpp
│   ├── test.h

代码:

// main.cpp
#include "test.h"
int main()
{
    printf("enter main func\r\n");
    test();
    return 0;
}

// test.h
#include <stdio.h>
int test();

// test.cpp
#include "test.h"
int test()
{
    printf("call test func\r\n");
    return 0;
}

编译:

g++ main.cpp test.cpp -o main.out

运行:
在这里插入图片描述

4. 编译不同目录下多个文件(一次性、参数-I)

还是上面的代码,目录结构改了,将test.cpp、test.h放到test文件夹下:

├── proj
│   ├── main.cpp
│   ├── test
│   │   ├── test.cpp
│   │   ├── test.h

编译:

g++ main.cpp ./test/test.cpp -Itest -o main.out

因为test.cpp、test.h和main.cpp不在相同文件夹内,所以需要指定cpp文件地址及.h文件所在目录。
-I :指定头文件所在目录。

5. 编译不同目录下多个文件(分步)

文件结构与4一致。
4需要指定具体的文件,而且每次编译的时候需要把所有的cpp文件都编译一次,有时候我们只改一个文件并不需要全部重新编译。我们可以将源文件编译成一个.o汇编代码文件,最后再链接再链接在一起。

目录结构:

├── proj
│   ├── main.cpp
│   ├── test
│   │   ├── test.cpp
│   │   ├── test.h
│   ├── obj

生成test.o到obj目录:

g++ -c ./test/test.cpp -o ./obj/test.o

生成可执行文件:

g++ main.cpp ./obj/test.o -Itest -o main.out

6. 编译及链接.a静态库(参数-l、-L)

我要将test.cpp做成静态库,用main去调用。
目录结构:

├── proj
│   ├── main.cpp
│   ├── test
│   │   ├── test.cpp
│   │   ├── test.h
│   ├── bin

生成.a文件:

g++ -c ./test/libtest.cpp -o ./bin/libtest.o
ar -r ./bin/libtest.a ./obj/test.o

分两步,先用-c生成.o文件,再通过.o文件生成.a文件;

用ar命令一步也可以生成.a文件,但是链接的时候会报“无法添加符号: 归档没有索引;运行 ranlib 以添加一个”的错误。

ar -r ./bin/libtest.a ./test/test.cpp

执行完后在bin目录下将生成一个libtest.a文件。

编译main.cpp:
链接libtest.a编译main.cpp

g++ main.cpp -ltest -Lbin -Itest -o main.out

-l :指定依赖库的文件名称,例子中依赖libtest.a,则参数为“-ltest”,去掉开头的lib及后缀,.so文件也是同样。
-L :指定依赖库的存放位置,例子中libtest.a放在bin目录下,则需要设置参数为“-Lbin”。

7. 编译及链接.so动态库

生成.so文件:

g++ ./test/test.cpp -shared -o ./bin/libtest.so

编译main.cpp:

g++ main.cpp -ltest -Lbin -Itest -o main.out

ldd查看可执行文件的依赖库:
在这里插入图片描述

找不到libtest.so文件,需要添加.so文件链接:

sudo ln -s /mnt/win_linux/linux_cpp_test/bin/libtest.so /usr/lib/libtest.so

.so文件地址必须是绝对路径。

再看一下:
在这里插入图片描述

8. g++ 常用命令选项

选项解释
-ansi
-ansi只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色, 例如 asm 或 typeof 关键词。
-c只编译并生成目标文件。
-DMACRO以字符串"1"定义 MACRO 宏。定义MACRO宏,#define MACRO
-DMACRO=DEFN以字符串"DEFN"定义 MACRO 宏,#define MACRO DEFN
-E只运行 C 预编译器。
-g生成调试信息。GNU 调试器可利用该信息。
-IDIRECTORY指定额外的头文件搜索路径DIRECTORY。
-LDIRECTORY指定额外的函数库搜索路径DIRECTORY。
-lLIBRARY连接时搜索指定的函数库LIBRARY。
-m486针对 486 进行代码优化。
-oFILE 生成指定的输出文件。用在生成可执行文件时。
-O0不进行优化处理。
-O或 -O1 优化生成代码。
-O2进一步优化。
-O3比 -O2 更进一步优化,包括 inline 函数。
-shared生成共享目标文件。通常用在建立共享库时。
-static禁止使用共享连接。
-UMACRO取消对 MACRO 宏的定义。
-w不生成任何警告信息。
-Wall生成所有警告信息。

Makefile的使用

(…)

Automake的使用

(…)

tips

  1. 查看可执行文件依赖库及地址(ldd)
david@david-VirtualBox:/mnt/win_linux/linux_cpp_test$ ldd main
	linux-vdso.so.1 (0x00007ffd069fe000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2fa289e000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f2fa2e91000)

其他:
objdump -x libxxxxx.so | grep NEEDED

  1. 创建链接
# sudo ln -s <src> <target>
sudo ln -s /usr/lib/x86_64-linux-gun/libm.so.6 /usr/lib/libm.so
  1. VirtualBox下共享文件夹(mount)
sudo mount -t vboxsf linux /mnt/win_linux
  1. 2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值