C语言的标准演变及编程机制

news2024/9/20 18:40:38

目录

C语言的标准

K&R C

C89

C90

C99

C11

C18

C2x

C语言的编程机制

示例

1.预处理(Preprocessing)

2.编译(Compilation)

3.汇编(Assemble)

4.链接(Linking)

结语

参考文献


C语言的标准

c语言标准的发展主要分为以下几个阶段:

K&R C

1978年,丹尼斯·里奇(Dennis Ritchie)和布莱恩·科尔尼干(Brian Kernighan)出版了一本书,名叫《The C Programming Language》。这本书被C语言开发者们称为“K&R”,很多年来被当作C语言的非正式的标准说明。人们称这个版本的C语言为“K&R C”。

C89

为统一C语言版本,1983年美国国家标准局(American National Standards Institute,简称ANSI)成立了一个委员会,来制定C语言标准。1989年C语言标准被批准,被称为ANSI X3.159-1989 “Programming Language C”。这个版本的C语言标准通常被称为ANSI C。又由于这个版本是 89年完成制定的,因此也被称为C89。

C90

后来ANSI把这个标准提交到ISO(国际化标准组织),1990年被ISO采纳为国际标准,称为ISO C。又因为这个版本是1990年发布的,因此也被称为C90。所以ANSI C、ISO C、C89、C90这4个标准的内容其实是一样的。

C99

在ANSI C标准确立之后,C语言的规范在很长一段时间内都没有大的变动。1995年C程序设计语言工作组对C语言进行了一些修改,成为后来的1999年发布的ISO/IEC 9899:1999标准,通常被成为C99。但是各个公司对C99的支持所表现出来的兴趣不同。当GCC和其它一些商业编译器支持C99的大部分特性的时候,微软和Borland却似乎对此不感兴趣。

C11

在2011年12月,ANSI采纳了ISO/IEC 9899:2011标准,这个标准通常即C11。

C18

2018年6月发布的ISO/IEC 9899:2018标准,这个标准被称为C18,是目前最新的C语言编程标准,该标准主要是对C11进行了补充和修正,并没有引入新的语言特性。

C2x

下一个版本的C语言标准,预计将于2022年12月1日完成。

C语言的编程机制

C语言程序从源代码到二进制行程序都经历了那些过程?本文以Linux下C语言的编译过程为例,讲解C语言程序的编译过程。

编写hello world C程序:

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

编译过程只需:

$ gcc hello.c # 编译
$ ./a.out # 执行
hello world!

这个过程如此熟悉,以至于大家觉得编译事件很简单的事。事实真的如此吗?我们来细看一下C语言的编译过程到底是怎样的。

上述gcc命令其实依次执行了四步操作:1.预处理(Preprocessing), 2.编译(Compilation), 3.汇编(Assemble), 4.链接(Linking)。

示例

为了下面步骤讲解的方便,我们需要一个稍微复杂一点的例子。假设我们自己定义了一个头文件mymath.h,实现一些自己的数学函数,并把具体实现放在mymath.c当中。然后写一个test.c程序使用这些函数。程序目录结构如下:

├── test.c
└── inc
    ├── mymath.h
    └── mymath.c

程序代码如下:

// test.c
#include <stdio.h>
#include "mymath.h"// 自定义头文件
int main(){
    int a = 2;
    int b = 3;
    int sum = add(a, b); 
    printf("a=%d, b=%d, a+b=%d\n", a, b, sum);
}

头文件定义:

// mymath.h
#ifndef MYMATH_H
#define MYMATH_H
int add(int a, int b);
int sum(int a, int b);
#endif

头文件实现:

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

1.预处理(Preprocessing)

预处理用于将所有的#include头文件以及宏定义替换成其真正的内容,预处理之后得到的仍然是文本文件,但文件体积会大很多。gcc的预处理是预处理器cpp来完成的,你可以通过如下命令对test.c进行预处理:

gcc -E -I./inc test.c -o test.i

或者直接调用cpp命令

$ cpp test.c -I./inc -o test.i

上述命令中-E是让编译器在预处理之后就退出,不进行后续编译过程;-I指定头文件目录,这里指定的是我们自定义的头文件目录;-o指定输出文件名。

经过预处理之后代码体积会大很多:

X文件名文件大小代码行数
预处理前test.c146B9
预处理后test.i17691B857

预处理之后的程序还是文本,可以用文本编辑器打开。

2.编译(Compilation)

这里的编译不是指程序从源文件到二进制程序的全部过程,而是指将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程。编译的指定如下:

$ gcc -S -I./inc test.c -o test.s

上述命令中-S让编译器在编译之后停止,不进行后续过程。编译过程完成后,将生成程序的汇编代码test.s,这也是文本文件,内容如下:

