gcc编译器

gcc是linux中c语言最常用的编译器

gcc工作步骤

  1. 处理所有预处理指令

  2. 把第一步的结果翻译成计算机认识的格式(编译,汇编)

  3. 把第二步的结果合并成 可执行文件(链接)

gcc 选项

  • -E 只处理预处理指令

  • -c 只完成预处理,编译,汇编工作,生成.o 目标文件

  • -o 指定生成的输出文件名为 file

  • -std=c89 决定用哪个版本来编译

    ​ -std=c99

    ​ 默认c89

  • -Wall 显示警告信息

1预处理:gcc -E hello.c -o hello.i
2
3编  译:gcc -S hello.i -o hello.s  
4
5汇  编:gcc -c hello.s -o hello.o
6
7链  接:gcc    hello.o -o hello //生成 hello 二进制可执行文件
8
9可以直接 gcc -c hello.c  //生成 hello.o 
.c C 语言文件
.i 预处理后的 C 语言文件
.s 编译后的汇编文件
.o 编译后的目标文件

注释

//叫行注释,注释的内容编译器是忽略的,注释主要的作用是在代码中加一些说明和解释,这样有利于代码的阅读

/**/叫块注释

块注释是C语言标准的注释方法

行注释是从C++语言借鉴过来的

c程序文件说明

c语言程序中以.c扩展名的叫源文件

c语言程序中以.h扩展名的叫头文件

include头文件包含

​ #include的意思是头文件包含,#include <stdio.h>代表包含stdio.h这个头文件

​ 使用C语言库函数需要提前包含库函数对应的头文件,如这里使用了printf()函数,需要包含stdio.h头文件

​ 可以通过man 3 printf查看printf所需的头文件

#include< > 与 #include ““的区别:

< > 表示系统直接按系统指定的目录检索

"” 表示系统先在 "” 指定的路径(没写路径代表当前路径)查找头文件,如果找不到,再按系统指定的目录检索

main函数

一个完整的C语言程序,是由一个、且只能有一个main()函数(又称主函数,必须有)和若干个其他函数结合而成(可选)。

main函数是C语言程序的入口,程序是从main函数开始执行。

{} 括号,程序体和代码块

{}叫代码块,一个代码块内部可以有一条或者多条语句

C语言每句可执行代码都是";“分号结尾

所有的#开头的行,都代表预编译指令,预编译指令行结尾是没有分号的

所有的可执行语句必须是在代码块里面

基本编码规范

  1. 一行最多包含一条语句

  2. 同级别的语句,最左列上下对齐

  3. 尽量使用空格和空行让程序看起来更加舒服

标识符(变量名)

规则:

  1. 第一个字母 英文或下划线开头
  2. 第二个字母开始英文,数字,下划线
  3. 大小写敏感
  4. 标识符长度不限制,如果很长,计算机只截取一部分使用
  5. 关键词不能作为标识符
  6. 使用驼峰法 或 下划线分割

printf标准函数

占位符

1printf("%d",1); 
2%d  整数
3%p  地址

数字的存储,存储区,变量概念

程序中所有的数字都必须记录在内存中

内存由大量的字节构成,每个字节可以单独记录一个数字

每个字节有一个编号,不同字节的编号不同,这个编号叫做字节的地址,所有字节的地址从0开始向上递增

字节地址有前后方向,地址小的在前地址大的在后

可以把几个相邻字节合并成一个整体记录一个数字

可以把内存里用来记录一个数字的所有字节叫做一个存储区

一个存储区只能记录一种类型的数字

存储区也有地址,它的地址是它包含的字节中最前面那个字节的地址

C语言程序中用变量代表存储区,对变量的操作实际就是对它所代表存储区的操作

变量在使用之前必须首先声明

可以在同一条语句里声明多个同类型的变量

赋值语句

赋值语句可以向变量里放一个数字

赋值语句需要使用赋值操作符(=)

赋值操作符左边的内容必须可以代表一个存储区(左值),变量就是一种左值

赋值语句可以把右边的数字放倒左边的存储区里

可以在声明变量的时候立刻对变量进行赋值,这叫做变量的初始化

C语言里所有变量都应该初始化

可以直接在程序中把变量当作数字使用,这个时候计算机会从变量里拿出数字,然后进行其他计算

变量名称即可以代表存储区,也可以存储区里的数字,有环境决定

存储区的地址也可以代表存储区

可以在变量名出前使用&得到存储区的地址

变量可以代表一个固定数字也可以代表一组数字

内存模型

寻址的最小单位 Byte 和 Byte 的最小单位 bit,1Byte = 8bit。

CPU 读写内存

