目录
1.什么是指针?
指针全名为指针变量,我们知道计算机在存储数据是有序存放的,而对于如何能够使用到存放的数据,我们就需要一个门牌号来区别每个数据所在的位置,而这个门牌号就是我们常说的地址,指针变量就是用来存放这些地址的变量。
1.指针的大小为固定的4/8个字节(32或64位平台)。
2.指针是有类型的,指针的类型决定了指针加减整数时的步长以及解引用指针是的权限。
4.指针加减整数时,以所指向数据类型的字节为单位进行变动,例:
#include<stdio.h>
int main()
{
int* p = NULL;
printf("%p\n", p ); //00000000
printf("%p\n", p + 1);//00000004
return 0;
}
2.多级指针
正如上一点所说,指针也是变量,因此指针在内存中也是通过地址的形式来存储的,因此一级指针的地址我们用二级指针来表示,例:
#include<stdio.h>
int main()
{
int* p = NULL;
int** p1 = &p;
printf("%p\n", p ); //00000000
printf("%p\n", p1);//0073FDB0
return 0;
}
其中二级指针p1是存储一级指针p地址的指针变量。以此类推,我们还可以有三级指针,四级指针等等。但只需要懂得其核心思想,其他的并不常用。
3.常用指针
1.整形指针
整形指针是指,指针所指向的数据类型为整形,用int*来定义,如int* p=&a,其中int表示p所指向的数据为整形,*则提示p为指针变量。
2.字符指针
与整形指针类似,我们知道有一种指针类型为字符指针char*;
int main()
{
char ch = 'w';
char* p = &ch;
*p = 'w';
return 0;
}
除此之外, 还有一种使用方法:
int main()
{
char* chr = "hell0";
printf("%c", *chr);
return 0;
}
看起来是将一个字符串放到了指针变量chr中,但其实是将首元素h的地址放到了chr中。
3.数组指针
顾名思义,数组指针就是把一个数组的地址放到一个指针变量中,则这个指针变量就是数组指针。它的本质还是一个指针,这与指针数组有这本质上的区别。(指针数组本质上是一个数组,数组中的元素都是指针)
int* a[10];
int(*b)[10];
其中,a先与[10]结合,通过数组的定义,我们可以得知a为数组名,这个数组一共有10和元素,每个元素都是int*类型,因此a为指针数组。
因为有括号的存在,b与*先结合,这就说明b是一个指针,它指向的类型是int [10],是一个数组,其中有10个元素,元素的类型是int,因此b为数组指针。
在数组指针这块的知识中,常见的难点就是数组名的使用。
3.1 &数组名VS数组名
int arr[10];
我们都知道arr是数组名,数组名表示数组首元素的地址。
请看下面一段代码:
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr); //00D5F81C
printf("%p\n", &arr); //00D5F81C
printf("%p\n", arr+1); //00D5F820
printf("%p\n", &arr+1);//00D5F844
return 0;
}
其中arr与&arr的地址相同,但是在加一之后地址就大不相同,这是为什么?
正如我们之前所提到的,指针的整数运算是按照指向数据的类型进行计算的,从这我们就可以看出arr与&arr看似存储的地址相同,但类型却不相同。这是因为在单独使用数组名时,数组名表示首元素的地址,但是&数组名表示的是整个数组的地址,其中的数组名表示的是整个数组。
tips:一般情况下,数组名表示首元素地址,但两种情况除外:1.&数组名。2.sizeof(数组名)。这两种情况下,数组名表示整个数组。
3.2数组指针的使用
数组指针指向的是数组,因此其中存放的应该是数组的地址。
int main()
{
int arr[10] = { 0 };
int(*p)[10] = &arr;//将arr数组的地址传给指针变量p
return 0;
}
3.3数组传参
一维数组传参:
void test(int arr[])//ok?
{}
void test(int arr[10])//ok?
{}
void test(int* arr)//ok?
{}
void test2(int* arr[20])//ok?
{}
void test2(int** arr)//ok?
{}
int main()
{
int arr[10] = {0};
int* arr2[20] = {0};
test(arr);
test2(arr2);
return 0;
}
上述形参的书写形式都是正确的,
test(arr)中的实参就是arr数组的首元素地址,在形参的部分我们需要用一个一级指针来接受实参。
test2(arr2)中的实参为指针数组arr2中首元素的地址,因此我们需要在形参部分采用一个二级指针来接受实参。
tips:在指针变量中,arr[0]等价于*(arr+0),*arr[0]等价于**(arr+0)。
二维数组传参:
void test(int arr[3][5])//ok
{}
void test(int arr[][])//x
{}
void test(int arr[][5])//ok
{}
void test(int *arr)//x
{}
void test(int* arr[5])//x
{}
void test(int (*arr)[5])//ok
{}
void test(int **arr)//x
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}
在二维数组中,每一个元素都是一个一维数组。test(arr)中的实参是首元素的地址,也就是一个一维数组的地址,因此在形参部分我们可以用一个数组指针来接收实参。
tips:在指针部分,int arr[][5]等价于int (*arr)[5]。
4.函数指针
函数指针:指向函数的指针被称为函数指针。
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test); //00421249
printf("%p\n", &test);//00421249
return 0;
}
输出的两个地址完全一样,都是test函数的地址,因为函数名和&函数名都表示函数的地址,这一点与数组名略有差异。
那函数指针该如何保存呢?
void test()
{
printf("hehe\n");
}
void (*pfun1)();
void *pfun2();
答案是pfun1。 首先,能给存储地址,就要求pfun1或者pfun2是指针,其中只有pfun1是指针,因为*先与pfun1结合,表示其为指针变量,所指向的函数类型是void ();而pfun2表示的是函数名,这个函数返回值为void*类型。
关于函数指针还有一个难点就是函数指针数组,也就是说把一系列的函数的地址存放在一个数组中,这个数组就是函数指针数组。关于函数指针数组,会在后面的博客中单独梳理知识。
本人拙见,大佬勿怪!^v^