gcc编译器
gcc是linux中c语言最常用的编译器
gcc工作步骤
-
处理所有预处理指令
-
把第一步的结果翻译成计算机认识的格式(编译,汇编)
-
把第二步的结果合并成 可执行文件(链接)
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语言每句可执行代码都是";“分号结尾
所有的#开头的行,都代表预编译指令,预编译指令行结尾是没有分号的
所有的可执行语句必须是在代码块里面
基本编码规范
-
一行最多包含一条语句
-
同级别的语句,最左列上下对齐
-
尽量使用空格和空行让程序看起来更加舒服
标识符(变量名)
规则:
- 第一个字母 英文或下划线开头
- 第二个字母开始英文,数字,下划线
- 大小写敏感
- 标识符长度不限制,如果很长,计算机只截取一部分使用
- 关键词不能作为标识符
- 使用驼峰法 或 下划线分割
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}