g++ 编译过程体会与命令行参数argc,argv[]详解

  • Post author:
  • Post category:其他





argc

,

*argv[]

参数介绍

main(int argc,char *argv[ ])

  1. argc为整数, 代表参数的个数。
  1. argv为指针数组,里面的元素个数是 argc 个,(可理解为:char **argv or: char *argv[] or: char argv[][] )。

    注:main()括号内是固定的写法。

下面给出一个例子来理解这两个参数的用法:

假设程序的名称为prog,

  • 当只输入prog,则由操作系统传来的参数为:

    argc=1,表示只有一程序名称。

    argv 指针数组中只有一个元素,

    argv[0]指向输入的程序路径及名称:./prog

当输入prog para_1,有一个参数,则由操作系统传来的参数为:

argc=2,表示除了程序名外还有一个参数。

argv[0]指向输入的程序路径及名称。

argv[1]指向参数para_1字符串。

当输入prog para_1 para_2 有2个参数,则由操作系统传来的参数为:

argc=3,表示除了程序名外还有2个参数。

argv[0]指向输入的程序路径及名称。

argv[1]指向参数para_1字符串。

argv[2]指向参数para_2字符串。

4.void main( int argc, char *argv[] )

char *argv[] :


argv 是一个指针数组,他的元素个数是argc,存放的是指向每一个参数的指针


#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
        printf("%s\n",argv[0]);//读取可执行程序(包括路径)

        /*读取参数*/
        int i = 1;
        while(i < argc)
        {
                printf("%s\n",argv[i]);
                i++;
        }
        //说明在dos和unix环境下,用""扩起来的表示其是一个字符串,代表着一个参数
        return 0;
}


不同类型的指针, 占用的字节个数, 从而便于系统分配内存;


x86
sizeof(char)  1
sizeof(char*)  4
sizeof(int)  4
sizeof(int*)  4
sizeof(double)  8
sizeof(double*)  4
sizeof(float)  4
sizeof(float*)  4
sizeof(string)  28
sizeof(string*)  4

x64

sizeof(char)  1
sizeof(char*)  8
sizeof(int)  4
sizeof(int*)  8
sizeof(double)  8
sizeof(double*)  8
sizeof(float)  4
sizeof(float*)  8
sizeof(string)  40
sizeof(string*)  8



1. 代码文件;

g++ –version:7.5.0

  1. main.cpp 函数的执行入口处,
// main.cpp
#include <iostream>
using namespace std;
 
int main(int argc, char *argv[])
{
    cout<<"this is a g++ : Hello World!!! "<<endl;
    return 0;
}

整体的过程 分为四部:

  1. 预编译 ( 生成预编译文件.ii )

    c 文件生成的预编译是.i 文件, C++ 生成的是.ii 文件;
g++  -E  main.cpp   -o   hello.ii
//C++预处理后生成的是.ii文件
  1. 编译 (生成 汇编代码.s 文件)
g++  -S  hello.ii    -o hello.s 
//.s  由编译器生成汇编代码
  1. 汇编 ( 将汇编代码生成 目标文件.o , 为二进制文件)
g++  -c  hello.s   -o hello.o
//.o  汇编器 生成 目标文件, 该目标文件为二进制文件, 为机器执行的指令  
  1. 链接 ( 对源文件 main.cpp 执行命令行 生成 .out 可执行文件)
g++  main.cpp  -o hello.out 
//:由链接器 ld,   将目标文件.o,  与库文件链接
// 将目标文件由链接器 链接库文件 生成  可执行文件.out  



1. 预编译



1.1 预编译的作用

主要处理源代码文件中的以“#”开头的预编译指令。处理规则见下:

  1. 删除所有的#define,展开所有的宏定义。

  2. 处理所有的条件预编译指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。

  3. 处理“#include”预编译指令,将文件内容替换到它的位置,这个过程是递归进行的,文件中包含其他文件。

  4. 删除所有的注释,“//”和“/**/”。

  5. 保留所有的#pragma 编译器指令,编译器需要用到他们,如:#pragma once 是为了防止有文件被重复引用。

  6. 添加行号和文件标识,便于编译时编译器产生调试用的行号信息,和编译时产生编译错误或警告时能够显示行号;



