我们先来复习下指针的概念:
指针就是地址,不过我们平常说的指针指的是指针变量,指针变量可以存放地址,地址唯一标识一块内存空间
指针在32位平台上是4个字节的大小,而在64位平台上是8个字节的大小
指针类型的意义:一是决定指针加减整数的步长大小,二是指针解引用的访问权限
指针与指针可以进行相减,相减的结果等于指针所指向的两个元素间的距离
接来下我们深入的了解下指针
字符指针
字符指针一般用来存放字符的地址,但是它还可以在字符串上使用,如下
#include<stdio.h>
int main()
{
char* p="hello world";
return 0;
}
乍一看这不是将字符串放到指针里了,但我们知道指针是用来存地址的,所以这种说法错的。
实际上p里面存的是字符串第一个字符的地址,在上述代码中也就是h的地址
知道了第一个字符串的地址我们就可以访问这个字符串的内容了
访问这里面的内容有两种方式,如下
#include<stdio.h>
#include<string.h>
int main()
{
char* p = "hello world";
int i = 0;
int len = 0;
len = strlen(p);
for (i = 0; i < len; i++)
{
printf("%c", *(p+i));
//printf("%c", p[i]);
}
return 0;
}
一种是指针方式,另一种是数组方式,其实当我们对指针和数组的理解深入后,就会发现在一些情况下,指针和数组可以相互转换,就如上面这个代码
小提示:字符指针虽然可以指向字符串,但是我们不可以使用这个指针去修改字符串里面的内容,因为该字符串是常量字符串不允许被修改,所以用这个字符指针时可以在前面加上个const,防止字符串被修改
接下来我们来看一道面试题
#include<stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char *str3 = "hello bit.";
const char *str4 = "hello bit.";
if(str1 ==str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 ==str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
这里的最终输出是
让我们来解析下
str1和str2两个字符数组分别用同样的内容进行赋值,虽然内容相同,但是终究是两个数组,他们分配到的地址一定是不相同的,就像双胞胎,是两个人,所以str1和str2的地址不同
而str3和str4是两个字符指针指向相同的字符串,这里关键在于我们的系统是否会给出两块空间创建相同的字符串呢?答案是不会的,因为前面说过常量字符串是不允许被修改的,所以字符串一定不变的,那么有什么必要将相同的字符串放到两块空间呢?所以这里str1和str2是指向同一个字符串的,所以str1和str2所存地址相同
指针数组
字符指针存放字符,整型指针存放整型,类比过来,我们可以得知指针数组存放指针
int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
......
指针数组可以用来模拟二维数组的实现,如下
#include<stdio.h>
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 6,7,8,9,10 };
int arr3[] = { 11,12,13,14,15 };
int* p[3] = { arr1,arr2,arr3 };
{
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for(j=0;j<5;j++)
printf("%2d ",p[i][j]);
printf("\n");
}
}
return 0;
}
因为我们知道数组名是首元素的地址,int的地址类型就是int*,所有我们可以用整型指针数组接收,然后我们用p[i][j]找到每个元素,这里的原理是,我们先用p[i]找到每个数组的数组名,然后用数组名[j]去找到数组中的每个元素
前面说过在一些情况下,数组和指针可以相互转换,上面这个print也可以写成下面的形式
printf("%2d ", *(*(p + i) + j));
这里给大家个公式p[i]=*(p+i)
从这里也可以看出数组访问操作符的实质不就是指针嘛
数组指针
3.1数组指针的定义
通过类比的方法我们也容易得出,数组指针就是指向数组的指针,指针里面存的是数组的地址
那么数组指针该怎么写呢?下面展示整形数组指针的写法
int (*p)[10]
//解释:p与*号结合代表p是一个指针,向左右看可以看出指针p指向的类型是一个大小为10个整型的数组
//注意:[]的优先级高于*号,所以必须加上()保证p先与*结合
3.2&数组名与数组名的区别
对于下面这个数组
int arr[10]
arr和&arr的区别是什么呢?
我们来看看下面这段代码和它的运行结果
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr+1= %p\n", &arr+1);
return 0;
}
我们可以发现arr和&arr的值是一样的,它们+1跳过的大小却不一样
这是因为arr的类型是int*,+1跳过一个整形的大小
而&arr的类型是int(*)[10],+1跳过10*int,也就是整个数组的大小
从这里也可以看出arr和&arr[0]是等价的,也就是我们常说的数组名就是首元素的地址
3.3数组指针的使用
数组指针一般用来接收二维数组的数组名,以此来访问二维数组中的元素,代码如下
#include<stdio.h>
void print(int(*p)[5], int r, int c)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
printf("%d ", p[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
print(arr, 3, 5);
return 0;
}
用int(*p)[5]接收arr,是因为在二维数组里数组名代表首行数组的地址,也就是这里的arr的类型为int(*)[5],可以用数组指针来接收
函数指针
来看一段代码和它的结果
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
这说明函数也有地址,而且函数名和&函数名是等价的,
既然是地址就可以用指针保存,这里的指针就是函数指针,如下
int add(int x,int y)
{
return x+y;
}
int main()
{
int (*p)(int,int)=&add
return 0;
}
p与*结合表示p代表指针,向左右看可以看出p指向的是函数返回类型为int,函数参数为int和int的函数
函数指针数组
函数指针数组,就是类型为函数指针的数组,因此我们在函数指针的形式上稍加改造就可以得到函数指针数组,如下
int(*p[10)(int,int)
p先与[10]结合表示p是数组,往外看可以看出p的类型为int(*)(int,int)
函数指针数组可以作为转移表使用,来简化代码,计算机的实现就可以使用函数指针数组,如下
#include<stdio.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;
}
void menu()
{
printf("****************************************\n");
printf("**************1.add 2.sub**************\n");
printf("**************3.mul 4.div**************\n");
printf("****************************************\n");
printf("请选择\n");
}
int main()
{
int input = 1;
int x = 0;
int y = 0;
int ret = 0;
int(*p[5])(int, int) = { 0,add,sub,mul,div };
do
{
menu();
scanf("%d", &input);
if (input <= 4 && input >= 1)
{
printf("请输入操作数\n");
scanf("%d%d", &x, &y);
ret = (p[input])(x, y);
}
else
{
printf("输入有误\n");
}
printf("ret=%d\n", ret);
} while (input);
return 0;
}
指向函数指针数组的指针
来看一段代码,我们将所学知识融合下
void test(const char *str)
{
printf("%s\n",str);
}
int main()
{
void(*pfun)(const char *str)=&test;
//函数指针
void(*pfunarr[5])(const char *str);
pfunarr[0]=test;
//函数指针数组
void(*(*ppfunarr[]5))(const char *str)=&pfunarr;
//指向函数指针数组的指针
}