CPU 在运作时,读取内存数据,首先要指定存储单元的地址。就是要确实读写哪段 数据。即要明确三件事。

  • 存储单元的地址(地址信息)
  • 器件的选择,读 or 写 (控制信息)
  • 读写的数据 (数据信息)

地址总线

其中 CUP 通过地址总线要寻址,指定存储单元。可见地址总线上能传送多少个 不同的信息,CPU 就可以对多少个存储单元进行寻址。有 10 根地址总线,就能传送 10 位二进制数据,也就是 2 的 10 次方 。最小位 0,最大为 1023。

CPU 地址总线的宽带决定了 CPU 的寻址能力。

数据总线

CPU 与内存或其他器件时行数据传达是通过数据总线来进行的。8 根数据总线一次可以传送 8 位二进制数据。16 根数据总线一次可以传 2 个字节。 数据总线的宽度决定了 CPU 和外界的数据传输速度。

控制总线

CPU 对外部部件的控制时通过控制总线来进行的。 控制总线是个总称,控制总 线是有不同的控制线来集合的。有多少根控制总线,就意味着 CPU 提供了对外部器 件的多少种控制。

控制总线的宽带决定了 CPU 对外部部件的控制能力。

原码,反码,补码

 1计算机都是补存储,手机,电脑,服务器
 2原码,反码,补码  正数都是一样的
 3原码      反码      补码(用一字节表示)
 4+7      00000111  00000111 00000111
 5-7      10000111  11111000 11111001
 6+0      00000000  00000000 00000000
 7-0      10000000  11111111 00000000
 8负数补码转换成10进制数,最高位不动,其余位数取反加1
 9
10补码的补码就是原码
1111111001
1210000110
1310000111=-7
14
15
160000 0111   +7  
171000 0111  -7  //原码第一位0代表正书,1代表负数,第一位符号位     87
18负数的原码,  原码是 正数原码的取反,(不包含符号位)
19负数的补码 =   他的反码+1   
20char  num= -17
210001 0001   +17的原码
221001 0001   -17的原码  
231110 1110   -17的反码
241110 1111  -17 的补码      EF  
25补码的运算速度 要快于 原码, 尤其是负数相关的。
26机器数的补码由原码得到,如果机器数是正数,则该机器数的补码与原码一样
27如果机器数是负数,则该机器数的补码是对它的原码(除符号位外)各位取反,并在????加1而得到的
28补码的补码就是原码

整数类型

数据类型 占用空间
short(短整型) 2字节
int(整型) 4字节
long(长整形) Windows为4字节,Linux为4字节(32位),8字节(64位)
long long(长长整形) 8字节

注意:

​ 需要注意的是,整型数据在内存中占的字节数与所选择的操作系统有关。虽然 C 语言标准中没有明确规定整型数据的长度,但 long 类型整数的长度不能短于 int 类型, short 类型整数的长度不能短于 int 类型。

​ 当一个小的数据类型赋值给一个大的数据类型,不会出错,因为编译器会自动转化。但当一个大的类型赋值给一个小的数据类型,那么就可能丢失高位。

整型常量 所需类型
10 代表int类型
10l, 10L 代表long类型
10ll, 10LL 代表long long类型
10u, 10U 代表unsigned int类型
10ul, 10UL 代表unsigned long类型
10ull, 10ULL 代表unsigned long long类型
打印格式 (占位符格式 ) 含义
%hd 输出short类型
%d 输出int类型
%l %ld 输出long类型
%ll 输出long long类型
%hu 输出unsigned short类型
%u 输出unsigned int类型
%lu 输出unsigned long类型
%llu 输出unsigned long long类型
%f 或%g float
%lf或%lg double

%f和%lf会保留小数点后面多余的0,%g和%lg不会保留

sizeof

64位机器

1char size = 1
2short size = 2
3int size = 4
4long size = 8
5long long size = 8
6float size = 4
7double size = 8
8long double size = 16
 1#include <stdio.h>
 2
 3int main(void) {
 4
 5	 printf("char size = %ld\n",sizeof(char));
 6	 printf("short size = %ld\n",sizeof(short));
 7     printf("int size = %ld\n",sizeof(int));
 8	 printf("long size = %ld\n",sizeof(long int));
 9	 printf("long long size = %ld\n",sizeof(long long));
10	 printf("float size = %ld\n",sizeof(float));
11	 printf("double size = %ld\n",sizeof(double));
12	 printf("long double size = %ld\n",sizeof(long double));
13	 return 0;
14 }

浮点数的表示方法和范围

一个浮点数(Floating Point Number)由三个基本成分构成:符号(Sign)、阶码 (Exponent)和尾数(Mantissa)。 通常,可以用下面的格式来表示浮点数:

S | P | M

11.1234567 
2
3s是符号 : 0
4P 是阶:   8
5M 是0-1的数值  0.11234567 
6
70.11234567 * 10^8
1# 32位机器
2float   32位
3double  64位

