认识指针
指针变量(指针)生来就是为了保存地址
总结:
- 指针就是变量,用来存放地址的变量。
- 数组传参降级为指针,即降级为指向其内部元素类型的指针。一维数组传参可以用
数组名
,也可以用数组名[]
,这些都会自动转变为数组元素类型对应指针。 - 任意的n维数组都可以看成元素都是(n-1)维数组的一位数组。如此递归,即可知道任意n维数组度看待成一位数组。
数组名单独出现
或者&数组名
,都表示这个数组。
int a[] = { 1, 2, 3, 4 };
printf("%d\n", sizeof(a));//16,数组名传参,降级为指针,代表整个数组。
printf("%d\n", sizeof(a + 0));//4,数组名+数字相当于指针+数字,指向第一个元素
printf("%d\n", sizeof(a + 1));//4,
printf("%d\n", sizeof(*a));//4,解引用的是第一个元素的值
printf("%d\n", sizeof(a[0] + 1));//4,sizeof(2)
printf("%d\n", sizeof(&a + 1));//4,整个数组的下一个地址,已经越界了
printf("%d\n", sizeof(&a[0] + 1));//4,第二个元素2的地址。
指针数组和数组指针
1、指针数组是数组,存储的元素是指针。
int* arr[10]; //数组元素int*
char* arr2[4]; //数组元素char*类型
char** arr3[5]; //数组元素char**类型
以上三式中arr[]
的优先级较高,所以三式都是数组,数组内元素是指针变量的指针数组。
2、数组指针是指针,是指向数组的指针。
例如:int (*p)[10];
,*p
的优先级高,所以是指针,指向一个数组的指针。
可这样定义一个数组指针:
int arr[10]={0};
int (*p)[10]=&arr;
指针传参
一级指针传参
#include<stdio.h>
#include<Windows.h>
void print(int *p,int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d\n", *(p + i));
}
}
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 ,10};
int *p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
print(p, sz);
system("pause");
return 0;
}
二级指针传参
#include<stdio.h>
#include<windows.h>
void test(int **ptr)
{
printf("%d\n", **ptr);//两次解引用
}
int main()
{
int n = 2;
int *p = &n;
int **pp = &p;
test(pp);
test(&p);
system("pause");
return 0;
}
函数指针
函数调用时也要形成栈帧,函数自然也有地址。那么用来保存函数的地址的变量就叫函数指针。函数名等同于函数取地址。
#include<stdio.h>
#include<unistd.h>
typedef void (*Func)(int a);
//定义了一个Func指针指向一个返回值为void类型形参是int类型的函数
void func(int a)
{
printf("%d\n",a);
}
void test(Func func,int a) //通过函数指针将函数当成参数传入test函数
{
func(a);
}
int main()
{
test(func,20);
return 0;
}
函数指针数组
同理,函数指针数组是数组,parr[]
优先级高,函数指针数组存储的内容是函数指针。
int (*parr1[10])();
函数指针数组的用途:转移表
例子:计算器
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<Windows.h>
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x*y;
}
int div(int x, int y)
{
return x / y;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[4])(int x, int y) = {add, sub, mul, div };//转移表
while (input)
{
printf("**********************************************\n");
printf("** 1: add 2: sub **\n");
printf("** 3: mul 4: div **\n");
printf("**********************************************\n");
printf("请输入:");
scanf("%d", &input);
if (((input<=4) &&( input>=1)))
{
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = (*p[input-1])(x, y);
}
else
printf("输入有误\n");
printf("ret=%d\n", ret);
}
system("pause");
return 0;
}
指向函数指针数组的指针
我们知道函数指针数组是数组,存储了函数指针的数组,就是一个一维数组中存储的全是这些函数的偏移量,通过这个偏移量就能找到这个函数。然后可以调用这些函数。
指向函数指针数组的指针,简称地址,实际上不管是什么指针都是地址,你只需要搞清楚它存储的是什么地址就OK了,这里存储的就是这个数组的地址。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<Windows.h>
void test(const char*str)
{
printf("%s\n", str);
}
int main( )
{
void(*pfun)(const char*) = test;
void(*pfunArr[5])(const char*str);
pfunArr[0] = test;
void(*(*ppfunArr)[10])(const char*) = &pfunArr;
system("pause");
return 0;
}
拓展内容:C++的多态中有虚函数的对象前四个字节中存储的就是函数指针数组的地址,拿到这个这个地址找到这个函数指针数组,通过偏移量找到虚函数的地址。就可以调用这个函数了。
多维数组
二维数组在内存中的存储也是连续,二维数组可以理解为一维数组中的元素是一维数组,数组在内存中都是线性存储的。以此类推,三维数组可以理解为一维数组中的元素是二维数组。所以说,多维数组都可以理解为一位线性顺序表(一维数组)。