数据类型
char | 1个字节 |
short | 2个字节 |
int | 4个字节 |
long | 4个字节 |
long long | 8个字节 |
float | 4个字节 |
double | 8个字节 |
字符串
char 字符类型,一对单引号引起来是一个字符;一对双引号引起来的是字符串。
字符串的结束标志是一个\0的转义字符。
strlen();//用于求字符串长度的函数 需添加头文件 string.h。
变量和常量
变量分为:
局部变量-在{}内部定义的变量 注:当全局变量和局部变量名称相同时,局部优先。
全局变量-在{}外部定义的变量。
变量的作用域:
局部变量:局部变量的作用域是变量所在的局部范围。
全局变量:全局变量的作用域是整个工程。
变量的生命周期:
局部变量:进入作用域生命周期开始,离开作用域生命周期结束。
全局变量:整个程序的生命周期。
变量的命名:
1、有意义
2、名字必须由字母、数字、下划线组成,不能有特殊字符,同时不能以数字开头。
3、变量名不能是关键字。
常量:
1、字面常量
2、const修饰的常变量 注:const修饰的常变量,在C语言中const修饰的a本身是变量,但是不能直接修改,有常量的属性。
3、#define 定义的标识符常量
4、枚举常量 enum为关键字
转义字符
\n | 用于换行 |
\0 | 字串结束标志 |
\? | 在书写连续多个问号时使用,防止他们被解析成三字母词 |
\' | 用于表示字符常量' |
\" | 用于表示一个字符串内部的双引号 |
\\ | 用于表示一个反斜杠,防止它被解释为转义徐列符 |
\a | 警告字符,蜂鸣 |
\b | 退格符 |
\f | 进纸符 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ddd | ddd表示1-3个八进制的数字 如:\130 |
\xdd | dd表示2个十六进制数字,如: \X30 |
输出类型
%d | 打印整形 |
%c | 打印字符 |
%s | 打印字符串 |
%f | 打印float类型的数值 |
%lf | 打印double类型的数值 |
%zu | 打印sizeof的返回值 |
循环语句
1、while循环
while循环中,break是用于永久的终止循环
while循环中,continue 跳过本次循环后面的代码,直接去判断部分,进行下一次循环的判断。
2、for循环
for(表达式1;表达式2;表达式3) 表达式1:为初始化部分,用于初始化循环变量;表达式2:条件判断部分,用于判断循环时候结束;表达式3:调整部分,用于循环调整。
for循环的判断部分省略意味着判断会恒成立。
3、do..while..循环
特点:循环至少执行一次,使用的场景有限,所以不经常使用。
分支语句
1、if语句(包含if;if..else..;if...else if....else)
2、switch语句
函数
1、函数的嵌套调用
函数可以嵌套调用,但是不能嵌套定义
2、链式访问
示例:
printf("%d\n", strlen("abcdef"));//链式访问
3、函数的声明
1、告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。
2、函数的声明一般出现在函数的使用之前。要满足先声明后使用。
3、函数的声明一般要放在头文件中的。如果需要引用则需要 #include "头文件.h"
4、函数的定义:
函数的定义是指函数的具体实现,交代函数的功能实现。
5、当实参传递给形参的时候,形参是实参的一份临时拷贝
6、对形参的修改不会改变实参
7、什么时候传地址?
如果函数内部想改变变量,需要传外部变量的地址。
8、什么时候不需要传地址?
如果函数只是用变量,则不需要传递变量地址。
9、数组传参数传的是什么?
数组传参实际上传递的是数组首元素的地址,而不是整个数组,所以在函数内部计算一个函数参数部分的数组的元素个数是不靠谱的。
10、什么是递归?
程序调用自生的编程技巧称为递归。
12、递归的两个必要条件
1、存在限制条件,当满足这个限制条件的时候,递归便不再继续。
2、每次递归调用之后越来越接近这个限制条件。
13、迭代
数组
1、一维数组:数组是一组相同类型元素的集合
int arr[X] int-数组类型 arr-数组名称 [X]-数组长度 注:数组的大小可以是变量,为了支持变长数组(只能在支持C99标准的编译器上进行)
2、数组的初始化:数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。不完全初始化,剩余的元素默认初始化为0.
3、数组在内存中是连续存放的
4、二维数组:可以理解为一维数组的数组
int arr[X][Y] int-数组类型 arr-数组名称 [X][Y]-X行Y列的数组 注:依次往后放
二位数组可以省略X但不可省略Y的值
5、数组的越界:数组的下标是有范围限制的,数组下规定的是从0开始,如果数组有n个元素,最后一个元素的下标就是n-1;所以如果数组的小标小于0或者大于n-1,就是数组越界访问
6、数组传参只需要写数组名
7、数组名:确实能够表示首元素的地址,但是有2个例外
1、sizeof(数组名):这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
2、&数组名(取地址数组名),这里的数组名表示整个数组,取出的是整个数组的地址。
8、二维数组的数组名的理解:二维数组的数组名也表示数组首元素的地址,即第一个一维数组的地址。
9、数组传参时,形参有两种写法:
1、数组
2、指针
操作符
1、算术操作符:+ - * /
注:
1、整数除法 1/2-->0 浮点型 1.0/2-->0.5(浮点型必须有一位是浮点)
2、% 取模操作符两端必须是整数 计算的是整除后的余数
2、移位操作符(只针对整数,且移动位数不能是负数/小数):
注:位移操作符移动的是二进制
<<(左移位):左边丢弃,右边补零 注:有乘2的效果
>>(右移位):算术移位:右边的丢弃,左边补原符号位
逻辑移位:右边丢弃,左边补0
3、位操作符(只适用于整形):
&-按(2进制)位与 注:有0则为0;全1则为1
|-按(2进制)位或 注:有1则为1;全0则为0
^-按(2进制)位异或 异或支持交换律 注:相同为0;相异为1
4、赋值操作符:= += -= *= /= %= >>= <<= &= |= ^=
5、单目操作符:! (逻辑反) -(负数) +(正数) &(取地址) sizeof(计算变量所占内存空间的大小,单位是字节) ~(对一个数的二进制取反) -- ++ *(间接访问操作符) (类型)(强制类型转换)
6、关系操作符号:> >= < <= != ==
7、逻辑操作符:&&(与:左右两边只要有一个是假即为假) ||(或:左右两边只要有一个是真即为真)
8、条件操作符:exp1 ? exp2:exp3
9、逗号表达式:
10、下标引用、函数调用和结构成员
整型提升
1、整数的二进制表示有三种
1、原码:正的整数的原码、反码、补码相同;如果是负的整数,原码可以直接计算.
2、反码:正的整数的原码、反码、补码相同;如果是负的整数,原码的符号位不变,其他位置 取反。
3、补码:正的整数的原码、反码、补码相同;如果是负的整数,反码最后一位+1就是补码。
2、整数在内存中存储的是补码。
3、隐式类型转换
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换成为整型提升。
关键字
for;while;do while;break;continue;if else;switch;case;default;goto
char;short;int;long;float;double;signed(有符号的);unsigned(无符号的);
enum(枚举);struct(结构体);union(联合体);void(无);sizeof(计算大小);typedef(类型重命名)
const(常属性);extern(声明外部符号);register(寄存器);static(静态的);return(函数返回值)
typedef:类型定义,类型重命名
示例:typedef unsigned int uint;//类型重命名
static:静态
1、用于修饰变量和函数
2、修饰局部变量-称为静态局部变量 注:局部变量除了作用域,不销毁;本质上static修饰局部变量的时候,改变了变量的存储位置。影响了变量的生命周期,生命周期变长,和程序的生命周期一样。
3、修饰全局变量-称为静态全局变量 注:全局变量本身具有外部连接属性;当static修饰全局变量的时候,这个全局变量的外部链接属性就变成了内部链接属性,其他的源文件就不能再使用到这个全局变量了。
4、修饰函数-称为静态函数 注:一个函数本身具有外部链接属性,但是再被static修饰后外部链接属性变为了内部链接属性,其他源文件无法使用。
指针
一、指针是什么?
1、指针是内存中一个最小单元的编号,也就是地址
2、平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
3、指针变量中存放的是地址,通过这个地址可以找到一个内存单元。
4、指针的大小,在32位的平台上大小是4个字节;在64位平台上是8个字节。
二、指针和指针类型
1、指针类型的意义:指针类型决定了指针在被解引用的时候访问几个字节。
2、指针的类型决定了+—操作的时候,跳过几个字节。
三、野指针:野指针就是指针指向的位置不可知的。
四、指针运算
1、指针+-整数
2、指针-指针
1、指针减去指针的绝对值得到的是指针和指针之间的元素个数。
2、指向同一个空间的指针才可以相减。
3、指针关系运算
1、指针只可以向后比较,不能向前比。
五、指针和数组
1、数组名表示的是数组首元素地址。
六、二级指针
1、二级指针变量用来存放一级指针变量的地址。
七、指针数组
1、存放指针的数组就是指针数组。
指针的进阶
1、字符指针
2、指针数组-是数组,用来存放指针的数组
举例:int* arr[6] 存放整型指针的数组
3、数组指针-指向数组的指针
数组指针使用来存放数组的地址
4、&数组名VS数组名
数组名通常表示的是首元素地址,但是有两个例外:
1、sizeof(数组名),这里的数组名表示整个数组,计算的整个数组的大小,单位是字节。
2、&数组名,这里的数组名表示的依然是整个数组,所以&数组名取出的是整个数组的地址。
5、int arr[5] arr是一个整型数组。
int *parr1[10] parr1整型指针数组。
int(*parr2)[10] parr2是数组指针。
int(*parr3[10])[5] parr3应该是存放数组指针的数组。
6、函数指针:指向函数的指针就是函数指针。
7、函数指针数组:把函数指针放在数组中,其实就是函数指针数组。
8、回调函数:回调函数就是一个通过函数指针调用的函数。
结构体
1、结构体类型的声明
结构是一些值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。
结构的成员可是是标量、数组、指针、甚至是其他结构体。
2、结构体的初始化
3、结构体成员访问 第一种方法:结构体变量.成员变量 第二种方法:结构体地址->成员变量
4、结构体传参:结构体传参时,最好传地址。、
数据存储
1、类型的意义:
1、使用这个类型开辟内存空间的大小(大小决定了使用范围)。
2、如何看待内存空间的视角。
2、整型在内存中的存储
1、整数在内存中存放的是补码的二进制序列。
3、大小端介绍
大端【字节序】存储:
把一个数据的高位字节序内容存放在低地址处,把低位字节序内容放在高地址处,就是大端字节序存储。
小端【字节序】存储
把一个数据的高位字节序的内容存放在高地址处,把低位字节序内容放在低地址处,就是小端字节序存储。
4、浮点型在内存中的存储
如果是8位E中间数为127;如果是11位E中间数为1023。
字符函数和字符串函数
1、strlen 字符串已'\0'作为结束标志,strlen函数返回的是在字符串中’\0'前面出现的字符个数(不包括’\0')。注明:返回的无符号整型。
2、长度不受限制的字符串函数——strcpy 字符串拷贝,被拷贝的数据必须有\0,且拷贝\0之前的内容(包含\0)。
3、长度不受限制的字符串函数——strcat 字符串追加,注:不能自己给自己追加
4、长度不受限制的字符串函数——strcmp 字符串比较(比较的是ASII码),注:比较的是内容不是长度
5、长度受限制的字符串函数——strncpy
6、长度受限制的字符串函数——strncat
7、长度受限制的字符串函数——strncmp
8、strstr 查找子串的函数
9、strtok 切割字符串
10、strerror 错误报告函数,C语言的库函数,在执行失败的时候,都会设置错误码。(errno-C语言设置的一个全局的错误码存放的变量)
11、字符分类函数——iscntrl(任何控制字符)
12、字符分类函数——isspace(空白字符)
13、字符分类函数——isdigit(十进制数字0~9)
14、字符分类函数——isxdigit(十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F)
15、字符分类函数——islower(小写字母a~z)
16、字符分类函数——isupper(大写字母A~Z)
17、字符分类函数——isalpha(字母a~z或者A~Z)
18、字符分类函数——isalnum(字母或者数字,a~z,A~Z,0~9)
19、字符分类函数——ispunct(标点符号,任何不属于数字或者字母的图形字符(可打印))
20、字符分类函数——isgraph(任何图形字符)
21、字符分类函数——isprint(任何可打印字符,包括图形字符和空白字符)
22、字符转换函数——tolower(转换为小写)
23、字符转换函数——toupper(转换为大写)
24、memcpy 内存拷贝 负责拷两块独立空间中的数据,不用来处理重叠的内存之间的数据拷贝的
25、memmove 重叠内存的拷贝 重叠内容之间的拷贝
26、memcmp 一对字节,一对字节的比较,小于返回小于0的数字;反之亦反
27、memset 内存设置 以字节为单位。
自定义类型
1、结构体
结构是一些值的结合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
2、结构体的自引用
3、结构体变量的定义和初始化
4、结构体内存对齐(重点)
结构体对齐规则:
1、第一个成员在与结构体变量偏移量为0的地址处。
2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数=编译器默认的一个对齐数与该成员大小的较小值。VS中默认的值为8
3、结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4、如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
5、注:在创建结构体的时候尽量将所占空间小的集中在一起。
6、修改默认对齐数
7、结构体传参:优先选择传址的形式
位段
位段:单位是比特,位段可以节省空间的
1、位段的声明和结构体类似
1、位段的成员必须是 int unsigned int 或者signed int。
2、位段的成员名后边有一个冒号和数字。注:冒号后的数字不能超过前面的类型
2、位段的内存分配
1、位段的成员可以是int unsigned int signed int 或者char(属于整形家族)类型
2、位段的空间是按照需要4个字节(int)或者1个字节(char)的方式来开辟的。
3、位段涉及很多不确定因素,位段是不能跨平台的,注重可移植的程序应该避免使用位段。
3、位段的跨平台问题
1、int 位段被当成有符号数还是无符号数不确定
2、位段中最大位的数目不能确定
3、位段中的承载内存中从左向右分配,还是从右向左分配尚未定义
4、当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,时舍弃剩余的位还是利用,这是不确定的。
枚举
1、枚举就是一一列举
2、枚举类型的定义
3、枚举的优点
1、增加代码的可读性和维护性
2、和#define定义的标识符比较枚举有类型检查,更加严谨。
3、防止了命名污染(封装)
4、便于调试
5、使用方便,一次可以定义多个变量
4、枚举的使用
联合(共用体)
联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征时这些成员共用同一空间。
联合的特点
联合的成员是共用同一块内存空间的,这阿姨那个一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)
联合大小的计算
1、联合的大小至少是最大成员的大小
2、当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
动态内存管理
1、为什么存在动态内存分配
1、空间开辟的大小是固定的
2、数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配
1、malloc void* malloc(size_t size);如果不想要初始化使用这个
在堆区中申请开辟内存块,内存开辟失败返回空指针
通常和free连用
2、calloc void* calloc(size_t num,size_t size);如果想要初始化使用这个
开辟空间后,会将空间内容初始化为0
3、realloc void* realloc(void* ptr ,size_t size)
可以做到对动态开辟内存大小的调整.
原理:如果后旧地址后的空间不够扩容的,会找一块新的足够大的空间将旧地址的内容复制后,返回新的地址,旧地址会被释放;如果后面的地址足够,就回返回旧的地址。
4、free
用来释放内存空间
5、常见的动态内存错误
1、对空指针的解引用操作
2、对动态开辟空间的越界访问
3、对非动态开辟空间进行free释放
4、使用free释放一块动态开辟内存的一部分
5、对同一块空间多次释放
6、动态开辟内存忘记释放(内存泄露)
柔性数组
柔性数组(只能存在一个) C99中结构体中的最后一个元素允许是未知大小的数组,这就叫【柔性数组】成员。
1、结构中的柔性数组成员前面必须至少一个其他成员。
2、sizeof返回的这种结构大小不包括柔性数组的内存。
3、包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构古的大小,以适应柔性数组的预期大小。
文件
文件
每个被使用的文件都在内存中开辟了一个相应的文件信息区
文件类型指针 FILE* pf
文件的打开(fopen函数用于打开文件)
在编写程序时,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件
文件的关闭(fclose函数用来关闭文件)
文件的读写
文本和二进制:只针对文件
流:
FILE*可以被称为文件流
任何一个C程序,只要运行起来就会默认打开3个流:
stdin——标准输入流(键盘)
stdout——标准输入流(屏幕)
stderr——标准输入流(屏幕)
scanf是针对标准输入的格式化输入语句
printf是针对标准输出的格式化输出语句
fscanf是针对所有输入流的格式化输入语句
fprintf是针对所有输入流的格式化输出语句
sscanf是从一个字符串中转化为一个格式化的数据
sprintf是把一个格式化的数据转换为字符串。
文件随机读写
fseek:根据文件指针的位置和偏移量来定位文件指针。
ftell:返回文件指针相对于起始位置的偏移量。
rewind:让文件指针的位置回到文件的起始位置
文本文件和二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到,就是二进制文件
如果要求以ASCII的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
文件读取结束的判定:feof
不能用feof判断文件是否结束,而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件末尾结束。
按照字符读,读到EOF才会到文件末尾
按照一行一行读,读到空指针才会到文件末尾