注意:
1.定义的局部变量都在栈区
一、指针
2.二级指针:
定义方式: 使用两个 ** 进行定义
概念:指针变量的地址-->指针的指针
保存一级指针的地址的指针 被叫做二级指针
常用二级指针类型的函数形参,接收一级指针地址形式的实参,以修改调用者指针的目标,或为其分配资源
注意:对一维数组的数组名取地址,得到的不是二级指针而是数组指针
int* pn =&p -->不是二级指针
(2) 常用二级指针类型的函数形参,接收一级指针地址形式的实参,以修改调用者指针的目标,或为其分配资源
调换两个字符串
1.指针基本概念
(1)C语言将地址形象地称为指针。地址(指针)指向变量单元
在编译时,系统会给变量分配内存,在执行语句时,会通过变量名找到对应地地址。
内存以字节为单位,不同类型数据的字节数不同,每个字节编号-->地址,每个字节都有唯一的地址
注意:::多字节数据,将其首字节地址作为该数据的地址
根据地址可以找到相应的数据,地址又称指针 将指针存在一个变量中,这个变量成为指针变量
(变量中存储一个地址-->指针变量)
2.取地址运算符:&
根据变量 得到变量的内存首地址
3.解引用运算(*)
根据变量内存首地址取得变量值
*(&a) 通过a的首地址 获取变量值
注意: 前面有&说明是地址
4.指针变量特点:
1.存储地址 ---> 4(32位)/8(64位)字节 跟数据类型无关 X000000...1
2.变量 --- 存储其他地址 自己也有地址 int *p = &a *p--->解引用 a的值 p--->指针的值 &p -->指针变量的地址
5.野指针与空指针:
(1) 野指针:指向不可用内存区域的指针
产生野指针原因:
1. 指针变量没有初始化
2.指针变量所指向的内存已经被释放---malloc
(2)空指针:值为0的指针,可以用宏NULL表示
1.任何情况下,操作空指针的结果都是确定的---》崩溃
2.空指针比野指针更适合作为指针有效性的判断依据
6. 指针计算
(1) 指针支持加减整数、关系比较和相减运算
指针计算的结果由指针的类型决定
指针变量 + n = 指针变量+n * sizeof(数据类型)
二、函数
三、数组
注意:数组名存储的是第一个元素的首地址 但是不能像指针一样 指向新的地方 相当于const修饰
不能进行指针运算 arr++ ; -->error
数组与指针具有通用性 注意 :不能一个元素都不进行赋值
1.基本概念:
有序的数据集合
每个元素都是同一个数据类型
下标从0开始
C语言不允许对数组的大小进行动态定义
2.一维数组的初始化
(1) 在定义时,对全部数组元素初始化
(2) 在定义时,对部分元素进行初始化 后面没有初始化的元素 会自动赋值 0 如果是字符型 会赋值 '\0' 如果是指针 会初始化为NULL(即空指针)
(3) 如果想全部赋值为0,可以写成
或者全部元素赋值为 0 但是不能只写长度 一个元素都没有
(4) 确定数组所有元素,但是不写数组长度(系统会根据数组元素个数 给数组定义长度)
如果数组的长度 与元素个数不同,则必须写上数组长度
注意:不能一个元素都不赋值
3.二维数组:
(1) 基本概念
二维数组常被称为矩阵,二维数组--->行和列的排列形式
(2) 定义二维数组
在内存中 二维数组是线性存储的:
4.多维数组
多维数组的特点:第一维的下标变化最慢,最右边的下标变化最快
(1) 多维数组的初始化
1.分行给二维数组赋初始值
2.所有元素按内存中的排列顺序进行赋值 --->可以不指定第一维的长度(计算机会自动计算),但是第二维的长度必须设置
3.对部分元素赋值
5.字符数组
C语言中没有字符串类型,也没有字符串变量,字符串是存储在字符型数组中的
注意:字符型数组是以整数形式(ASCII代码)存放的 a--> 65
1.在字符数组长度之内,未赋值的元素会被初始化为'\0'
2.字符串的结束标志---> '\0'
字符数组在存储字符串常量时,会自动在末尾加'\0'; 以下二者等价
注意:以下代码的数组长度是11 而不是10 (末尾有个'\0')
如果不以'\0'结尾会出现在内存中乱找
1.数组指针
概念:指向整个数组的指针 进行指针运算 会跳跃数组长度的字节数
重点:N维数组的数组名是指向N-1维数组的数组指针 相当于数组指针
一维数组转二维数组://数组中每个元素都是4个int类型的数组
二维数组转一维数组: //数组中每个元素都是int类型
2.指针数组
概念:每个元素都是指针的数组
int* arr[] = {&a,&b,&c};
3.多维数组与指针
重点:N维数组的数组名是指向N-1维数组的数组指针
八 、 数组:
1.概念:
数组用来存储多个相同类型数据的内存分配方法
语法:元素数据类型 数组名[元素个数] = {初始值};
int a[5] = {1};
特点:多个、相同类型、连续的内存区域
数组名是数组首元素的符号地址,即数组的首地址
数组元素是数组中存储的数据,一般是多个,没有时,有默认值
2.数组使用
printf("%d\n",a[0]); a[0] 数组的下标从0开始
*&a[0] = 20 解引用
如果不初始化,必须要指定数组的长度
sizeof(数组名) 计算数组的占用内存大小
sizeof(a[0]) 计算数组单个元素的内存大小
数组名是数组首地址====第一个元素的首地址
sizeof(a)/sizeof(a[0]) ====计算数组的长度
3.动态数组 C99才支持
在定义数组时,通过变量指定数组的长度 非C99会报错
4. 多维数组:
二维数组:int arr[5][10] = {{},{},{}}
二维数组最右边的[]必须有长度
一.二维数组
a[i][j] - 表示二维数组某一个存储区的数据
i - 分组编号 - 一维数组下标
i - 0 ~ 一维数组个数减一
j - 组内下标 - 一维数组中的存储区下标
j - 0 ~ 存储区个数建议
7.指针与函数:
(1) 将函数的形参定义为指针,并且传递实参的地址
(2) 可以从函数中返回指针,但是不要返回指向局部变量的指针,因为该变量的内存在函数返回后会被释放,此类函数又被称为指针函数
可以返回static修饰的静态变量/返回全局变量的指针,因为在函数结束之后,内存不会销毁
8.指针与数组:
(1) 数组名本身是一个指针,代表数组的首元素地址
(2)数组元素下标访问的本质就是首元素地址做指针计算 然后解引用
(3)数组名是个指针常量,不能通过再次赋值令其指向其他的数据
9.泛型指针 ---void*(无类型指针)
(1)仅存储内存地址,不指定目标类型
(2) 目标类型不确定,不能直接解引用
(3)使用前,必须先进行数据类型转换
(4)泛型指针做指针计算,以1字节位单位
在ANSI C标准中,不允许对void指针进行算术运算如pvoid++或pvoid+=1等,而在GNU中则允许,因为在缺省情况下,GNU认为void *与char *一样。sizeof( *pvoid )== sizeof( char ).
10.常量指针与指针常量
(1) const型常量:
被const修饰的变量具有只读属性,必须在定义时初始化
(2)常量指针:
当const修饰指针时:
11.常量指针:指针变量保存的地址可以改变,指向的内存值不能修改
const int* 或 int const*
常量指针常作为函数的输入参数,在避免值赋值传递参数开销的同时,有效防止在函数中以外地修改实参数
22. 指针常量:
指针变量保存地址不能修改,内存值可以修改
int* const
数组名就是指针常量
arr++ 不可以进行指针计算 *(arr+i) = 5 可以使用解引用进行修改值
33.常量指针常量:
指针地目标和指针本身都只读:
const int* const 或 int const* const
11.指针 指向单个字节
(1)进行强转 然后解引用
12.高级指针(重点)
四、指针函数与函数指针、指针函数指针(重点)
注意:()的优先级大于 *
函数的参数最好不要超过四个
1.指针函数(函数名就是函数的首地址)
指针函数 --> 主体是 函数 --> 返回类型是指针
2.函数指针
函数指针---> 主体是 指针 ---> 指针指向一个函数 相当于调用对应的函数
表达式:int (*f)(int a , int b) ;
用法:可以提高代码复用性:
3.指针函数指针
--->返回一个指针,但是用的是函数指针调用对应的函数
4.回调函数 (函数指针部分内容)---> 函数作为参数 就是回调函数
五、动态分配内存
注意:动态分配了内存 一定要记得free释放内存
(1)分配内存的方式:
1.变量(静态)
2.数组(静态)
3.结构体(静态分配内存)
4.malloc 、call
静态分配内存的特点(缺点):占用的内存空间大小在编辑代码的时候已经定死了
(2)如何实现动态内存空间分配
首先需要包含头文件:#include<stdlib.h>
1.malloc --- memory allocation --内存分配
void* malloc (size_t size); --- 分配size个字节的内存空间 --堆中
堆中的内存特点:
a.直到free函数进行释放 ---注意:一定要记得释放内存空间 free
b.直到程序结束
返回值void* --》返回分配内存的首地址
如果分配失败 会返回NULL
2.free ---的参数就是malloc的返回值
void free(void* ptr);
释放ptr所指向的动态内存 ptr必须是之前malloc/calloc/realloc函数的返回值
释放一块已被释放过的内存,会导致未定义的后果
若ptr取NULL,则不会执行任何操作
所有动态分配的内存都需要释放,否则会导致内存泄漏
(1) malloc 分配内存
void* malloc (size_t size); --- 分配size个字节的内存空间 --堆中
(2)calloc分配内存 ---》 clear allocation 主要对数组进行分配空间
void* calloc(size_t nmemb,size_t size); --- 分配nmemb个元素的数组,其中每个元素占size字节
成功返回数组起始值 失败返回NULL 对分配数组的每个元素,用相应类型的0初始化
(3)realloc分配内存 ---》 reset allocation 调整内存 配合 malloc/calloc使用
void* realloc (void* ptr , size_t size );
将ptr所指向的动态内存大小调整为size字节,原内容不变,对新增部分不做初始化
成功返回数组起始值 失败返回NULL
ptr 必须是之前malloc/calloc/realloc函数的返回值
realloc 内存分配不够时: 首地址会发生变化
会从堆中继续寻找,直到找到一块连续16个字节的内存空间,以存储动态分配的内存
这个时候 ---> 首地址 p 就会发生变化
连着前面的 8 字节 + 新分配的 8 字节
realloc 缩减内存 和 free()函数相同 会把不用的内存释放掉
若ptr取NULL,则等价于 malloc函数 realloc(NULL,1024) 分配内存
若size取 0 realloc(ptr,0) ---> 等价于 free()释放内存
六、文件操作IO流:
1.fopen:打开一个文件
向文件中写入数据:
从文件中读取数据:
2.fclose:关闭一个文件
七、关键字
1.const
2.enum
3.extern
4.goto
5.register
6.signed、unsigned
7.sizeof
8.static
9.struct
10.typedef
11.union
12.volatile
八、结构体、联合体
1.结构体的基本概念与定义形式
自己设计的类型,用结构体封装一些属性
struct 结构名
{
成员列表(基本数据类型/指针/其他结构体)
}
struct Student
{
char s_id[8];
char s_name[8];
char s_sex[4];
int s_age;
};
2.结构体类型不占用空间,结构体中的变量需要占用空间
3.结构体初始化的几种方式
4.结构体访问成员变量的两种方式
5.结构体大小
6.结构体取别名