指针

很多人在学习指针的时候,可能只停留在表面的理解,并没有深入的思考指针的特性。
就比如只了解以下的基础知识:

  • 指针就是一个变量,用来存放地址,地址唯一标识一块内存空间。
  • 指针的大小是固定的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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值