一、C语言简介
1、 BCPL-> New B-> C-> UNIX-> Minx -> Linux->gcc
C语言诞生:1971~73年,美国贝尔实验室,肯.汤普逊和丹尼斯.里奇 (二选一:丹尼斯.里奇)
C是为了编写操作系统诞生的,天然适合对硬件编程,以运行速度快而闻名,而且C语言非常适合编写数据结构和算法 (早期C为科学家使用,且出现时间太早,所以为了运行快而将一些“人皆熟知”的规则要求不加约束,也是c比c++更松散的原因之一)
优点:语法简单,只有32个关键字,执行速度快能媲美汇编语言的执行速度,也适合用于实现算法
缺点:学习难度大,需要对内存,操作系统有一定的了解,由于出现早个人计算机还没有普及,所以在设计时没有为普通人考虑,有一些语法上的陷阱和缺陷,(如:数组越界访问,未初始化变量,内存泄漏,悬空指针(野指针),字符串处理问题,运算符优先级等)没有大型软件公司在背后支持,可用软件库比较少
C可参考教材《C语言三剑客》《C陷阱与缺陷》《C与指针》
C语法自由,自由意味着危险,而自由源于自律
C89语法标准,旧的系统中的默认标准
C99语法标准,对C语言的扩展和增强 -std=gnu99
C11语法标准,全新的升级
二、C语言第一个程序
#include
程序员所编写的代码都不是标准的c代码,需要一段程序翻译成标准c代码,负责翻译的程序叫预处理器,翻译的过程叫做预处理,被翻译的语句叫做预处理指令,以#开头的语句都是预处理指令 #include的功能就是把一个头文件导入(拷贝cp)到当前位置
#include
从系统指定的路径下查找并加载头文件
#include "xxx.h"
先从当前工作路径查找,如果找不到再从系统指定的路径查找并加
stdio.h
头文件:以.h结尾,里面存储的是一些辅助性的代码,绝大多数都是函数的声明
源文件:以.c结尾,里面存储的是实际的功能性的代码
C语言标准委员会为C语言以函数形式提供一些基础功能,这些函数都被封装在libc.so。 然后通过很多的头文件对不同功能的标准库函数进行了说明,stdio.h就是其中之一,常用:stdlib.h string.h
int main()
C语言中函数是管理代码的最小单位,一个函数就是一个功能的实现
main函数是程序的入口,有且只有一个
int 是一种数据类型,表示main函数的执行结果是一个整数值
return 的作用:
1、返回一个执行结果给函数的调用者
正数 出现异常(别人导致)
0 一切正常
负数 出现错误(自己导致)
echo $? 查看main函数的返回值
2、提前结束函数
main函数遇到return语句程序结束
main函数是由操作系统调用,所以它的返回值给了操作系统,反应了程序是如何结束的
{
printf("Hello World!\n");
return 0;
}
printf/scanf 是标准库的输出输入函数,一般用于调试代码
printf("---------\n");
xxxx
printf("---------\n");
转义字符:键盘上有一些不好直接表示的符号,通过一些特殊字符搭配来表示,这种字符搭配称为转移字符
\n 换行
\t 制表符,Tab键
\a 铃响
\b 光标后退一个字符 printf("\b \b") 退格效果
\r 光标回到行首
%% 显示一个%
\\ 显示一个\
注意:C语言是以分号作为一行代码的结束标志,使用大括号来划分代码
注意:必须使用英文输入法
三、编译器:
它是一个负责翻译代码的程序,它负责把人类能看懂的代码翻译成计算机能理解的二进制指令,它由 1预处理器、2编译器、3汇编器、4链接器组成,统称为编译器。 gcc是GNU组织为了编译Linux内核代码而开发一款C语言编译器,默认语法标准是C99或者C89
gcc编译器常用的参数:
-E 预处理
-S 生成汇编文件
-c 生成目标文件
-o 设置编译结果的名字
-I (大写i)设置要导入的头文件的路径
-l (小写L)设置要链接的库名,例如:使用sqrt、pow等数学函数时就需要链接数学库 -lm
-L 设置要链接的库的路径
-D 在编译时定义宏
-g 编译时添加调试信息,这样编译出的程序可以用gdb调试。
-Wall 显示所有警告,编译器会以更严格的标准检查代码
-Werror 把警告当错误处理
-std 指定编译器要遵循的语法标准,c89,c99,c11,当前系统默认的是c99标准
gcc编译器把C代码变成可执行程序的过程:
1、把程序员所编写的代码进行预处理
gcc -E hello.c 把预处理的结果显示到屏幕上
gcc -E hello.c -o hello.i 会生成以.i结尾的预处理文件
2、把预处理的结果翻译成汇编代码
gcc -S hello.i 会生成以.s结尾的汇编文件
3、把汇编代码翻译成二进制指令
gcc -c hello.s 会生成以.o结尾的目标文件
4、把若干个文件目标文件、库文件合并成可执行文件
gcc a.o b.o c.o ... 默认会生成a.out可执行文件,也可以使用-o指定可执行文件的名字。
gcc a.o b.o c.o -o hello
./hello 执行程序,也可以编译执行:gcc a.o b.o c.o && ./a.out
注意:gcc hello.c 就包含了以上四个步骤,了解这个步骤是我们后续学习预处理指令、多文件编 程、静态库、动态库的基础。
C语言中的文件类型:
.c 源文件 功能性代码
.h 头文件 对.c文件的说明
.i 预处理文件
.s 汇编文件
.o 目标文件
.h.gch 头文件的编译结果文件,用于检查自定义的头文件是否有语法错误,会被优先使用(建议立即删除)
.so 共享库文件
.a 静态库文件
四、C语言中得基本的数据类型
C语言为什么要把数据分为不同的类型:
1、现实中的数据就是自带类别属性
数据存储计算机中需要耗费存储空间(内存、硬盘),在编程语言中把数据按照使用范围、特点划分为 不同的种类,解决什么问题使用什么类型的数据,这样可以
2、节约存储空间、提高运算速度。
存储空间的单位:
Bit 比特 存储一个二进制位,只能存储0或者1,计算机存储数据的最小单位
Byte 字节 存储八个二进制位,计算机存储数据的基本单位
Kb 1024字节
Mb 1024Kb
Gb 1024Mb
Tb 1024Gb
Pb 1024Tb
C语言中数据分为两大类别:自建(程序员自己设计的)、内建(C语言自带的)
sizeof:可以计算类型/变量所占内存字节数
整型:
signed有符号整型:
最高位的二进制用于表示正负,0代表正数,1代表负数。
由于有符号整型使用较多,所以编译器默认signed可以省略,不加就代表加。
类型名 字节数 数据范围
signed char 1 -128~127
signed short 2 -32768~32767
signed int 4 正负20亿
signed long 4(32位)/8(64位)
signed long long 8 正负9开头19位整数
注意:signed不加就代表加了
char == signed char
unsigned无符号整型:
所有的二进制位都用来表示数据,只能表示正数。
二进制最高位作为数据位
类型名 字节数 数据范围
unsigned char 1 0~255
unsigned short 2 0~65535
unsigned int 4 0~40亿
unsigned long 4(32位)/8(64位)
unsigned long long 8 0~1开头的20位整数
注意:由于定义无符号数据时比较麻烦,标准库中把这些类型重新定义成了新的类型名
需要包含头文件
uint8_t uint16_t uint32_t uint64_t
int8_t int16_t int32_t int64_t
浮点型:
float 4 单精度浮点型
double 8 双精度浮点型
long double 12(32位)/16(64位)
注意:采用科学计数法、在二进制与真实数据之间需要进行换算过程,因此浮点数的运算使用速度比整型要慢得多,所以编程时尽量使用整型
注意:大部分操作系统只对小数点后6位有效
小数点是浮动的,也就是带小数点的数据,采用科学计算数法存储,由符号位+指数域+小数域组成, 这种存储格式,运算速度慢、又不太准确,所以尽量少用。
模拟型:
字符型:char
字符就是图案或符号,字符在内存中依然存储成整数,需要显示成字符时,操作系统会根据ASCII码表中的对应关系把整数显示成对应的符号或图案
'\0' 0 特殊字符 空字符
'0' 48
'A' 65
'a' 97
char想要显示成整数时:%hhd
想要显示成字符时: %c
布尔型:bool
先有的C语言后有的bool类型,C语言中不可能有真正的布尔类型,都是在
bool : true false
ASCLL
十进制 | 八进制 | 十六进制 | 字符 | 描述 |
---|---|---|---|---|
0 | 0 | 00 | NUL | |
1 | 1 | 01 | SOH | start of header |
2 | 2 | 02 | STX | start of text |
3 | 3 | 03 | ETX | end of text |
4 | 4 | 04 | EOT | end of transmission |
5 | 5 | 05 | ENQ | enquiry |
6 | 6 | 06 | ACK | acknowledge |
7 | 7 | 07 | BEL | bell |
8 | 10 | 08 | BS | backspace |
9 | 11 | 09 | HT | horizontal tab |
10 | 12 | 0A | LF | line feed |
11 | 13 | 0B | VT | vertical tab |
12 | 14 | 0C | FF | form feed |
13 | 15 | 0D | CR | carriage return |
14 | 16 | 0E | SO | shift out |
15 | 17 | 0F | SI | shift in |
16 | 20 | 10 | DLE | data link escape |
17 | 21 | 11 | DC1 | no assignment, but usually XON |
18 | 22 | 12 | DC2 | |
19 | 23 | 13 | DC3 | no assignment, but usually XOFF |
20 | 24 | 14 | DC4 | |
21 | 25 | 15 | NAK | negative acknowledge |
22 | 26 | 16 | SYN | synchronous idle |
23 | 27 | 17 | ETB | end of transmission block |
24 | 30 | 18 | CAN | cancel |
25 | 31 | 19 | EM | end of medium |
26 | 32 | 1A | SUB | substitute |
27 | 33 | 1B | ESC | escape |
28 | 34 | 1C | FS | file seperator |
29 | 35 | 1D | GS | group seperator |
30 | 36 | 1E | RS | record seperator |
31 | 37 | 1F | US | unit seperator |
32 | 40 | 20 | SPC | space |
33 | 41 | 21 | ! | |
34 | 42 | 22 | " | |
35 | 43 | 23 | # | |
36 | 44 | 24 | $ | |
37 | 45 | 25 | % | |
38 | 46 | 26 | & | |
39 | 47 | 27 | ' | |
40 | 50 | 28 | ( | |
41 | 51 | 29 | ) | |
42 | 52 | 2A | * | |
43 | 53 | 2B | + | |
44 | 54 | 2C | , | |
45 | 55 | 2D | - | |
46 | 56 | 2E | . | |
47 | 57 | 2F | / | |
48 | 60 | 30 | 0 | |
49 | 61 | 31 | 1 | |
50 | 62 | 32 | 2 | |
51 | 63 | 33 | 3 | |
52 | 64 | 34 | 4 | |
53 | 65 | 35 | 5 | |
54 | 66 | 36 | 6 | |
55 | 67 | 37 | 7 | |
56 | 70 | 38 | 8 | |
57 | 71 | 39 | 9 | |
58 | 72 | 3A | : | |
59 | 73 | 3B | ; | |
60 | 74 | 3C | < | |
61 | 75 | 3D | = | |
62 | 76 | 3E | > | |
63 | 77 | 3F | ? | |
64 | 100 | 40 | @ | |
65 | 101 | 41 | A | |
66 | 102 | 42 | B | |
67 | 103 | 43 | C | |
68 | 104 | 44 | D | |
69 | 105 | 45 | E | |
70 | 106 | 46 | F | |
71 | 107 | 47 | G | |
72 | 110 | 48 | H | |
73 | 111 | 49 | I | |
74 | 112 | 4A | J | |
75 | 113 | 4B | K | |
76 | 114 | 4C | L | |
77 | 115 | 4D | M | |
78 | 116 | 4E | N | |
79 | 117 | 4F | O | |
80 | 120 | 50 | P | |
81 | 121 | 51 | Q | |
82 | 122 | 52 | R | |
83 | 123 | 53 | S | |
84 | 124 | 54 | T | |
85 | 125 | 55 | U | |
86 | 126 | 56 | V | |
87 | 127 | 57 | W | |
88 | 130 | 58 | X | |
89 | 131 | 59 | Y | |
90 | 132 | 5A | Z | |
91 | 133 | 5B | [ | |
92 | 134 | 5C | \ | |
93 | 135 | 5D | ] | |
94 | 136 | 5E | ^ | |
95 | 137 | 5F | _ | |
96 | 140 | 60 | ` | |
97 | 141 | 61 | a | |
98 | 142 | 62 | b | |
99 | 143 | 63 | c | |
100 | 144 | 64 | d | |
101 | 145 | 65 | e | |
102 | 146 | 66 | f | |
103 | 147 | 67 | g | |
104 | 150 | 68 | h | |
105 | 151 | 69 | i | |
106 | 152 | 6A | j | |
107 | 153 | 6B | k | |
108 | 154 | 6C | l | |
109 | 155 | 6D | m | |
110 | 156 | 6E | n | |
111 | 157 | 6F | o | |
112 | 160 | 70 | p | |
113 | 161 | 71 | q | |
114 | 162 | 72 | r | |
115 | 163 | 73 | s | |
116 | 164 | 74 | t | |
117 | 165 | 75 | u | |
118 | 166 | 76 | v | |
119 | 167 | 77 | w | |
120 | 170 | 78 | x | |
121 | 171 | 79 | y | |
122 | 172 | 7A | z | |
123 | 173 | 7B | { | |
124 | 174 | 7C | | | |
125 | 175 | 7D | } | |
126 | 176 | 7E | ~ | |
127 | 177 | 7F | DEL | delete |
五、变量与常量
什么是变量:在程序运行期间值可以发生变化的叫做变量,相当于存放数据的盒子
定义变量: 【类型名 变量名】;
int num;
1、变量的所占用的内存字节数、存储数据的范围、使用的规则都由变量的类型决定。
2、变量的定义就是操作系统把一个标识符与内存建议映射关系,操作系统不会对这个内存进行初始化, 所以变量的默认值是不确定的,我们要对一些特殊用途的变量进行初始化,比如:计数、求和、平均、 累加。
3、所定义的变量,出了它所在的大括号就不能再使用了。
取名规则:
1、由字母、数字、下划线组成
2、不能以数字开头
3、不能与C语言32个关键字重名
4、见名知意(功能、类型、范围...)
下列哪些是正确的变量名:
printf (o) scanf (o) bool (o) static (x) 1num (x) num%2 (x)
特殊的合法的变量名: bool,printf,true,flase
非法变量名举例: 1num、 *num、 auto
32关键字:
数据类型关键字(12个):
int, char, short, long, signed, unsigned, float, double, struct, union, enum, void
控制语句关键字(12个)
1、循环控制(5个):
for,do,while,break,continue
2、条件语句(3个):
if,else,goto
3、开关语句(3个):
switch,case,default
4、返回语句(1个):
return
存储类型关键字(5个):
auto,extern,register,static,typedef
其他关键字(3个):
const,sizeof,volatile
注意:变量的初始值默认是随机的,为了安全起见,一般会在定义时初始化为0
变量的使用:
赋值: 变量名 = val;
num = 0;// 被赋值、存储数据,相当是个容器
参与运算: 变量名*10;
num*3/4; // 参与运算,此时变量名就代表它存储的数据
变量的输出:
int printf(const char *format, ...);
功能:c标准库函数,把若干数据输出到终端
format: 提示信息+变量的类型信息+转义字符
...: 若干个变量名,变量名之间有逗号分隔
返回值: printf输出到终端上的字符个数
在C语言中使用占位符来表示变量的类型:
%hhd %hd %d %ld %lld 有符号整型的占位符
%hhu %hu %u %lu %llu 无符号整型的占位符
%f %lf %Lf 浮点型的占位符
%c 字符型的占位符
bool布尔类型是用来逻辑运算的,不参与输入、输出,如果要强行输出,把它当整型即可。
输入:
int scanf(const char *format, ...);
功能:从终端读取数据赋值给变量
format:占位符,提示信息和转义字符不要使用
...:若干个变量的地址,&变量名可以计算出变量的地址
返回值:成功读取数据的变量个数
错误的使用案例: scanf("%d\n",&num);
改正(输入不需要“\n”): scanf("%d",&num);
六、常量
常量就是程序运行过程中不能改变的量,C语言的常量有:字面值常量、宏常量、枚举常量。
10 默认int
10l long
10ll long long
10u unsigned int
10lu unsigned long
10llu unsigned long long
3.14 默认double
3.14f float
3.14l long double
‘A' char
案例:定义一个表示100年总共有几秒的宏常量(忽略闰平年)
#defind SEC 3600*24*365*100u
数据的格式化输出
%nd 显示n个字符宽度,不足时补空格,右对齐
%-nd 显示n个字符宽度,不足时补空格,左对齐
%0nd 显示n个字符宽度,不足时补0,右对齐
%n.mf 显示n个字符宽度(小数点也算一位),不足时补空格,右对齐,m表示小数点后显示的位数(四舍五入)
%g 不显示小数点后多余的0
七、运算符
优先级 | 操作符 | |||
---|---|---|---|---|
1 | () | [] | -> | . |
:: | ||||
! | ~ | ++ | -- | |
2 | - (unary) | * (dereference) | ||
& (address of) | sizeof | |||
3 | ->* | .* | ||
4 | * (multiply) | / | % | |
5 | + | - | ||
6 | << | >> | ||
7 | < | <= | > | >= |
8 | == | != | ||
9 | & (bitwise AND) | |||
10 | ^ | |||
11 | | | |||
12 | && | |||
13 | || | |||
14 | ? : | |||
15 | = | += | -= | etc. |
16 | , |
自变运算符:
前自变: ++/--i 变量的值立即加1或减1。
后自变: i++/-- 变量的值加1或减1,下一行代码有效。
注意:不要在复杂表达式中过度使用自变运算符,不同的编译器对它的运算规则不同,编译器会把合适的后自变优化成前自变。
算术运算符:
+ 加
- 减
* 乘
/ 除 进行除法运算得到的结果是商
% 求余 进行除法运算得到的结果是余数
注意1: 整数/整数 计算的结果没有小数点,例如:3/5 的结果是1。
注意2: /和% 都是进行除法运算,/结果是商,%结果是余数,它们除数不可以为零,会出现浮点数例外的错误(核心已转储),该错误会让程序立即结束。
关系运算符:
> 大于
>= 大于等于
< 小于
<= 小于等于
== 相等
!= 不相等
注意1: 它们运算结果是逻辑值,C语言中的逻辑值是用0(假)和1(真)模拟的,计算出的结果还能进行数学运 算。
注意2: 与数学中的用法不同,10 < x < 100,在数学中表示是x的取值范围,但在C语言中它就是需要运算的 表达式,会先计算 10 < x 得到0|1,然后再把这个结果与100比较,所以该表达的结果永远是真。
注意3: 使用==运算符时,非常容易出错误,容易漏写一个=,该错误编译器检查不出,阅读代码也很难查出 来,所以在使用==时,把常量放在左边,变量写右边,这样当漏写=时,编译器会报错误。
逻辑运算符:
&& 与 双目运算符,左右两边都是真,结果才是真
|| 或 双目运算符,左右两边只要有一个是真,结果就是真
! 非 单运算符,只针对右边的运算对象,求反,如果运算对象是真,结果就是假,如果运算对象是假, 结果就是真
注意: ! 比 &&、|| 的运算优先级高
&& 和 || 的短路特性:
当左边的值已经可以确定运算结果时,右边的不再计算,适当的使用可以实现精简的if结构,也可以借助短路特性,实现简单的单分支效果 ,能看懂即可,建议不要这样写,因为它会影响代码的可读性。
三目运算符:
[A] ? [B] : [C];
它的运算对象有三个,所以叫三目运算符,先把[A]转换成逻辑值,为真执行[B],为假执行[C],相当 于精简的 if else 结构。
注意: 与 if else 不同的是三目运算符必须有运算结果,所以它里面不能使用流程控制语句,比如:return、 break、continue。
赋值运算符:
= += *= /= %=... 是变量先运行后赋值的一种精简写法
a += b; 等价 a = a + b;
a *= b; 等价 a = a * b;
注意:赋值运算符的运算结果是右边赋的数据
位运算符:
& | ~ ^ << >>
字节数运算符:
sizeof它不是函数,是C语言的32个关键字之一,它能计算出数据在内存需要占用的内存字节数,如果运算对象不是表达式,它可以不使用小括号。 注意: sizeof(表达式)不会执行小括号里表达式,它只是推算表达式的执行结果是什么类型,然后计算出该类 型在内存中占用多少个字节。
八、类型转换
前提:只有相同类型的数据才能进行运算,如果类型不相同的数据需要先转换成相同的类型才能运算
自动类型转换(隐式类型转换):
转换规则:以不丢失数据为基础,可以适当地牺牲一些空间
1、字节少的向字节多的转
2、有符号的向无符号的转
3、整型向浮点型转(int -> float)
特殊特例:当运算对象类型只是 char或者short,且类型不同,编译器会做类型提升,提升为int再运算
强制类型转换:
(新类型名)数据;
有丢失数据的风险,但是需要使用时还是得用
九、if语句
代码的默认执行流程是从上到下,逐步、逐条执行的,if语句可以根据判断条件选择让代码是否执行,改变了代码 的默认执行流程,所以这种语句也叫流程控制语句。
if分支语句
if(表达式) // 单分支语句
{
// 当条件为真时,执行此处代码,如果此处代码只有一行,大括号可以省略,但在商业项目中建 议不要省略,因为这样会影响代码的可扩展性
}
if(表达式) // 双分支语句
{
// 表达式的值为真(非0),则执行此处代码
}
else
{
// 表达式的值为假(0),则执行此处代码
}
if(表达式1) // 多分支语句
{
// 表达式1的值为真(非0),则执行此处代码
}
else if(表达式2)
{
// 表达式2的值为真,则执行此处代码
}
...
else
{
// 所有表达式都为假,最后执行此处代码
}
十、switch开关语句
1、switch小括号中的数据可以是变量或表达式,但结果必须是整数型,case后面的数据必须是整数型常量。
2、把switch小括号中的数据与case后的数据进行对比,如果相同则打开执行开关。
3、当执行开关处于打开状态时,case后面的语句就会被执行,如果执行开关不关闭,之后case、 default后面的语句也会被执行。
4、break语句可以关闭执行开关,如果每个case后都break语句,就形成了多分支结构。
5、default无论写在任何位置,都是等所有的case没有匹配成功后,才打开执行开开关,就相当于多 分支if语句最后的else,可以有也可以没有。
switch (数据)
{ case v1: 语句1; break;
case v2: 语句2; break;
case v3: 语句3; break;
...
default: 语句; break;
}
注意: case n1 ... n3:
表示在范围[n1,n3]内,会打开case开关,但是这属于GNU编译器的特有语法,不建议使用