11.22 今天的内容有些难哦
取值运算符:*
取址运算符:&
占位符:%p
12.2 指针运算
12.2.1 指针加减运算
12.2.2 指针自增自减
指针加减指的是指针所指向的内存地址的移动,指针加1,就是移动指针所指向类型的长度,比如int型,就移动4个字节。
12.2.3 同类型指针相减
同类型指针相减的结果是ptrdiff_t类型的数据,ptrdiff_t类型是一个带符号的整数,格式占位符是%td。
不同类型的指针也是可以相减的,但是不同类型的指针相减没有什么意义,而且编译虽然不会报错,但是会有警告,不建议使用不同类型的指针相减。
12.2.4 指针的比较运算
指针之间可以比较运算,==,<,<=,>,>=,比较的是指向各内存地址的大小,返回值是int类型的整数0(false)或1(true)
12.3 指针和数组
12.3.1 数组名
数组名可以看作是一个常量,他的指向和内容是不可更改的,它指向的是数组的第一个元素,但是不能自增也不能自减,他的指向也不能修改,而且使用sizeof运算符,数组名会得到整个数组的大小,指针得到的是本身的大小。
指针本身的大小与编译器根据该cpu的寻址位数决定的,32位的电脑指针的大小是4字节,64位的电脑指针大小是8字节。
指针和数组可以混着用,不必掌握。
#include<stdio.h>
int main(){
int nums[5] = {10,20,30,40,50};
int *ptr = &nums[0];
//数组名可以认为是一个指针,指向了数组的第一个元素
printf("数组名的值为:%p\n",nums);
printf("指针ptr的值为:%p\n",ptr);
if (nums == ptr)
{
printf("数组名和指向数组首元素的指针的值是一样的!\n");
}
printf("nums[0] = %d\n",nums[0]); //10
printf("*ptr = %d\n",*ptr); //10
printf("nums[1] = %d\n",nums[1]); //20
printf("*(ptr+1) = %d\n",*(ptr+1)); //20
printf("%d\n",ptr[1]); //20
printf("%d\n",*(nums+1)); //20
//数组名和指针不同
//数组名类似于指针常量,而普通的指针是变量
printf("指针ptr的大小:%zu\n",sizeof(ptr));
return 0;
}
12.3.2 指针数组
他是一个数组,每个元素都是指针
语法规则:
数据类型 *指针数组名[长度];
12.3.3 数组指针
他是一个指针,指向一个数组,应用在二维数组,一个指针指向一个数组,指针加1将会指向下一个数组。
应用不是很多。
语法规则:
数据类型 (*数组指针名)[长度];
数组指针和数组名虽然输出的内容相同,但是含义不同,数组指针指向的是一整个数组的地址,数组名指向的是元素的首地址。
数组名是不可变的,但是数组指针是可以改变的,它可以指向不同的数组。
数组名不需要初始化,它会自动获取到数组的首地址作为数组名,但是数组指针就需要具体的初始化,说明数组的指向。
数组名
#include<stdio.h>
int main(){
int arr[5] = {10,20,30,40,50};
//定义一个数组指针
int (*arr_ptr)[5] = &arr;
//数组指针的值
printf("数组指针arr_ptr的值为:%p\n",arr_ptr); //00000026abfffb50
printf("数组名的值为:%p\n",arr); //00000026abfffb50
printf("&arr的值为:%p\n",&arr); //00000026abfffb50
//对数组指针进行加1操作
printf("数组指针arr_ptr + 1的值为:%p\n",arr_ptr + 1); //00000026abfffb64
//地址00000026abfffb64与地址00000026abfffb50相差了20个字符
printf("arr + 1的值为:%p\n",arr + 1); //00000026abfffb54
//地址00000026abfffb54与地址00000026abfffb50相差了4个字符
//用数组指针遍历数组元素
for (int i = 0; i < 5; i++)
{
printf("第%d个元素为:%d,地址为:%p\n",i+1,(*arr_ptr)[i],&(*arr_ptr)[i]);
}
// 第1个元素为:10,地址为:00000026abfffb50
// 第2个元素为:20,地址为:00000026abfffb54
// 第3个元素为:30,地址为:00000026abfffb58
// 第4个元素为:40,地址为:00000026abfffb5c
// 第5个元素为:50,地址为:00000026abfffb60
return 0;
}
arr是指向数组首元素的地址
&arr是指向整个数组的地址,他是数组指针,为指向数组的指针赋值。
12.3.4字符指针
11.24今天又是头秃的一天
字符串,可以修改的字符串,嘎嘎好用。
char *ptr = "hello tom~"
字符数组和字符指针的区别:对于字符数组修改只能一个一个修改,不能直接全部修改,但是字符指针是可变的。
12.4 指针和函数
12.4.1 传递指针给函数
函数形参内容是指针。
传递数组给函数,但是数组的本质即使传地址。
12.4.2 指针函数
一个函数他的返回值是指针。
语法规则:
返回类型 *指针函数名(参数列表)
返回值不能指向局部变量。
12.4.3 函数指针
一个函数也会占据着一段连续的空间,他与数组十分相似,把函数的首地址或者是是入口地址赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。
语法规则:
返回类型 (*函数指针名)(参数列表)
12.4.4 回调函数
函数指针作为函数的参数来使用,回调函数就是一个通过函数指针调用的函数。就是参数一个指针指向的函数。
12.4.5 多级指针
12.5 空指针
int *p = NULL;
赋值NULL,防止变成野指针。
12.6 野指针
12.6.2 野指针的成因
使用前未初始化。
指针越界访问。
指针指向已释放的空间。
12.7 指针使用一览
变量定义 | 类型表示 | 含义 |
---|---|---|
int i | int | 一个整型的变量 |
int *p | int * | 一个整型的指针 |
int a[5] | int[5] | 大小为5的整型数组 |
int *p[5] | int *[5] | 数组大小为5,数组元素是整型的指针 |
int (*p)[5] | int(*)[5] | 是一个指针,指向大小为5的数组 |
int f() | int() | 返回值为整型的函数 |
int *f() | int *() | 返回值为整型指针的函数 |
int (*p)() | int (*)() | 是一个指针,指向返回值为整型的函数 |
int **p | int ** | 是一个二级指针 |