浮点数类型分为单精度浮点类型和双精度浮点类型

单精度:float 4字节

双精度:double 8字节

程序中带小数点的数字默认是double

如果带小数点点数字后加f 代表这个是单精度

c语言里允许程序员扩展新的数据类型

这些新的数据类型叫复合数据类型

c99类型引入bool概念

这个类型里只包含0和1两个整数,0为假1为真

任何一个整数都可以当作布尔值 0为假其他都为真

 1#include <stdio.h>
 2
 3
 4int main(void){
 5
 6	float fvar = 0.1234567654321;
 7	printf("fvar = %.13f\n",fvar);
 8	double dvar = 0.1234567654321;
 9	printf("dvar = %.13f\n",dvar);
10	return 0;
11}
12
13// fvar = 0.1234567686915 精度丢失
14// dvar = 0.1234567654321

字符类型

1个字节

字符类型名称是char

字符类型包含256个不同的整数,每一个整数可以用来代表一个字符(例如’a’,’^‘等)

这些整数和字符可以互相替代

ASCII码表列出的所有整数和字符的对应关系

​ ‘a’ 97

​ ‘A’ 65

​ ‘0’ 48

ASCII 码表里所有小写英文字母是连续排列的,并且’a’ 对应的整数最小,字母’z’对应的整数最大(‘a’-‘z’中最大)

所有大写英文字母和阿拉伯数字也是连续排列的

1'd'-'a' == 3
2'D'-'A' == 3
3'3'-'0' == 3  

字符类型里的所有字符被分成两组,每组包含128个

其中一组字符和整数之间的对应关系在所有计算机上都一样,他们对应的整数范围从0开始到127为止

另外一组字符和整数之间的对应关系在不同计算机上可能不同,它们对应的整数有可能从-128到-1,

也有可能从128-255

特殊字符

1'\n' 换行字符
2'\r' 回车字符
3'\t' 制表符
4'\"' 代表字符"
5'\\' 代表字符\
 1#include<stdio.h>
 2
 3
 4int main(void){
 5
 6	char val = 'a';
 7	for(;val<='z';val++){
 8		printf("%c\t",val);
 9	}
10	return 0;
11}

常量(Constant)

常量是程序中不可改变的量,常以字面量(Literal),或者宏(Macro)的方式出现。 主要 用于赋值或是参与计算,并且常量也是有类型的。

常量类型

整型常量

  • 十进制表示、十六进制表示 、八进制表示

实型常量

  • 小数形式
    • 由数字和小数点组成,必须有小数点。 例:4.23、0.15、.56、78.、0.0
  • 指数形式
    • 以幂的形式表示,以字母 e 或 E 后跟一个以 10 为底的幂数
    • (1)字母 e 或 E 之前后必须要有数字。 (2)字母 e 或 E 后面的指数必须为整数,字母 e 或 E 的前后及数字之间不得有 空格。 默认是 double 型,后缀为"f"或"F"即表示该数为 float 型,后缀"l"或"L"表示 long double 型。

隐式转换

整型提升

char short int 等类型在一起运算时,首先提升到 int,这种现象叫作整型提升。整 型提升的原更换是符号扩充

混合提升

在进行运算时,以表达式中最长类型为主,将其他类型位据均转换成该类型,如:

(1)若运算中最大范围为 double,则转化为 double。(10e308)

(2)若运算中最大范围为 float 则转化为 float。(10e38)

(3)若运算中最大范围为 long long 则转化为 long long。(2的64次)

(4)若运算中最大范围为 int 则转化为 int。(2的32次)

(5)若运算中有 char short 则一并转化为 int。

赋值转化

整型和实型之间是可以相互赋值的。赋值的原则是,一个是加零,一个是去小数位。

 1#include <stdio.h>
 2
 3// 赋值转换
 4int main(void) {
 5
 6	int a = 5;
 7
 8	float b = 10.5;
 9	int aa = 5;
10	float bb = 10.5;
11	a = b;
12	printf("a = %d\n",a);
13        bb = aa;
14	 printf("bb = %f\n",bb);
15	return 0;
16}
17//a = 10
18//bb = 5.000000

隐式转化规则

有低到高

char、short -> int -> long -> long long -> float -> double

浮点数比较

加上0.000001这样的值去比较,就认为是相等。

 1#include <stdio.h>
 2int main() {
 3	// double tmp = 0.3; 
 4	//0.3 0.7 坑 
 5	// printf("%d\n", (int)((tmp+0.00000001) *10)); 
 6	float a = 0.1; 
 7	if(a*10 >= 1.0 - 0.0000001 && a*10 <= 1.0+0.000001) { 
 8	  printf("===="); 
 9	}
10}