目录
指针的本质
指针的分类
野指针
空指针
其他指针
字符串指针
数组指针与指针数组
函数指针
二级指针与多级指针
正文
指针的本质
大家觉得指针的本质是什么呢,其实就是一个地址而已,指针这个词对于初学C语言的提醒来说,可能不是特别友好,其实大家换成地址,就好理解了。指针本质就是一个地址,每个地址对应着一个数值,对地址取这个值,便是把这个值取出来了。这样说,还是好理解了吧。
指针的分类
野指针
野指针是一个指向未知(undefine)、不确定的地方的指针。
未知的、不确定的地方:可能存在,可能不存在。
并且指向的地方,可能可以访问,可能不可以访问。
对野指针的访问会有什么后果呢?
可能可以访问,可能不能访问(导致非法的内存访问)
非法的内存访问:
不存在的地方,你去访问。
存在但不能读,但是你去读了。
存在但不能写,但是你去写了。
非法的内存访问会导致:segment fault(段错误),系统会把你这个进程给杀掉(kill掉)
int *p;//定义一个指针变量,你不赋初值,不代表p没有值,相反p一定有一个值,
//只不过这个值是多少,不知道。
int *p;
int a; -->p为一个野指针
p = &a;//p不是一个野指针,指向了一个确定的地方
所以我们一般不建议用以上写法,我们建议在定义指针时,就给其赋值,避免成为野指针。如下:
int a;
int *p = &a; //<====>int *p; p = &a;
空指针
空指针
空指针是指向一个空的(NULL)指针。但空指针是野指针吗,显然不是。空指针不是一个野指针,但是空指针指向的地方一定是不能访问的。
对空指针访问会有什么后果呢,答案是出现“段错误。段错误也是使用指针时最常出现的错误。
其他指针
这里的其他指针,指的是定义正确,不是野指针与空指针的指针,如数组指针,函数指针等,我们一一介绍。
字符串指针
字符串与指针
1.字符串
字符串:一串字符,“多个字符”
单个字符,c语言有专门的类型:
char/unsigned char
单个字符,保存的是字符的ascii码
c语言中有没有字符串类型?没有,c语言中的字符是通过char *(字符指针)来实现的。
字符串:用一组连续的地址空间来依次存储每个字符。并且要访问它的话,要通过char*.
字符串就是一串字符,0个或者多个字符,并且约定以'\0'结束(ASCII码为0的字符)
’\0‘标志着字符串的结束。
我们只需要知道字符串的首地址(char *),就可以访问所有字符了。
so,char* 就可以描述一个字符串(保存的是字符串的首地址)
1.1字符串常量 const char *
不可以修改的字符串,只能读。 (存放在.rodata区域)
如:在C代码中,是用“”引起来的都是字符串常量
eq:
char *p = "abc";
p是一个字符指针,指向'a'的地址,保存'a'的地址;
“abc”:字符串常量
*p: 代表它指向的对象 'a', ’a‘没有左值,不可以被修改,'a'有右值
printf("%c",*p);
*p = 'A';//段错误 非法的内存访问
p + 1 保存的是字符b的地址
p + 2 保存的是字符c的地址
p + 3 保存的是'\0'的地址
p = "123"//没有问题的,p是一个变量,有左值,有右值。
2.2 字符串变量
字符串变量?可以修改的字符串,可读可写。
eq:
char s[5] = {'a','b','c'};
char *p = s;//p保存的是&s[0] ,'a'的地址
*p 代表的是它指向的对象 s[0],有左值也有右值
*p = 'A';
eq:
char s[5] = {"abc"};
==>
s[0] = 'a';
s[1] = 'b';
s[2] = 'c';
s[3] = '\0';
s[4] = 0;
char s[5] = {"abc"};
char *p = s;
*p = 'A';没有问题的。
==》 S是一个数组,如果数组是一个全局变量,s保存在.data区域【全局变量和静态变量(static)】;
如果说数组是一个局部变量,s保存在栈空间,所以,数组s无论保存在哪里,s都是可以被修改的。
数组指针与指针数组
指针数组和数组指针:
数组指针是什么? B
A 数组 B指针
数组指针就是一个指针,指向一个数组的指针。
函数指针就是一个指针,指向一个函数的指针。
int (*p)[4] ==>int[4] *p -->数组指针
p是一个指针,指向int[4]
p + 1 迈过了16个字节。
eq:
char (*p)[3] 数组指针 ==》char[3] *p
p + 1 迈过了3个字节
指针数组:
指针数组是什么? B
A 指针 B 数组
指针数组是一个数组,只不过数组元素都是指针。
int *p[4] // int* p[4]
p是一个数组名,里面有4个元素,每一个元素都是一个指针,每一个指针都是指向一个int型。
p是含有4个int*类型元素的数组
函数指针
有地址的对象,我们就可以定义一个变量,来保存它的地址。
eq:
int a;
int *p = &a;
int b[4];
int (*p)[4] = &b;//p:数组指针
函数也有地址,我们定义一个指针变量来保存函数的地址,这类指针,我们称为函数指针。
1.什么是函数指针?
函数指针就是一个函数的地址,
函数指针变量就是一个指针变量,只不过它指向了一个函数。
2.函数指针如何定义?
指针定义的语法:
指向的类型 *指针变量名
函数的类型在c语言中如何描述?
返回值的类型 (参数类型列表)
eq:
int sum(int a, int b)
{
}
sum是一个带两个参数,第一个参数是int类型,第二个参数也是Int类型,返回值是int类型的函数。
typeof(sum):
int (int,int)
如果要定义一个指针p,来保存sum的地址;
typeof(sum) *p;
int (int,int)*p; ==> int (*p)(int, int)
p = ∑
p = sum;
eq:
int find_max(int *b, int n);
定义一个指针p,保存find_max的地址
typeof(find_max) *p;=> int (int *, int) *p//编不过去
=》int (*p)(int*, int);
函数指针定义的语法:
指向函数的返回值类型 (*函数指针变量名)(指向函数的参数类型列表)
3.如何获取一个函数的地址?
1.& 取地址符
eq:
&abc
2.函数名
在C语言中,函数名本身就是一个地址,函数的地址。
eq:
void abc(int b[],double f) -->abc函数名
typeof(abc) *p = void(int[],double) *p = void (*p)(int[],double)
p = &abc
p = abc
4.如何通过函数指针去调用函数呢?
1,普通的函数调用
函数名(实参表达式列表);
eq:
find_max(a,4);
p = &find_max;//定义了一个函数指针变量p
p = find_max;//函数名本身就是一个地址
*p= *&find_max = find_max
*函数指针 ==》 指向的函数
eq:
*p= *&find_max = find_max
2.通过函数指针来调用函数:
(1)
(*函数指针)(实参表达式列表)
int (*p)(int, int)
eq: (*p)(3,4)
(2)
函数指针名(实参表达式列表)
p(a,4) = find_max(a,4)
5.为什么需要函数指针?
函数指针是用来实现callback(回调)的
call me back
回调?
回过头来调用,我现在不用。
通过函数指针保存一个函数的地址,等需要调用时我再来通过函数指针来调用。--》回调
eq:
int sum(int a, int b)
{
return a + b;
}
int main()
{
int (*p)(int,int);//定义了一个函数指针
p = ∑
int a;
a = sum(3,5);
printf("%d\n",a);
a = (*p)(10,12);
printf("%d\n",a);
a = p(10,10);
printf("%d\n",a);
}
二级指针与多级指针
二级指针和多级指针(---->就是一个指针)
int a;
定义一个指针变量,来保存a的地址。
typeof(a) *p; //p是一级指针
int *p;
p = &a;//int *p = &a;
p本身也是有地址的,我们可以定义一个指针变量p2来保存p的地址。
typeof(p) *p2;
int** p2; //p2是2级指针
p2 = &p;
p2本身也是有地址的,我们可以定义一个指针变量p3来保存p2的地址。
typeof(p2) *p3;
int*** p3;//p3是3级指针
p3 = &p2;
p3本身也有地址,我们定义一个指针变量p4来保存p3的地址。
typeof(p3) *p4; int**** p4;//p4是4级指针 p4 = &p3; ... p = &a; p2 = &p; p3 = &p2; p4 = &p3; int main() { int a = 1024; int *p, **p2,***p3, ****p4; p = &a; //1024 p2 = &p; //1024 p3 = &p2; //1024 p4 = &p3; //1024 => *p,**p2,***p3,****p4. }
*p = *&a = a;
*p2 = *&p = p
**p2 = **&p = *p = *&a = a;
***p2 = ***&p = **p = **&a = *a;//ERROR :a的值不是一个地址
*p3 = *&p2 = p2
***p3 = ***&p2 = **p2 = **&p = *p = *&a = a;
****p4 = ****&p3 = ***p3 = ***&p2 = **p2 = **&p = *p = *&a = a;