百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术流 > 正文

C程序编程四步走(c程序四个步骤)

citgpt 2024-09-07 13:32 9 浏览 0 评论

任何一个 C 程序代码到生成一个可执行文件都需要四步,分别是预处理 Pre-processing ,编译 Compiling ,汇编 Assembling 和链接 Link ,这里借助 Gcc 工具来探究这四步分别做了什么事,起到什么样的作用。本文使用的测试代码是经典入门程序 "Hello World!"。

测试环境

为探究预处理,编译,汇编和链接的功能,我们在 Ubuntu 系统中使用 Gcc 编译器( version=4.8.4 ),简单的也是最经典的入门程序 "Hello World!" 作为测试代码。源文件 hello.c[1] 代码如下:

C程序编程四步走(c程序四个步骤)

// filename: hello.c
# include <stdio.h>

int main(void){
  printf("Hello World!");
  return 0;
}

正常情况我们都会执行命令 gcc hello.c -o hello.out 来生成二进制可执行程序 hello.out。

预处理[2]

C 预处理器是用在编译器处理程序之前,它预扫描源代码完成包含头文件宏扩展条件编译行控制等功能。对于测试代码中,预处理器只对头文件进行了处理。获取预处理器输出的结果使用该命令 gcc -E hello.c -o hello.i。由于 hello.i[3] 文件内容比较多,这里截取部分进行说明。

// filename: hello.i
# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "hello.c"

...
# 1 "/usr/lib/gcc/x86_64-linux-gnu/4.8/include/stddef.h" 1 3 4
# 212 "/usr/lib/gcc/x86_64-linux-gnu/4.8/include/stddef.h" 3 4
typedef long unsigned int size_t;

...
# 5 "hello.c" 2

int main(){
    printf("Hello World!");
    return 0;
}

Tips:
hello.i 中有很多这样的格式
# line filename flags,它表示下面行是由文件 filename 的第 line 行生成的。其中 flags 有 1,2,3,4 四种取值

? 1 代表新文件的开始

? 2 代表返回一个文件

? 3 代表下面的文本来自系统头文件,所以某些警告可以过滤掉

? 4 代表下面的文本应该包含在extern C块中 按照提示 stddef.h 文件中第 212 行有 size_t 的宏定义。

编译[4]

编译的过程是将某种编程语言写的源代码(这里特指 C 语言)转换成另一种编程语言(这里特指汇编语言)。前面我们将 hello.c 预处理成了 hello.i 文件,现在就要将 hello.i 文件编译成汇编文件 hello.s 。获取编译器输出的结果使用命令 gcc -S hello.i -o hello.s 。汇编结果见 hello.s[5]

    .file    "hello.c"
    .section    .rodata
  .LC0:
    .string    "Hello World!"
    .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    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
  .LFE0:
    .size    main, .-main
    .ident    "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
    .section    .note.GNU-stack,"",@progbits

汇编[6]

汇编的过程是将汇编语言编写的源码转换成可执行的机器代码,通常目标文件中包含至少两个段:代码段和数据段。其中代码段包含程序的指令,一般可读和可执行,不可写;数据段用来存放程序中所用到的各种全局变量或静态数据,一般可读,可写,可执行。获取汇编器输出的结果使用该命令 gcc -o hello.o -c hello.c ,由于 hello.o 是二进制文件,是无法阅读的。这里我们通过命令 objdump 来对二进制文件进行反汇编,查看里面内容。

// objdump -d hello.o 查看hello.o中代码段信息
hello.o:文件格式 elf64-x86-64

Disassembly of section .text:

0000000000000000 <main>:
   0:    55                   push   %rbp
   1:    48 89 e5             mov    %rsp,%rbp
   4:    bf 00 00 00 00       mov    $0x0,%edi
   9:    b8 00 00 00 00       mov    $0x0,%eax
   e:    e8 00 00 00 00       callq  13 <main+0x13>
  13:    b8 00 00 00 00       mov    $0x0,%eax
  18:    5d                   pop    %rbp
  19:    c3                   retq

hello.o中各段信息如下:

// objdump -h hello.o 显示hello.o中各个段的头部信息
hello.o:文件格式 elf64-x86-64

节:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         0000001a  0000000000000000  0000000000000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000000  0000000000000000  0000000000000000  0000005a  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  0000000000000000  0000000000000000  0000005a  2**0
                  ALLOC
  3 .rodata       0000000d  0000000000000000  0000000000000000  0000005a  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002c  0000000000000000  0000000000000000  00000067  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  0000000000000000  0000000000000000  00000093  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000038  0000000000000000  0000000000000000  00000098  2**3
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

链接[7]

