C语言总结
C语言的概述(自己总结)
起源,发展,现状,特点,优缺点
数据类型
内建:C语言自带的数据类型
整型
stdint.h 对数据类型进行了封装
limits.h 各种数据类型的最大值是小值
unsigned
uint8_t,uint16_t,uint32_t,uint64_t
char,short,int,long,long long
char 0~255 256个值
short 0~65535 65536个值
signed
int8_t,int16_t,int32_t,int64_t
char,short,int,long,long long
char -128~127 256个值
short -32768~32767 65536个值
浮点型(小数点浮动)
数据格式:符号位 指数位 尾数位
默认小数点后六位有效,精度有限,运算效率低
有专门针对浮点运算的CPU
float 4
double 8
如何判断float类型的数据是“零值”?
if(val<0.000001 && val>-0.000001)
{
//不等于0,但无效,相当于零值
}
如何提高浮点类型数据的准确率?
先放大,在还原。
布尔型
由于C语言出现于1970年左右,布尔类型流行于1980以后,所以c语言中是没有真正的布尔类型。
bool stdbool.h
true 非零值 NULL 绝大多情况小为假 ' '
false 零值
NULL == ((void*)0)||((void*)1);
_bool C99标准中补充的
如何判断int,bool,int*类型的值是否为假?
int num;
if(0 == num)
if(flag)
if(NULL==p)
字符型
底层采用整数存储的,整数与ASCII表的字符对应
' ' 0
'0' 48
'A' 65
'a' 97
转义字符
a
v
f
%%
"
\
枚举
功能受限制的int类型,防君子不防小人
使用枚举是一直锦上添花,使用枚举可以提高程序的安全性和可读性
有名枚举
typedef enum CMD {ADD,DEL,FIND};
可以定义枚举变量
匿名枚举
ENUM {UP,DOWN,RIGHT,LEDT}
使用枚举值,或者与switch配合使用
指针:整型(整数),表示的是内存的编号
自建:C程序自己设计的数据类型
结构
联合
数组
变量(容器)与常量
常量:字面值,宏常量,枚举值,初始化过并且被const修饰的全局变量
变量:
全局变量:普通全局变量,静态全局变量
作用域不同
局部变量:普通全局变量,静态全局变量
存储位置不同:普通在栈,静态在bss
块变量:普通块变量,静态块变量
变量名:
1、由数字、字母、下划线组成
2、不能以数字开头
3、不能与关键字重名
4、见名知义
功能、范围、类型
输入数据
scanf 占位符 变量地址
scanf返回值,成功接取数据的个数
如何不理输入缓冲区:
getchar()
scanf("%*[^
]"),scanf("%*c");
stdin->_IO_read_ptr = stdin->_IO_read_end;
输出数据
printf 占位符 变量名
数据遇到什么条件才会从缓存区到达屏幕:
1、满4k(4096byte)
2、遇到
3、遇到输入类型的函数
4、手动刷新 fflush(stdin);
5、程序结束
数组
数组的定义:类型 数组名[数量];
在内存的分布上优先定义数组,再定义变量
数组的初始化:类型 数组名[数量] = {val1,val2...};
1、初始化值过多会有警告
2、初始化值不够补0
3、类型 数组名[数量] ={};
4、类型 数组名[] = {val1,val2...};
数组的越界:
1、脏数据
2、段错误
3、一切正常
变长数组:数组的长度在编译时无法确定,在运行时可以发生变化,当执行到定义数组语句时,数组的长度就确定下来了,不可再变
优点:根据需求在确定数据的长度,节约内存。
缺点:不能初始化(数组的初始化是由编译器帮助完成)
多维数组:
一维数组:变量组成的队列
二维数组:变量组成的方阵
三维数组:变量组成的立方体
流程控制语句:
分支
if else if else
switch
小括号中只能是整型数据
case后必须是常量
default 最后执行
break 关闭执行开关
循环
for
for([1];[2];[3])
{
[4]
}
for 语法灵活,1,2,3,4都可以缺少
用循环变量i(index)引导for的运行,负责明确知道循环次数的情况
while
是for的一种精简,负责只知道循环条件
do while
先执行循环体,在判断循环条件,直到循环
if、for、while 如果没有大括号,默认下一行代码是它的语句体,但不建议这样使用,大括号不要省略(安全性、可扩展性),如果执行异常,记得检查小括号后面是否有多余的分号
跳转
goto
goto 标签;功能是在函数内可以任意跳转,这种跳转会破坏原先的分支,循环结构
大多数公司会禁止使用goto,禁止的方式就是把goto定义为病毒(应用层程序)
但在驱动编程时goto非常适合用来处理异常
break
1、switch中关闭开关
2、在循环中使用可以跳转一层循环
continue
只能在循环中使用,功能是结束本次循环进入下次循环
return
1、返回函数的执行结果
2、提前结束函数的执行
函数
函数的声明:
extern 返回值类型 函数名(参数类型,参数名,..);
返回值类型 函数名(参数类型,参数名,...);
返回值类型 函数名(参数类型1,参数类型2,...);
函数的定义:
返回值类型 函数名(参数类型,参数名,...);
{
函数体
}
使用函数要注意的问题:
1、函数传参全部都是值传递(内存拷贝)
由于数据名就是指针,以数组当函数参数时也是值传递,而且数组的长度也会丢失,
2、函数的隐式声明
当调用函数,如果没有提前声明,编译器会猜测函数的格式,如果猜测正确函数则正常执行如果猜错,则会执行异常。
3、如果函数不需要参数要写void
定义函数时如果小括号中是空的,则意味着任意类型,任意个参数都可以调用函数
4、不写return不表示没有返回值
递归:
函数自己调用自己的行为叫递归,这种解决问题的思想:分支
这种调用极易引发死循环,递归编写的格式:
1、出口
2、解决一部分问题
3、再次调用自己
递归是一种解决复杂问题的常见手段,也比较适合解决树型结构问题,容易理解但不能模拟它的执行过程
指针:
什么是指针:一种数据类型,代表内存编号的整数
使用它可以定义指针变量
什么情况下使用指针:
1、函数之间共享变量
2、优化函数传参
3、使用堆内存必须配合指针
指针的使用方法:
定义:类型* 变量名_p;
1、指针的类型决定了,通过指针访问内存时的字节数
2、指针不能连续定义
int* p1,p2,p3;//p1是指针,p2是int变量
int *p1,*p2,*p3;
3、指针的默认是随机的(野指针),如果没有确定的值可以初始化为NULL(空指针)
4、指针变量与普通变量的使用方法有很大区别,因此最好从名字上就能区分开,所以以p结尾
arr,str
赋值:指针变量名 = 内存编号
1、类型要匹配,否则会有警告
2、类型指针<=>void*
3、void*不能直接使用,要转换成有类型的指针
int* p = malloc(4);
访问内存:*指针变量
int *p = #
*p <=>num;
这个过程是有可能产生段错误的
访问内存的字节数由指针的类型决定的
使用指针要注意的问题
1、空指针
指针变量的值为NULL,NULL不一定是0
使用空指针的后果就是一定产生段错误
0地址是操作系统的复位地址,存储了重启时要使用的数据,因此不能让应用程序访问,NULL也是应用程序的错误标志,如果函数的返回值是NULL说明函数执行错误
使用来历不明的指针钱要先检查NULL == p
strlen strcat strcpy strcmp
2、野指针
指针变量的值不确定
使用野指针不一定出错,肯呢过产生的后果:
1、脏数据
2、段错误
3、一切正常
出现野指针比出现空指针的后果更严重,因此野指针无法判断
防止野指针出错是只能从根源着手,不制造野指针:
1、定义指针变量时一定要初始化
2、不持有局部变量的地址
3、堆内存一旦初始释放,指针要及时设置为空
3、指针的运算
因此指针变量的值就是整数,所以整数数据可以使用的运算符它基本都可以使用,但是要以实际意义为准则
4、指针与数组名
数组名:是一种常指针,他与数组的首地址是对应关系
指针和数组名的用法是可以互换的:
p[i] = *(p+i);
*(arr+i) = arr[i];
修饰变量的关键字
auto 不能与static 一起用,全局不变量也不能使用
定义自动分配,释放的变量,不加就代表加
unsigned:定义无符号整型变量
signed:定义有符号整型变量,不加就代表加
const:为数据提供一种保护机制,被它修饰过的变量不能显示修改
变量、函数参数、返回值、指针
const 全局变量 初始化 = 常量
const int *p; 不能通过 *p修改内存的值
inr const *p;同上
int* const p; p的值不能修改
const int* const p; 不能通过*p修改内存的值,p的值也不能修改
static
限制作用域:全局变量、函数
改变存储位置:局部、块变量
延长生命周期:局部、块变量
volatile
防止编译器优化变量的取值过程
register
申请把变量存储到寄存器
extern
声明变量、函数
程序在内存中的分布
代码:可以执行文件会被全部加载到此段
只读:字面值、常量
全局:被初始化过的全局变量
bss:静态变量、未初始化的全局变量
程序执行前会把这段内存清理为0
栈:局部变量、块变量
堆:程序员自己变量、无法取名、足够大
字符串
串:是一种数据结构,由若干个相同类型的数据组成,有一个明确的结束标志' '。
字符串字面值:
以地址形式存储在只读内存中。
如果在程序中已经存在一份字符串字面值,当再使用它时不会在内存中再定义一份。
字符串可以换行写,编译器会自动把它们连接。
定义:
const char* str = "hehe";
char str[] = "hehe";
处理字符串的函数:
输入:
scanf
gets
fgets
getstr
fscanf
fread
输出:
printf
puts
fpritnf
fwirte
string.h
strlen
strcat/strncat
strcpy/strncpy
strcmp/strncmp
strchr
strstr
atoi
memset
memcpy
sscanf/sprintf
字符串-》数值 数值-》字符串
堆内存使用
C语言中是没有提供内存管理的语句,只能靠标准库提供的库函数(stdlib.h)。
内存芯片-》操作系统-》malloc-》a.out
malloc 申请内存
calloc 按块申请内存,并把申请的内存初始化
realloc 调整已有内存的大小,把内存调大就相当于申请内存,把内存调小就相当于在释放内存。
free 内存的释放。
使用堆内存要注意的问题:
1、重复释放
2、越界:
前续错误
后续错误
段错误
脏数据
一切正常
3、泄漏
谁申请,谁释放
谁知道该释放,谁释放
结构、联合
typedef struct 结构名
{
成员类型 成员名=-1;
void func(void); // eroor
void (*funcp)(void); // 允许
char sex:1;
}结构类型;
1、在定义结构变量时struct不能省略。
2、结构的成员不能初始值。
3、结构的成员不能是函数,但可以是函数指针。
4、结构变量可对结构变量直接赋值
Student stu1 = {};
Student stu2 = stu1;
stu2 = stu1;
结构的大小:
补齐:结构的总字节数要是它最大成员的整数倍,如果不是则添加空白字节补齐。
对齐:每个成员在结构中的内存起始编号,要是它自身大小的整数倍,如果不是则添加空白字节保持对齐。
Linux和Windows系统对待补齐和对齐的区别?
Window会严格按照补齐和对齐的规定严格执行
Linux对待补齐和对齐时,超过4字节按4字节计算。
补齐和对齐的字节数是可以设置的。
联合:节约内存的一种手段。
联合常考的问题:
1、联合的字节数。
union data
{
char arr[5];
int num;
};
2、使用联合判断大、小端系统。
0x010203 高位数据1 低位数据3
0x18[][][][] 高位地址0x1B 低位地址0x18
大端 0x01020304
小端 0x04030201
文件操作:
文件的分类:
二进制文件:数据的补码。
文本文件:存储的是ASCII的二进制。
windows系统对待换行与Linux系统不同。
文件的打开方式:
r
w
a
r+
w+
a+
wb
->
w
->
文件操作函数:
fopen
fscanf/fprintf
fgets/fputs
fread/fwrite
fclose
fseek 调整文件位置指针
ftell 返回文件位置指针的位置
rewind 调整文件指针到文件的开头。