// test.c汇编之后的结果test.s
    .file   "test.c"
    .section    .rodata
.LC0:
    .string "a=%d, b=%d, a+b=%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    movl    $2, 20(%esp)
    movl    $3, 24(%esp)
    movl    24(%esp), %eax
    movl    %eax, 4(%esp)
    movl    20(%esp), %eax
    movl    %eax, (%esp)
    call    add 
    movl    %eax, 28(%esp)
    movl    28(%esp), %eax
    movl    %eax, 12(%esp)
    movl    24(%esp), %eax
    movl    %eax, 8(%esp)
    movl    20(%esp), %eax
    movl    %eax, 4(%esp)
    movl    $.LC0, (%esp)
    call    printf
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret 
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

请不要问我上述代码是什么意思!-_-

3.汇编(Assemble)

汇编过程将上一步的汇编代码转换成机器码(machine code),这一步产生的文件叫做目标文件,是二进制格式。gcc汇编过程通过as命令完成:

$ as test.s -o test.o

等价于:

gcc -c test.s -o test.o

这一步会为每一个源文件产生一个目标文件。因此mymath.c也需要产生一个mymath.o文件

4.链接(Linking)

链接过程将多个目标文以及所需的库文件(.so等)链接成最终的可执行文件(executable file)

命令大致如下:

$ ld -o test.out test.o inc/mymath.o ...libraries...

结语

经过以上分析,我们发现编译过程并不像想象的那么简单,而是要经过预处理、编译、汇编、链接。尽管我们平时使用gcc命令的时候没有关心中间结果,但每次程序的编译都少不了这几个步骤。也不用为上述繁琐过程而烦恼,因为你仍然可以:

$ gcc hello.c # 编译
$ ./a.out # 执行

参考文献

1.https://www3.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html
2.http://www.trilithium.com/johan/2005/08/linux-gate/
3.https://gcc.gnu.org/onlinedocs/gccint/Collect2.html

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/536522.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】类的属性和行为

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01;时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、封装 3、总结 1、缘起 C 面向对象的三大特性为&#xff1a;封装&#xff0c;继承和多态。C 认为 万事万物皆为对象&a…

刷题day66:最后一块石头的重量II

题意描述&#xff1a; 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果如下&a…

SRM系统是什么?有什么作用?企业如何应用SRM系统?

一、什么是SRM系统 到底什么是SRM系统&#xff1f; SRM&#xff1a;Supplier Relationship Management&#xff0c;供应商关系管理。 SRM是一种致力于实现与供应商建立和维持长久、紧密伙伴关系的管理思想和软件技术的解决方案&#xff0c;实现从采购需求的下达、审批、招标以…

SpringBoot定时任务里的多线程

SpringBoot定时任务里的多线程 提示前言遇到的问题验证与解决验证单线程执行单任务分析代码及结果 单线程执行多任务 解决实现单任务的多线程为每个任务创建一个子线程 解决多任务的多线程设定固定容量线程池动态设定容量线程池固定线程池和动态线程池的选择 简单总结借鉴及引用…

基于卷积的图像分类识别(三):VGGNet

系列文章目录 本专栏介绍基于深度学习进行图像识别的经典和前沿模型&#xff0c;将持续更新&#xff0c;包括不仅限于&#xff1a;AlexNet&#xff0c; ZFNet&#xff0c;VGG&#xff0c;GoogLeNet&#xff0c;ResNet&#xff0c;DenseNet&#xff0c;SENet&#xff0c;Mobile…

分布式锁解决方案_Redis实现的分布式锁原理

获取锁 互斥&#xff1a;确保只有一个线程获得锁 添加锁 利用setnx的互斥性 启动Redis的我们进行测试看看怎么能某个数据来获得锁 setnx命令表示上锁只要是aa被创建出来我们不删除aa或者设定时间自动删除&#xff0c;那么这把锁就不能被释放开 释放锁 手动释放锁 如果我们…

Ubuntu20.04安装VTK8.2

Ubuntu20.04安装VTK8.2 相关依赖的安装安装步骤参考文献相关依赖的安装 sudo apt install checkinstall sudo apt-get install cmake-curses-gui sudo apt-get install freeglut3-dev sudo apt install libqt5x11extras5-dev sudo apt install cmake-qt-gui安装步骤 1、从官网…

【运筹优化】元启发式算法详解:禁忌搜索算法(Tabu Search,TS)+ 案例讲解代码实战