链接的过程是将一个或多个由编译器或汇编器生成的目标文件链接库(静态库或动态库)形成可执行文件。其中静态库会和汇编生成的目标文件一起链接打包到可执行文件中(静态链接),它对函数库的链接是放在编译时期完成的。而动态库在程序编译时不会被链接到可执行文件中,而是在程序运行时才会被载入(动态链接)。不同的应用程序如果调用相同的库,那么在内存中只需要一份该共享库实例。获取链接器链接后的可执行文件使用命令 gcc hello.o -o hello 。如果想看该可执行文件依赖的库,可以使用命令 ldd hello

  # ldd hello 显示hello依赖的库
  linux-vdso.so.1 =>  (0x00007ffc85980000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f06c7a53000)
  /lib64/ld-linux-x86-64.so.2 (0x000055ad7be9e000)

参考文献

1.预处理[8]

2.预处理-行号标记[9]

3.编译器[10]

4.汇编[11]

5.链接器[12]

References

[1] hello.c: https://blog.haojunyu.com/atts/hello.c
[2] 预处理:
https://zh.wikipedia.org/wiki/C%E9%A2%84%E5%A4%84%E7%90%86%E5%99%A8
[3] hello.i:
https://blog.haojunyu.com/atts/hello.i
[4] 编译:
https://zh.wikipedia.org/wiki/%E7%B7%A8%E8%AD%AF%E5%99%A8
[5] hello.s:
https://blog.haojunyu.com/atts/hello.s
[6] 汇编:
https://zh.wikipedia.org/wiki/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80
[7] 链接:
https://zh.wikipedia.org/wiki/%E9%93%BE%E6%8E%A5%E5%99%A8
[8] 预处理:
https://zh.wikipedia.org/wiki/C%E9%A2%84%E5%A4%84%E7%90%86%E5%99%A8
[9] 预处理-行号标记:
https://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html
[10] 编译器:
https://zh.wikipedia.org/wiki/%E7%B7%A8%E8%AD%AF%E5%99%A8
[11] 汇编:
https://zh.wikipedia.org/wiki/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80
[12] 链接器:
https://zh.wikipedia.org/wiki/%E9%93%BE%E6%8E%A5%E5%99%A8

相关推荐

js中arguments详解

一、简介了解arguments这个对象之前先来认识一下javascript的一些功能:其实Javascript并没有重载函数的功能,但是Arguments对象能够模拟重载。Javascrip中每个函数...

firewall-cmd 常用命令

目录firewalldzone说明firewallzone内容说明firewall-cmd常用参数firewall-cmd常用命令常用命令 回到顶部firewalldzone...

epel-release 是什么

EPEL-release(ExtraPackagesforEnterpriseLinux)是一个软件仓库,它为企业级Linux发行版(如CentOS、RHEL等)提供额外的软件包。以下是关于E...

FullGC详解  什么是 JVM 的 GC
FullGC详解 什么是 JVM 的 GC

前言:背景:一、什么是JVM的GC?JVM(JavaVirtualMachine)。JVM是Java程序的虚拟机,是一种实现Java语言的解...

2024-10-26 08:50 citgpt

使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
跨域(CrossOrigin)

1.介绍  1)跨域问题:跨域问题是在网络中,当一个网络的运行脚本(通常时JavaScript)试图访问另一个网络的资源时,如果这两个网络的端口、协议和域名不一致时就会出现跨域问题。    通俗讲...

微服务架构和分布式架构的区别

1、含义不同微服务架构:微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并...

深入理解与应用CSS clip-path 属性
深入理解与应用CSS clip-path 属性

clip-pathclip-path是什么clip-path 是一个CSS属性,允许开发者创建一个剪切区域,从而决定元素的哪些部分可见,哪些部分会被隐...

2024-10-25 11:51 citgpt

HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
Request.ServerVariables 大全

Request.ServerVariables("Url")返回服务器地址Request.ServerVariables("Path_Info")客户端提供的路...

python操作Kafka

目录一、python操作kafka1.python使用kafka生产者2.python使用kafka消费者3.使用docker中的kafka二、python操作kafka细...

Runtime.getRuntime().exec详解

Runtime.getRuntime().exec详解概述Runtime.getRuntime().exec用于调用外部可执行程序或系统命令,并重定向外部程序的标准输入、标准输出和标准错误到缓冲池。...

promise.all详解 promise.all是干什么的
promise.all详解 promise.all是干什么的

promise.all详解promise.all中所有的请求成功了,走.then(),在.then()中能得到一个数组,数组中是每个请求resolve抛出的结果...

2024-10-24 16:21 citgpt

Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解

取消回复欢迎 发表评论: