首页 > 其他分享 >GCC使用具体教程以及例子

GCC使用具体教程以及例子

时间:2024-02-02 17:45:55浏览次数:19  
标签:GCC sub int 教程 gcc 编译 add 例子 main

编译第一个C程序

#include <stdio.h>
int main(void)
{
    printf("hello world!\n");
    return 0;
}

使用gcc命令将hello.c编译成可执行程序 a.out,并运行:

 将源文件hello.c编译为一个指定名称的可执行文件:hello,可以通过gcc -o参数来完成

GCC 编译过程分析

 

以demo.c为例:从一个C语言源文件,到生成最后的可执行文件,GCC编译过程的基本流程如下:

 

  • C 源文件: 编写一个简单的hello world程序
  • 预处理:生成预处理后的C源文件 hello.i
  • 编译:将C源文件翻译成汇编文件 hello.s
  • 汇编:将汇编文件汇编成目标文件 hello.o
  • 链接:将目标文件链接成可执行文件

 

 

gcc命令是GCC编译器里的一个前端程序,用来控制整个编译过程:分别调用预处理器、编译器和汇编器,完成编译的每一个过程,最后调用链接器,生成可执行文件:a.out

默认情况下,gcc命令会自动完成上述的整个编译过程。当然,gcc还提供了一系列参数,使用这个参数,可以让用户精准控制每一个编译过程。

  • -E :只做预处理,不编译
  • -S :只编译,将C程序编译为汇编文件
  • -c :只汇编,不链接。
  • -o :指定输出的文件名

GCC -E 参数

如果只对一段C语言程序做预处理操作,而不进行编译,可以通过gcc -E 参数来完成。如下面的一段程序,在程序中分别使用#include包含头文件,使用#define定义宏,使用#ifdef条件编译。

#include <stdio.h>
#define PI  3.14
int main(void)
{
    printf("hello world!\n");
    printf("PI = %f\n", PI);
#ifdef DEBUG
    printf("debug mode\n");
#else
    printf("release mode\n");
#endif
    return 0;
}

 上面的C源程序使用gcc -E进行预处理,就可以生成原汁原味的C程序

 

 通过预处理后的C程序,使用#include包含的的头文件就地展开,我们可以看到stdio.h头文件中printf函数的声明。程序中使用#define定义的宏PI,也会在实际使用的地方展开为实际的值。使用#ifdef定义的条件编译,会根据条件判断,选择实际要编译的代码分支。

GCC -S 参数

如果只对C源程序做编译处理,不汇编,可以使用gcc -S 参数:会gcc会将C源程序做预处理、编译操作,生成对应的汇编文件,不再做进一步的汇编和链接操作。

也可以 利用上次的

gcc -S demo.i

GCC -c 参数

 如果只想对一个C程序做汇编操作,不进行链接,可以使用gcc -c 来完成:

 

gcc只对源文件做预处理、编译和汇编操作,不会做链接操作。在当前目录下,我们可以看到demo.c经过汇编编译,生成的对应的demo.o目标文件。

当然,gcc -c 选项,也可以对上几节生成的 demo.i、demo.s文件直接汇编,生成对应的目标文件

 

默认情况下,gcc会将demo.c生成对应的demo.o目标文件。当然,我们也可以通过 -o 输出选项,生成指定的目标文件:

GCC 静态链接库

我们也可以通过gcc命令,将自己实现的一些函数封装成库,提供给其他开发者使用。

制作静态链接库

假如现在有add.c和sub.c 源文件,分别实现了加法函数add()和减法函数sub():

// add.c
int add(int a, int b)
{
    return a + b;
}
// sub.c
int sub(int a, int b)
{
    return a - b;
}

将它们编译生成一个静态库libmath.a,供其他程序调用:

# gcc -c add.c 
# gcc -c sub.c 
# ls
add.c  add.o  sub.c  sub.o
# ar rcs libmath.a add.o sub.o
# ls
add.c  add.o  libmath.a  sub.c  sub.o

生成的libmath.a就是一个静态库,里面包含了我们实现的add()函数和sub()函数:

# ar t libmath.a 
add.o
sub.o
# nm libmath.a 
add.o:
0000000000000000 T add
sub.o:
0000000000000000 T sub

使用静态链接库

接下来,我们就可以编写一个main()函数,然后在main函数里调用它们。

int add(int a, int b);
int sub(int a, int b);
int main(void)
{
    add(1, 2);
    sub(4, 3);
    return 0;
}

在编译mainc源文件时,因为调用了libmath.a库中的add和sub函数,编译时要使用gcc -l指定库的名字,使用-L指定库的路径:

# ls
libmath.a  main.c
# gcc main.c -L./ -lmath
# ls
a.out  libmath.a  main.c

GCC -I 参数

按照C语言的传统,调用函数之前,要先声明,然后才能使用。对add和sub函数的声明,可以放到C源文件里声明,也可以单独放到一个头文件里声明,任何使用add和sub函数的源文件,直接包含这个头文件就可以了。