文章目录 一、介绍二、The Classical Vehicle Routing Problem 经典的车辆路径问题三、基本概念3.1 历史背景3.2 禁忌搜索3.3 搜索空间和邻域结构3.4 Tabus 禁忌3.5 解禁准则3.6 简单禁忌搜索的模板3.7 终止标准3.8 概率TS和候选列表 四、中级概念4.1 搜索强化4.2 多样化4.3 允…

Maven基础使用

Maven 学习目标 理解Maven的用途掌握Maven的基本操作掌握Maven如何创建Web项目 Maven是什么 面临问题 在学习Maven之前&#xff0c;我们先来看一下我们现在做的项目都有哪些问题。假设你现在做了一个crm的系统&#xff0c;项目中肯定要用到一些jar包&#xff0c;比如说myb…

opencv_c++学习(九)

一、图像二值化 固定阈值二值化 threshold ( lnputArray src, outputArray dst,double thresh, double maxvall, int typesrc:待二值化的图像&#xff0c;图像只能是CV_8U和CV_32F两种数据类型。对于图像通道数目的要求和选择的二值化方法相关。 dst:二值化后的图像&#xff…

【数据结构】哈希表上——开放寻址法

文章目录 前言映射哈希冲突开放寻址法思路分析结构分析函数实现插入删除寻找 结语 前言 大家好久不见&#xff0c;今天来讲解一下哈希表的基本原理并使用开放寻址法实现简单哈希表。 映射 哈希表的实现思路就是将一组数据映射成另外一组可以直接查找的数据&#xff0c;假如有…

【LeetCode】每日一题:链表部分经典题型

文章目录 1.反转链表2.链表的中间节点3.合并两个有序链表4.相交链表5.环形链表6.环形链表Ⅱ ​&#x1f47b;内容专栏&#xff1a;《LeetCode刷题专栏》 &#x1f428;本文概括&#xff1a;归纳链表部分经典题型。206.反转链表、876.链表的中间节点、21.合并两个有序链表、160.…

如何优雅地彻底解决 antd 全局样式问题

背景 由于某些原因&#xff0c;我们团队负责在组件 上做二次开发&#xff0c;简单理解就是封装组件&#xff0c;组件库选择了 antd&#xff0c;尴尬的是引入之后发现&#xff0c;父组件 自身是带一套全局样式的&#xff0c;而 子组件antd 又带了一套全局样式&#xff0c;导致 子…

字节原来这么容易进,是面试官放水,还是公司实在是太缺人?

本人211非科班&#xff0c;之前在字节和腾讯实习过&#xff0c;这次其实没抱着什么特别大的希望投递&#xff0c;没想到字节可以再给我一次机会&#xff0c;还是挺开心的。 本来以为有个机会就不错啦&#xff01;没想到能成功上岸&#xff0c;在这里要特别感谢帮我内推的同学&…

《程序员面试金典(第6版)》面试题 16.24. 数对和

题目描述 设计一个算法&#xff0c;找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。 题目传送门 示例 1: 输入: nums [5,6,5], target 11 输出: [[5,6]]示例 2: 输入: nums [5,6,5,6], target 11 输出: [[5,6],[5,6]]提示&#xff1a; nums.length &…

基于物联感知和GNSS技术的铁塔安全监测解决方案

监测背景 电力铁塔是承载电力供应的重要设施&#xff0c;它的安全性需要得到可靠的保障。但是铁塔一般安装在户外&#xff0c;分布广泛&#xff0c;且有很多安装在偏远地区&#xff0c;容易受到自然、人力的影响和破环。因此需要使用辅助的方法实时监控通信塔的安全状态&#x…

5th-Generation Mobile Communication Technology(二)

目录 一、5G/NR 1、 快速参考&#xff08;Quick Reference&#xff09; 2、5G Success 3、5G Challenges 4、Qualcomm Videos 二、PHY and Protocol 1、Frame Structure 2、Numerology 3、Waveform 4、Frequency Band 5、BWP 6、Synchronization 7、Beam Management 8、CSI Fra…

一文4000字从0到1用WebDriver+Selenium实现浏览器自动化

前言 Selenium是一款可以自动化操作浏览器的开源项目&#xff0c;最初的目的是浏览器功能的自动化测试&#xff0c;但是随着项目的发展&#xff0c;人们根据它的特性也用来做一些更多的有意思的功能而不仅仅是UI的自动化测试工具。就像Selenium官方网站上描述的那样&#xff0…

chatgpt怎么搭建,以及怎么接入企业微信工作台

gpt目前也用了一段时间了&#xff0c;用起来只能说越来越顺手&#xff0c;然后集成到企业微信让公司全部成员都用起来了。 使用界面如下&#xff1a; 主界面 功能&#xff1a; 1、通过企业微信认证后访问使用&#xff0c;防止非公司人员入侵 2、记录用户姓名和提问内容&#x…