文章目录
- 6. 指针
- 6.1 指针基础
- 6.1.1 指针变量的定义
- 6.1.2 指针变量的初始化和赋值
- 6.1.3 指针运算符
- 6.2 指针的算数运算
- 6.3 指针作为函数参数
- 6.4 指针和数组
- 6.4.1 指针和一维整型数组
- 6.4.2 指针和一维字符型数组
- 6.4.3 数组指针
- 6.4.4 指针和二维数组
- 6.4.5 指针数组
- 6.5 二级指针
- 6.6 指针函数
- 6.7 函数指针
- 6.8 const修饰的指针
- 6.9 void*指针
此处为C语言汇总版: 【C语言md编辑器版】从0基础开始到熟练掌握(完整篇,共9部分)
6. 指针
内存:程序运行时,可以访问到的内存存储空间;计算机为了方便对内存的管理,以字节为单位对内存进行了编号。
地址:计算机管理内存时,给内存的编号。
指针:指针即为地址,地址即为指针,指针指向了一个具体的空间。
指针变量:变量,保存地址的变脸,需要占用内存空间。
6.1 指针基础
6.1.1 指针变量的定义
指针变量保存的地址为指针指向数据的首地址。
基类型*变量名- 基类型:该指针指向空间存储数据的数据类型。(例如:int、char、float、构造数据类型…)
*:类型说明符,说明后面的变量是指针。- 变量名:遵循标识符的命名规则。(一般以p开头,没有强制要求)
sizeof(指针):在64位计算机系统下,占8字节。在32位计算机系统下,占4字节。
6.1.2 指针变量的初始化和赋值
- 野指针:未初始化或赋值的一个指针,或指向一个已经释放内存空间的指针。
- 避免野指针:
- 对指针定义初始化为空指针
NULL - 在指针定义时直接赋值。
#defineNULL(void*)0;//万能指针NULL - 对指针定义初始化为空指针
6.1.3 指针运算符
*指针运算符- 用来访问指针变量指向的内存空间的数据。
*指针变量——>指针降级为变量类型——>基类型的变量。
注意:int*p;p ——>int**p ——>int*指针运算符不可以用于普通变量,只能放在指针变量的前边。*指针变量,访问指针变量指向的数据,该表达式降级为变量类型——>基类型的变量。*指针变量,访问的字节数取决于定义该指针时的基类型。
| 类型 | 访问大小 |
|---|---|
| char *p | 1字节 |
| int *p | 4字节 |
| double *p | 8字节 |
&取地址运算符
&变量&只能给一个变量取地址,不能给常量或表达式取地址。&给某个变量取地址时,获取的是这个变量首字节的地址。
6.2 指针的算数运算
+ - * / % ++ --
+ * / %对指针运算毫无意义-:指针1-指针2:表示两个指针之间间隔的元素个数(地址值之间的字节差/sizeof(基类型))++:指针+1:表示从当前地址向后偏移sizeof(基类型)字节。--:指针-1:表示从当前地址向前偏移sizeof(基类型)字节。
char*p;p+1;//向后偏移一个字节。int*p;p+1;// 向后偏移一个字节。6.3 指针作为函数参数
- 被调函数中,想要修改主调函数中定义的变量,必须传递该变量的地址;
- 被调函数中,想要使用主调函数中定义的变量,可以传递值,也可以传递地址。
- 被调函数想要返回多个结果时,可以传递主调函数定义的变量的地址,地址指向的空间可以保存被
调函数得到的结果。
6.4 指针和数组
6.4.1 指针和一维整型数组
数组的数组名为数组的首元素地址。
注意:数组名为地址常量,不可赋值,不可移动。
一维整型数组作为参数传递:
voidprint_array(inta[],intlen){}voidprint_array(int*pa,intlen){}6.4.2 指针和一维字符型数组
charstr[32]={"hello"};str===>&str[0]====>char*-------->*str====>str[0]str+1===>&str[1]====>char*-------->*(str+1)===>str[1]...str+n===>&str[n]====>char*-------->*(str+n)===>str[n]str+1;指针向内存高地址偏移sizeof(char)字节数 str-1;指针向内存低地址偏移sizeof(char)字节数char*mystrcpy(char*dst,constchar*src){char*rdst=dst;while(*src!='\0'){*dst=*src;dst++;src++;}*dst='\0';returnrdst;}注意:const在这起到提高安全性的作用,保证源不会发生改变。
6.4.3 数组指针
- 本质:是一个指针,指向数组整体的指针。
基类型(*指针名)[常量];//数组的指针。int(*指针名)[5];//定义了一个指向5个整型元素的数组的指针。inta[5]={1,2,3,4,5};a:首元素地址——>&a[0]——>int*&a:数组的指针——>int(*)[5];&a+1:数组指针+1——>偏移整个数组,大小为sizeof(a)字节。注意:指针变量自己占8字节的空间大小。
总结:int a[5] = {1, 2, 3, 4, 5};a:首元素地址——>int *
除了两种情况:
1.sizeof(a/数组名);a不能当作int *类型,当作数组整体,代表整个数组。
2.&a;数组指针,该指针类型为:int (*)[5];指向一个数组。
6.4.4 指针和二维数组
二维数组的数组名是首元素地址,首元素是一维数组,所以二维数组名本质上是一个数组指针。
inta[5]={1,2,3,4,5};a 首元素地址&a[0]int*a[n]——>*(a+n)inta[2][3]={1,2,3,4,5,6};a 首元素地址&a[0]int(*)[3]——>数组指针 a[m][n]——>*(a[m]+n)——>*(a+m)[n]——>*(*(a+m)+n)二维数组的数组名作为参数传递:
voidprint_2d_array(inta[][列数],inthang){}voidprint_2d_array(int(*pa)[列数],inthang){}voidprint_2d_array(chara[][列数],inthang){}voidprint_2d_array(char(*pstr)[列数],inthang){}6.4.5 指针数组
定义:用数组来保存若干个指针。
语法:指针类型 *数组名[常量];
int*a[5];int*——>数组中保存的数据类型:该数组保存的元素是int*指针。[5]——>该数组中可以保存5个int*指针。 定义了一个可以保存5整型指针的数组,整体占40个字节。指针数组的初始化:
char*p=NULL;char*p[5]={NULL};将5个字符型指针都初始化成NULL;char*p="hello";char*p[5]={"hello","world","how","are","you"};p[0]——>是一个指针,保存字符串常量"hello"的首地址。char*p1="hello";char*p2="world";char*p[2]={p1,p2};指针数组和二维字符数组的区别:
charstr[3][100]={"hello","world","how"};//300字节char*pstr[3]={"hello","world","how"};//24字节- 二维字符型数组中保存的是多个字符串本身;指针数组中保存的是多个字符串的地址。
- 要保存多个字符串用二维字符型数组,要操作多个字符串用指针数组。
6.5 二级指针
定义:本质上还是指针,是一个指针变量的指针。
int**指针名称;——>保存整型指针的指针char**指针名称;——>保存字符型指针的指针C语言中主要有两个二级指针的使用场景:
- 想要在被调函数中修改主调函数中的指针变量,实参需要传递该指针变量的地址;形参用二级指针接收。
- 指针数组的数组名,是一个二级指针,实参传递指针数组的数组名时,形参用二级指针接收。
char*str[3]={"hello","world","!"};str[0];——>char*——>保存了字符串常量"hello"的首地址。 str ——>&str[0]——>char**二级指针值传递:无法在被调中修改主调中的变量,只能使用该变量
指针传参:可以在被调中修改主调中定义的变量,也可以使用该变量。
值传递:恰好传递的值是一个指针变量,无法在被调用修改主调中定义的指针变量,可以使用该指针变量。
指针传参:可以在被调函数中可以修改主调函数中定义的指针变量,也可以使用该变量。
6.6 指针函数
定义:本质上还是一个函数,具有指针类型返回值的函数,我们便称其为指针函数。
指针函数返回的指针类型:
局部变量的地址不能被返回(变量、数组);
charstr[32]={"hello world"};具有静态生命周期的变量的地址可以被返回。
- static修饰的静态变量地址。
- 全局变量的地址。
可以返回字符串常量的地址。
char*str="hello world!";可以返回通过参数传递给该函数的地址。
strcpy(dst,src);可以返回堆区申请且没有被释放的空间地址。
6.7 函数指针
定义:本质上还是一个指针,指向函数,我们便称其为函数指针。
函数:函数的函数名,为函数的入口地址。
函数的指针语法:
- 定义函数指针:
返回值类型 (*指针名称)(形参表)
注意:函数声明:intfun(intnum1,intnum2);函数指针:int(*pfun)(int,int);定义了一个函数指针变量,该指针变量在内存占8字节 该函数指针可以指向具有int类型返回值,两个int类型形参的函数。char*(*pfun)(char*);int *fun(int, int )为指针函数。int (*pfun)(int, int)为函数指针。 - 函数指针初始化
int(*pfun)(int,int)=NULL; - 函数指针赋值:
intadd(intnum1,intnum2){}int(*pfun)(int,int)=NULL;pfun=add; - 函数指针调用函数:
函数名(实参表);函数指针名(实参表); - 函数指针的应用
- 回调函数
- 将函数作为某个函数的参数传递,传递的函数称为回调函数。
- 降低代码耦合度,减少重复代码。
- 回调函数
6.8 const修饰的指针
const:read only ——>只读,必须初始化。
constintnum=10;该变量只允许被使用,不允许被改变。constint*p;——>与intconst*p 等价。intconst*p;——>修饰*p,对指针p指向的内容修饰。即p指向的内容不可改变。 p=&num//ok*p=1000;//noint*constp;——>修饰p,指针p只读,不能修改。因此定义该指针时必须为其初始化。 p=&num//no*p=1000;//okconstint*constp;与下面等价。intconst*constp;p的指向不能修改,*p中的内容也不允许修改,记得初始化。6.9 void*指针
void*:缺省类型指针。只是一个保存地址编号的变量。是一个万能指针。
void*<——char*int*short*double*float*void*p;*(int*)p;*(char*)p;char *int *short *double *float *:这些指针不仅保存地址,还有具体指向,可以明确指向sizeof(基类型)个字节空间。
注意:void *可以看作一个高精度的指针,其余看作低精度,即可以将其看作一个隐式转换。