# tree
.
├── inc
│   ├── add.h
│   └── sub.h
├── libmath.a
└── main.c
# cat inc/add.h 
int add(int a, int b);
# cat inc/sub.h 
int sub(int a, int b);
# cat main.c 
#include "add.h"
#include "sub.h"
int main(void)
{
    add(1, 2);
    sub(4, 3);
    return 0;
}

因为头文件 add.h 和 sub.h 统一放到了inc目录下,编译器在预处理时,要告诉编译器这个路径,否则编译器就会找不到这些头文件报错。通过 gcc -I参数可以告诉编译器,这些头文件的所在路径:

# ls
inc  libmath.a  main.c
# gcc main.c -L./ -lmath -I inc/
# ls
a.out  inc  libmath.a  main.c

GCC 动态链接库

静态库里实现的函数,可能被多个应用程序调用,那么在链接时,被调用的这个函数可能就会多次链接到不同的应用程序中。

 

比如C标准库的printf函数,可能被一个应用程序调用多次,被不同的应用程序调用,当这些应用程序加载到内存运行时,内存中也就存在多个printf函数代码的副本,太浪费了内存空间。而且,对于应用程序来说,每一个调用的库函数都被链接进来,自身的文件体积也会大增。是可忍孰不可忍,动态链接此时就粉墨登场了。

动态链接跟静态链接相比,具有以下优势

  • 库的代码不会链接到应用程序里
  • 同一份代码(如printf代码)可以被多个应用程序共享使用
  • 大大节省了内存空间

但动态库也有缺点,发布软件时,动态库需要和应用程序一起发布,否则你编译的应用程序到了一个新的平台可能就无法运行。

制作一个动态库

 

 

标签:GCC,sub,int,教程,gcc,编译,add,例子,main
From: https://www.cnblogs.com/Galesaur-wcy/p/18003586

相关文章

  • GCC使用教程——GCC是什么?
    整理学习来自:https://www.xinbaoku.com/gcc/对于GCC的认知,很多读者还仅停留在“GCC是一个C语言编译器”的层面,是很片面的。从本节开始,我将带领大家系统学习GCC,本节先带领大家系统地了解一下GCC。谈到GCC,就不得不提GNU计划。GNU全称GNU'sNotUNIX,又被称为“革奴计划”,......
  • 无涯教程-toString()函数
    此方法返回表示指定对象的字符串。toString()-语法string.toString()toString()-返回值返回表示指定对象的字符串。toString()-示例varstr="Applesareround,andApplesareJuicy.";console.log(str.toString());运行上面代码输出Applesareround,an......
  • 【教程】Objective-C 性能监控
     1、内存监控CPU内存监控克魔助手提供了分析内存占用、查看CPU实时活动数据以及追踪特定应用程序的功能,让开发者可以更好地了解应用程序的运行情况。以下是一些示例截图: ​    ​   ​  同样,克魔助手还提供了内存、GPU性能监控、网络监控等功......
  • 无涯教程-toLowerCase()函数
    此方法返回转换为小写的调用字符串值。toLowerCase()-语法string.toLowerCase()toLowerCase()-返回值返回转换为小写的调用字符串值。toLowerCase()-示例varstr="Applesareround,andApplesareJuicy.";console.log(str.toLowerCase())运行上面代码输......
  • 无涯教程-substring()函数
    此方法返回String对象的子集。substring()-语法string.substring(indexA,[indexB])indexA   - 小于字符串长度的0到1之间的整数。indexB   - (可选)0到字符串长度之间的整数。substring()-返回值substring方法根据给定的参数返回新的子字符......
  • 无涯教程-substr()函数
    此方法以指定的字符数返回从指定位置开始的字符串中的字符。substr()-语法string.substr(start[,length]);start   -开始提取字符的位置(0到1的整数,小于字符串的长度)。length  -要提取的字符数substr()-返回值substr()方法根据给定的参数返回新的子字符......
  • 【教程】苹果上架要求有哪些常见要点?
    ​苹果上架要求是苹果公司对于提交应用程序到苹果商店上架的要求和规定。这些要求主要是为了保证用户体验、应用程序的质量和安全性。以下是苹果上架要求的详细介绍:1.应用程序的内容和功能必须符合苹果公司的规苹果上架要求是苹果公司对于提交应用程序到苹果商店上架的要求和规......
  • PostgreSQL从小白到高手教程 - 第44讲:pg流复制部署
       PostgreSQL从小白到专家,是从入门逐渐能力提升的一个系列教程,内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容,希望对热爱PG、学习PG的同学们有帮助,欢迎持续关注CUUGPG技术大讲堂。 第44讲:流复制部署 PostgreSQL第44讲:2月3日(周六)19......
  • 无涯教程-split()函数
    此方法通过将字符串对象分成子字符串来将字符串对象拆分为字符串数组。split()-语法string.split([separator][,limit]);separator  - 指定用于分隔字符串的字符。limit      - 整数,用于指定要找到的分割数的限制。split()-返回值split方法返回......
  • 无涯教程-slice()函数
    此方法提取字符串的一部分并返回新的字符串。slice()-语法string.slice(beginslice[,endSlice]);beginSlice  - 从零开始的索引。endSlice   - 终止提取的从零开始的索引。如果省略,则slice提取到字符串的末尾。slice()-返回值如果成功,slice返......