很多人在学习指针的时候,可能只停留在表面的理解,并没有深入的思考指针的特性。
就比如只了解以下的基础知识:
- 指针就是一个变量,用来存放地址,地址唯一标识一块内存空间。
- 指针的大小是固定的4 / 8 个字节,(32位平台 / 64位平台)
- 指针是有类型,指针的类型决定了指针 + - 整数的步长,指针解引用操作时候的权限。
- 指针的运算。
对于指针的其他方面的理解,就如下所示:
一.字符指针
字符指针是一种指针类型char*
int main()
{
char c = 'w';
char *pc = &c;
*pc = 'w';
//还有另一种指针操作的方法
char* pstr = "hello world";
printf("%s\n",pstr);
return 0;
}
对于上述代码中 char* pstr = “hello world”;会使人认为是将字符串“hello world”放在字符指针pstr里了,但是本质上是把字符串hello bit,首字符的地址放在pstr中。
其实就是将首字符为h的地址存放在指针类型的变量中。
二.指针数组
指针数组是一个存放指针的数组。它不是指针,是数组。
例如:
int* arr1[10]; //整形指针的数组
char **arr2[4]; //一级字符指针的数组
char **arr3[5]; //二级字符指针的数组
int (* p arr[10]) [5]表示的含义是:arr是一个数组,这个数组中有10个元素,每个元素都是一个指针,
而每个指针都指向一个数组,数组中存放int型的五个元素。
三.数组指针
1.数组指针是指针?还是数组?
指针
例如:整形指针:int* pint 是能够指向整形数据的指针;
浮点数指针: float* pint 是指向浮点数数据的指针;
数组指针:就是指向数组的指针;
2.
int * p1[10];
//p1先和[]结合,是一个空间大小为10的数组,每个数组里面存放是个指针。
int (*p2)[10];
//*先和p2结合,说明p是一个指针变量,然后指向的是一个大小为10个整形的数组,
//所以p是一个指针,指向一个数组,叫做数组指针。
3.&数组名和数组名之间的区别
#include<iostream>
int main()
{
int arr[10] = {0};
printf("arr = %p\n",arr); //arr = 0133FBB0
printf("&arr = %p\n",&arr); //&arr = 0133FBB0
printf("arr + 1 = %p\n",arr + 1); //arr + 1 = 0133FBBB4
printf("&arr + 1 = %p\n",&arr + 1); //&arr + 1 = 0133FBD8
return 0;
}
由上面的代码可以得出结论:
arr表示的是数组首元素的地址,&arr表示的是数组的地址
arr + 1表示跳过一个元素的大小;
&arr + 1表示跳过一个数组的大小;
四.函数指针
#include<iostream>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n",test); //013211DB
printf("%p\n",&test); //013211DB
return 0;
}
输出的是两个地址,这两个地址都是test函数的地址。那么,该怎样存放test的地址呢?
void test()
{
printf("hehe\n");
}
void (*fun1)();
//想要存放地址,那么首先证明它是指针,fun1先和*结合,说明fun1是指针,
//指针指向的是一个函数,指向的函数无参数,返回值类型为void
五.函数指针数组
把函数的地址存放到一个数组中,那么这个数组就叫做函数指针数组。
例如:
int (arr[10]) ();arr先与[]结合,说明arr是一个数组,数组中的内容是int ()()类型的函数指针。
下面就是一个例子:用函数指针数组实现的 - 计算器
#include<iostream>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a*b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = { 0, 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])(x, y);
}
else
printf( "输入有误\n" );
printf( "ret = %d\n", ret);
}
return 0;
}
六.指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针。
void test(const char* str)
{
printf("%s\n",str);
}
int main()
{
//函数指针fun
void (*fun) (const char*) = test;
//函数指针的数组funarr
void (*funarr[5]) (const char* str);
funarr[0] = test;
//指向函数指针数组funarr的指针pfunarr
void (*(*pfunarr)[10]) (const char*) = &funarr;
return 0;
}
七.回调函数
回调函数就是一个通过函数指针调用的函数,如果你把函数得指针(地址)作为一个参数传递给另一个函数,当这个指针被用来调用其所指向得函数时,我们就说这是回调函数。回调函数不是由该函数得实现方进行调用,而是在特定得事件或者条件发生时由另一方调用得,用于对该事件或者条件进行响应。
#include <iostream>
#include <string>
using namespace std;
typedef void (*FP)(char* s); //结构体表示函数指针
void f1(char* s){cout<<s;}
void f2(char* s){cout<<s;}
void f3(char* s){cout<<s;}
int main(int argc,char* argv[])
{
int funcselector=0; //定义一个整数用于控制待执行的函数
void* a[]={f1,f2,f3}; //定义了指针数组,这里a是一个普通指针
a[0]("Hello World!\n"); //编译错误,指针数组不能用下标的方式来调用函数
FP f[]={f1,f2,f3}; //定义一个函数指针的数组,这里的f是一个函数指针
/* Handle of funselector */ //此处用于处理funselector,控制待执行的函数
f[funselector]("Hello World!\n"); //正确,函数指针的数组进行下标操作可以进行函数的间接调用
return 0;
}
上面一个例子中提现了回调函数的部分作用。这里f1,f2,f3表示三个功能不相同的函数(举例说明:f1实现最大值输出,f2实现平均值输出,f3实现最小值输出)。总结一下回调函数的一些优势:
采用funcselector作为标志量,选择待执行的函数很方便的控制了函数的流程和工序。
f1,f2,f3三个特定函数模块化明显,便于设计者去维护、修改。如图1-1所示,很多系统中software library会完全封装,这样开发者只能通过回调函数去修改函数功能。
分析函数思路更加清晰,在lwip中大量使用回调函数,开发者可以根据回调函数的调用流程分析系统结构
笔试题:
//sizeof(数组名) 表示整个数组,计算的是整个数组的地址
//&数组名 表示整个数组,取出的是整个数组的地址
//除此之外所有 的数组名表示的是首元素的地址
//sizeof内部的表达式不参与运算。
//一维数组
void test1()
{
int a[] = { 1, 2, 3, 4 };
printf("%d\n", sizeof(a));//16 sizeof(a) / sizeof(a[0])元素个数
printf("%d\n", sizeof(a + 0));//4 首元素地址
printf("%d\n", sizeof(*a));//4 首元素的大小
printf("%d\n", sizeof(a + 1));//4 第二个元素的地址
printf("%d\n", sizeof(a[1]));//4 第二个元素
printf("%d\n", sizeof(&a));//4 &a取出的是数组的地址 在32位平台上是4个字节
printf("%d\n", sizeof(*&a)); //16 数组的地址解引用,访问的是数组的大小
printf("%d\n", sizeof(&a + 1));//4 下一个数组的地址
printf("%d\n", sizeof(&a[0]));//4 第一个元素的地址
printf("%d\n", sizeof(&a[0] + 1));//4 第二个元素的地址
}
//字符数组
void test2()
{
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f'};
printf("%d\n", sizeof(arr));//6 数组的大小
printf("%d\n", sizeof(arr + 0));//4 首元素地址4个字节
printf("%d\n", sizeof(*arr)); //1 首元素的大小
printf("%d\n", sizeof(arr[1])); //1 第二个元素的大小
printf("%d\n", sizeof(&arr));//4 数组的地址
printf("%d\n", sizeof(&arr + 1));//4 下一个数组的地址
printf("%d\n", sizeof(&arr[0] + 1));//4 第二个元素的地址
printf("%d\n", strlen(arr));// 随机值
printf("%d\n", strlen(arr + 0)); // 随机值
//printf("%d\n", strlen(*arr)); 错误--只能传地址
//printf("%d\n", strlen(arr[1])); 错误--只能传地址
//printf("%d\n", strlen(&arr)); 随机值 == arr
//printf("%d\n", strlen(&arr + 1)); 随机值--下一个数组的地址
printf("%d\n", strlen(&arr[0] + 1));// 第二个元素的地址
}
void test3()
{
char arr []= "abcdef";
printf("%d\n", sizeof(arr));//7 数组的大小:要+'\0';
printf("%d\n", sizeof(arr + 0));//4 首元素的地址
printf("%d\n", sizeof(*arr));//1 第一个元素的大小
printf("%d\n", sizeof(arr[1]));//1 第二个元素的大小
printf("%d\n", sizeof(&arr));//4 数组的地址
printf("%d\n", sizeof(&arr + 1));//4 下一个数组的地址
printf("%d\n", sizeof(&arr[0] + 1));//4 第二个元素的地址
printf("%d\n", strlen(arr));//6
printf("%d\n", strlen(arr + 0));//6
//printf("%d\n", strlen(*arr)); err
//printf("%d\n", strlen(arr[1])); err
//printf("%d\n", strlen(&arr)); 6
//printf("%d\n", strlen(&arr + 1)); x
printf("%d\n", strlen(&arr[0] + 1));//5
}
void test4()
{
char *p = "abcdef";
printf("%d\n", sizeof(p));//4 数组的地址
printf("%d\n", sizeof(p + 1));//4 第一个元素的地址
printf("%d\n", sizeof(*p));//1 首元素的大小
printf("%d\n", sizeof(p[0]));//1 首元素的大小
printf("%d\n", sizeof(&p));//4 数组p的地址char **
printf("%d\n", sizeof(&p + 1));//4 第二个数组的地址
printf("%d\n", sizeof(&p[0] + 1));//4 第二个元素的地址
printf("%d\n", strlen(p)); //6 数组的长度
printf("%d\n", strlen(p + 1));//5 第二个元素到结束的长度
//printf("%d\n", strlen(*p)); err
//printf("%d\n", strlen(p[0])); err
//printf("%d\n", strlen(&p)); x
//printf("%d\n", strlen(&p + 1)); x
printf("%d\n", strlen(&p[0] + 1));//5 第二个元素到结束的长度
}
//二维数组
void test5()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a)); //48 数组的大小
printf("%d\n", sizeof(a[0][0])); //4 第一个元素的大小
printf("%d\n", sizeof(a[0])); //16 第一行数组的大小
printf("%d\n", sizeof(a[0] + 1)); //4 第一行第二个元素的地址
printf("%d\n", sizeof(*(a[0] + 1))); //4 第一行第二个元素的大小
printf("%d\n", sizeof(a + 1)); //4 第二行的地址
printf("%d\n", sizeof(*(a + 1))); //16 第二行数组的大小
printf("%d\n", sizeof(&a[0] + 1)); //4 第二行的地址
printf("%d\n", sizeof(*(&a[0] + 1)));//16 第二行数组的大小
printf("%d\n", sizeof(*a));//16 第一行数组的大小
printf("%d\n", sizeof(a[3]));//x 随机值--第四行数组的地址
}
int main()
{
test1();
test2();
test3();
test4();
test5();
system("pause");
return 0;
}