1.2 预编译的使用

使用 g++ -E 代表在预编译过后, 停止编译的过程;

g++ -E

//预处理后的版本将超过 两万 行。

//这主要是因为头文件 iostream 被包含进来,而且它又包含了其他的头文件,

//除此之外,还有若干个处理输入和输出的类的定义。



1.3 预编译的调试作用

当我们无法判断宏定义是否 正确

或者 头文件包含是否正确时,

可以通过查看预编译后文件来确定问题;



2. 编译 (生成汇编语言)


汇编代码是在编译阶段生成的;



2.1 编译的作用

把预编译之后生成的xxx.i(c 文件而来)或xxx.ii文件(C++ 文件),进行一系列词法分析、语法分析、语义分析及优化后,

生成相应的汇编代码文件

  1. 词法分析:利用类似于“有限状态机”的算法,将源代码程序输入到扫描机中,将其中的字符序列分割成一系列的记号。

  2. 语法分析:语法分析器对由扫描器产生的记号,进行语法分析,产生语法树。由语法分析器输出的语法树是一种以表达式为节点的树。

  3. 语义分析:语法分析器只是完成了对表达式语法层面的分析,语义分析器则对表达式是否有意义进行判断,其分析的语义是静态语义——在编译期能分期的语义,相对应的动态语义是在运行期才能确定的语义。

  4. 优化:源代码级别的一个优化过程。

  5. 目标代码生成:由代码生成器将中间代码转换成目标机器代码,生成一系列的代码序列——汇编语言表示。

  6. 目标代码优化:目标代码优化器对上述的目标机器代码进行优化:寻找合适的寻址方式、使用位移来替代乘法运算、删除多余的指令等。



2.2 编译的使用

  1. 把预编译之后生成的xxx.ii文件,进行一系列词法分析、语法分析、语义分析及优化后,生成相应的汇编代码文件。
 g++ -S  

请添加图片描述

注意: 汇编过程 会和CPU的架构相关,


不同的CPU架构 采用不同的汇编指令集



故生成不通过的汇编代码
请添加图片描述



3. 汇编(生成目标文件 obj.o )



3.1 汇编的作用

生成目标文件 (obj.o ):



汇编代码转变成机器可以执行的指令,

每一个汇编语句 几乎都会都对应一条机器指令

汇编器的汇编过程相对于编译器来说更简单,没有复杂的语法,也没有语义,更不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译过来,汇编过程可以由汇编器as完成。

经汇编之后,产生目标文件xxx.o(Windows下)、xxx.obj(Linux下)。

注意这里的目标文件是指 机器可以执行的指令;



3.2 汇编的使用

输出的 .o 代表的是目标文件, 该目标文件是二进制码文件;

g++ -C



4. 链接( 生成可执行文件 .out)

实践中, 会有多个源文件, 即多个 .cpp 文件, 并且多个源文件, 也会生成多个对应的 .o 目标文件;

链接: 便是将 不同源文件生成的目标文件进行 链接, 从而形成一个可执行程序.out 文件;

而链接有两种方式可以实现:

静态链接 和 动态链接;



4.1 静态链接

函数和数据被编译进一个二进制文件。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件。

  1. 空间浪费:因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,会出现同一个目标文件都在内存存在多个副本;

  2. 更新困难:每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。

  3. 运行速度快:但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。



4.2 动态链接

动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。

  1. 共享库:就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分副本,而是这多个程序在执行时共享同一份副本;

  2. 更新方便:更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标。

  3. 性能损耗:因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损

    失。



5.



6.

参考链接:

https://blog.csdn.net/Three_dog/article/details/103688043#t1



版权声明:本文为chumingqian原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。