C复习-基础知识

参考:

  1. 里科《C和指针》
  2. Bryant, Hallaron 《深入理解计算机系统》
  3. 何昊,叶向阳《程序员面试笔试宝典》

从hello.c到可执行文件hello

在Unix系统中,从源文件到目标文件的转化是由编译器驱动程序完成的:

root> gcc -o hello hello.c

这个转化可以分为4个阶段,执行这4个阶段的预处理器、编译器、汇编器和链接器一起构成了编译系统compilation system。
在这里插入图片描述

1)预处理阶段:cpp根据#开头的命令,修改原始程序。比如#include <stdio.h>就是读取系统头文件stdio.h的内容,直接插入到程序中。输出结果以 .i 后缀

2)编译阶段:编译器ccl将文本翻译成汇编语言。汇编语言是一个通用的低级机器语言指令。输出结果以.s后缀

3)汇编阶段:汇编器as将汇编语言翻译成机器语言,并且打包成可重定位目标程序relocatable object program的格式,输出结果以.o后缀

4)链接阶段:因为hello调用了printf函数,而它是标准C库的一个函数,因而有一个名为printf.o的目标程序,因此需要使用链接器合并,最终变成可执行文件。

注意:
1)一般UNIX系统中目标文件名后缀是.o,但MS-DOS中则是.obj

2)UNIX系统中,如果编译的源文件只有一个,中间产生的.o文件会在产生完可执行文件.out后被自动删除;但如果源文件有多个,不会删除,这样的话如果改动某个源程序,编译器会只重新编译改动过的,后面再一起链接。MS-DOS在单个源文件时也不会删除目标文件

在这里插入图片描述

linux键入./hello

1)键盘键入后,USB控制器将输入通过I/O总线经过I/O桥读入寄存器,然后再经过I/O桥、内存总线存入内存。

2)当键入回车时,shell知道输入结束了,随后加载可执行的hello文件,这些指令将代码和数据从磁盘复制到主存(使用直接存储器存取DMA技术能将数据直接从磁盘复制到主存)

3)代码和数据加载完成后,CPU开始执行指令,最后将输出“hello, world\n”从主存复制到寄存器文件,再经过总线接口、I/O桥到达显示器

在这里插入图片描述

GNU项目

GNU项目(GNU’s Not Unix,1984年提出)已经开发出一个包含Unix操作系统的所有主要部件的环境,但内核除外(由Linux项目独立发展)。GNU环境包括EMACS编辑器、GCC编译器(GNU Compiler Collection)、GDB调试器、汇编器、链接器、处理二进制文件的工具喝其他一些不见。

GCC可以使用不同版本的C语言编译程序,参数是-std=xx

C版本GCC命令行值
GNU89无,-std=gnu89
ANSI, ISO C90-ansi, -std=c89
ISO C99-std=c99
ISO C11-std=c11

C90被称为C89是因为它的标准化工作是从1989年开始的。

ANSI C的任何一种实现中,都有两种环境。一是翻译环境translation environment,即将源代码转换为可执行的机器指令;二是执行环境execution environment,用于实际执行代码。这两种环境不必在同一台机器上。交叉编译器cross compiler就是在一台机器上编译,但是产生的可执行代码可以在不同机器使用。独立环境freestanding environment是指不存在操作系统的环境,比如嵌入式系统(微波炉控制器)


编码规范

字符

三字母词trigraph:原本是为了减少字符集规模,不过其实不太常用。

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

void main() {
    printf("??) ??( ??! ??< ??> ??' ??= ??/ ??-");
}

编译+执行。默认是不开启的,gcc要使用-trigraphs开启

gcc -o hello hello.c -trigraphs
./hello
] [ | { } ^ #  ~

注释

如果需要注释掉一段代码,如果代码里有长段注释,可能/**/的效果不好,此时可以考虑#if和#endif,这样更安全

#if 0
statements //要注释掉的代码
#endif

数据

C中只有4种基本数据类型:整型、浮点、指针和聚合类型(如array和struct)。

整型包括:字符、短整型、整型和长整型。都有signed有符号和unsigned无符号两种版本。

标准只规定了长整型至少应该跟整型一样长,整型至少应该跟短整型一样长。因此,缺省的int是16位还是32位,通常是由编译器决定的。一般来说是字长。limits.h定义了不同的整数类型的最大最小值。

char本质是小整型值,缺省的char可能是signed char或者unsigned char,所以为了可移植起见,char变量的值应该在两者的交集中(比如ASCII),也可以显式声明(某些机器处理signed char更快,但是一些库函数的参数声明是char,显式声明可能有兼容性问题)。

int ch;
while( (ch = getchar()) != EOF && ch != '\n' );

为什么要把ch声明为int?EOF是一个整型值,如果使用char可能导致EOF解释错误

在整型字面值后添加L或l可以让整数被解释long,U或u则可以解释为unsigned,UL也可以组合

十进制整型字面值在缺省情况下,它的类型是能容纳这个值的最短类型。八进制需要以0开头,十六进制需要以0x开头。当然,使用\转义的时候,八进制格式为\ddd,十六进制格式为\xddd(此时算字符常量,所以输出是字符)

printf("\127\n"); // W
printf("\x00f\n"); // 如果数值较大会报错

宽字符常量wide character literal:当运行时环境支持一种宽字符集时,可以使用。比如使用Unicode字符集

#include <windows.h>
...
wchar_t c = L'Xs';
MessageBoxW(0, L"你好世界", L"I am", 0); // MessageBoxW默认以宽字符处理

在这里插入图片描述

枚举enumerated类型

枚举类型的值是符号常量,不是字符串。声明枚举类型的时候,实际就给符号名赋予了整数值,如下面的CUP实际就是0,PINT是1,以此类推。

enum Jar_Type {CUP, PINT, QUART, GALLON}; // 声明类型
enum Jar_Type milk_jug; // 声明枚举类型的变量

当然也可以给部分符号赋值,对于没被显式赋值的符号,就是前一个符号名的值+1

enum Jar_Type {CUP = 8, PINT, QUART, GALLON}; // 声明类型
enum Jar_Type milk_jug = PINT; // 声明枚举类型的变量
printf("%d", milk_jug); // 9

浮点数

包括float、double和long double,它们的范围存储在float.h中。浮点数必须至少有一个小数点或者指数(E/e),浮点数字面值缺省情况下是double,除非后跟L/l表示是long double,或者后跟F/f表示是float。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值