指针的简单了解

一  什么是指针

        在计算机中,数据是以二进制的形式进行存储的,而在存储时内存会随机分配一个空间给需要存储的数据,这些空间都有一个属于自己的地址,如果想要想要通过地址来访问这个地址所存储的数据,就需要用到指针。通过指针,就可以对地址所指向的内存单元的值进行访问,指针指向的便是内存单元的地址。

int a=0;
int* p=&a;

1.1 指针的解引用

      指针存放的是内存单元的地址,那么,通过指针来访问内存单元的数据,就需要对指针进行解引用。

      如果说,内存单元是一个房间,那么房间号就是地址,*就相当于解引用时候的一把房间钥匙,通过*来访问房间内存放的数据。

      因为数据在内存中有int,char等类型,不同类型所占的字节数是不一样的,char* 类型的指针解引用就只能访问一个字节,而int* 类型的指针解引用就是访问四个字节。

 

1.2 二级指针

      指针变量也是变量,变量就有地址,存放指针地址的指针就是二级指针

int main()
{
    int a=10;
    int* pa=&a;//用pa存放a的地址
    int* ppa=&pa;//用二级指针ppa来存放一级指针pa的地址
}

 

二 指针类型

2.1 字符指针

      C/C++会把常量字符串储存在一个单独的区域(rodata段),在这个区域中,(read only)只能对数据进行读取,而不能更改(更改的话程序会崩溃掉的)。当几个字符指针指向同一个字符串的时候,实际上会指向同一块内存。

int main()
{
	int* str1 = "hello word";//把字符串的首字符h的地址放在指针变量str1中
	int* str2 = "hello word";
	if (str1 == str2)
	{
		printf("str1 = str2\n");
	}
	else
	{
		printf("str1 != str2\n");
	}
        printf("%c", *str1);
	return 0;
}

2.2 指针数组

     指针数组是一个存放指针的数组,数组指向指针,可以用来存放变量的地址。

int* arr1[10];//整形指针的数组,数组的有十个(可以存放int型数据地址的)内存单元
char* arr2[10];//一级字符指针的数组
char** arr3[5];//二级字符指针的数组

2.3 数组指针

    2.3.1 数组指针

    数组指针是一个指向数组的指针,指针指向数组,可以用连续的内存区间来存放不同数组的地址,函数名等,以此来方便调用。

int (*p)[10];

     p先和*结合,说明p是一个指针变量,然后和[10]结合,指针指向的是一个大小为10的int型数组 。

     [ ]的优先级高于*,所以要加( )来使p和*先结合。

     2.3.2 数组名和&数组名

    对于一个数组,其数组名表示数组首元素的地址;而对数组名进行取地址,代表的意义则是整个数组的大小。虽然打印出二者的地址数值上是一样的,但是表示的意义确是不同的。

int main()
{
	int arr[3] = { 0 };
	printf("%p\n", arr);//数组首元素的地址
	printf("%p\n", &arr);//整个数组的地址
	printf("%p\n", arr + 1);//数组第二个元素的地址
	printf("%p\n", &arr + 1);//表示数组最后一个内存单元+1
	return 0;
}

      2.3.3  数组指针的使用

      既然数组指针中存放的是数组,函数的地址,那么就可以通过数组指针来对函数的一个参数(二维数组的参数)进行解引用来使用。

//void printf_p(int p[3][5], int row, int col)//一般写法
void printf_p(int(*p)[5], int row, int col)//用数组指针接收
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			printf("%d ", *(*(p + i) + j));// p[i][j]相当于*(*(p + i) + j)
		}
		printf("\n");
	}
}
int main()
{
	int a[3][5] = { 1, 2, 3, 4, 5, 6, 7 };
	printf_p(a, 3, 5);
	return 0;
}

     二维数组在传参的时候,数组名表示第一行整个元素的地址,可以用数组指针接收,对数组第一行的地址进行解引用。 

指针和数组应用题解

int main()
{
	//一维数组
	int a[] = { 1, 2, 3, 4 };
	printf("%d\n", sizeof(a));//16
	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
	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
}

 &a表示整个数组的地址,而*&a就是对整个数组进行解引用,所以是4*(sizeof(a[0])),既16个字节大小。

int main()
{
	//字符数组
	char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
	printf("%d\n", sizeof(arr));//6
	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));//随机值
	printf("%d\n", strlen(arr + 0));//随机值
	//printf("%d\n", strlen(*arr));
	//printf("%d\n", strlen(arr[1]));
	printf("%d\n", strlen(&arr));//随机值
	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//随机值-1
}

strlen是求字符串的长度,遇到\0就停止,参数类型是char* ,不能是char类型的数据;对一段地址求长度,会随机一个不同的值。 

int main()
{
    char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));//7
	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));
	printf("%d\n", strlen(arr[1]));*/
	printf("%d\n", strlen(&arr));//随机值
	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//5
}

 &arr[0]+1表示arr[1]的地址,符合strlen的参数格式char*;而&arr表示整个数组的地址,对他进行解引用后是一个随机值。

int main()
{
    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
	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));
	printf("%d\n", strlen(p[0]));*/
	/*printf("%d\n", strlen(&p));
	printf("%d\n", strlen(&p + 1));*/
	printf("%d\n", strlen(&p[0] + 1));//5
}

 p表示数组首元素的地址,&p则是指针p的地址,对指针的地址进行读取,就要用二级指针来接收。

int main()
{
   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-->a[0][1]地址的大小
	printf("%d\n", sizeof(*(a[0] + 1)));//4-->a[0][1]数据所占字节的大小
	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]));//16
}

 sizeof在编译期间只是确定一个大小,不参与运算;

数组名的意义:

      1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
      2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
      3. 除此之外所有的数组名都表示首元素的地址

2.4 函数指针

   2.4.1 函数指针

   每一个函数都有一个唯一的函数名,那么这个函数名有什么意义呢?它代表的又是什么?

void text()
{
}
int main()
{
	printf("%p\n", text);
	printf("%p\n", &text);
	return 0;
}

代码运行后,显示的是两个一样的地址的值,说明函数名代表的是函数的地址。

int main()
{
    void (*pfun1)();
}

     pfun1是一个指针,指向一个函数,指向的函数无参数,返回值类型为void

int main()
{
    void* pfun();
}

     函数名为pfun ,返回值类型为void* ,无参数的函数指针

int main()
{
    (*(void (*)() 0)();
}

       调用0地址处,不带参数且返回值类型为void的函数

int main()
{
    void (*signal(int ,void(*)(int)))(int);
}

        signal是一个函数,他有两个参数,一个是int ,一个是函数指针( 参数为int ,返回值类型为void), 返回值类型为函数指针 void (*)(int) 。

----》typedef void(*pfun)(int);

----》pfun (int ,pfun) ;

  2.4.2 函数指针数组

       在函数中,函数名代表着这个函数的地址,那么如果把函数的地址存放在一个数组中,那么我们在访问函数的时候,就不用再把函数名书写一遍,可以通过数组来访问函数。

     函数指针数组的用途:转移表

//利用函数指针数组来实现一个简单的计算器
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 input = 1;
	int x = 0;
	int y = 0;
	int ret = 0;
	int (*p[5])(int ,int) = {0,add,sub,mul,div};//p-->函数指针数组 数组长度为5
	//  int (*) (int,int);
	while(input) 
	{
		printf("*******************************\n");
		printf("*****1.add********2.sub*******\n");
		printf("*****3.mul********4.dev********\n");
		printf("*********************************\n");
		printf("请输入你的操作:");
		scanf("%d",&input);
		if(input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:");
			scanf("%d%d",&x,&y);
			ret = (*p[input]) (x,y);//调用函数指针
			printf("ret = %d\n",ret);
		}
	}
	return 0;
}

  2.4.3 指向函数指针数组的指针

     指向函数指针数组的指针,首先,这是一个指针,指针指向的是一个数组,而数组的元素是函数指针。

int main()
{
    void (*p1) ();  //函数指针
    void (*p2[5])();  //函数指针数组
    void ( * (*p3)[5]) ()=&p2;
}

       p3是一个指针,指向一个数组 长度为5,数组里面放的是 void (*)(); 的函数指针 。

三 回调函数

     回调函数就是通过函数指针调用的函数。

     如果把函数的指针(地址)作为参数传递给另一个函数时,回调函数就是这个指针被用来调用其所指向的函数。其实,回调函数就是在一个函数中,以参数的形式再调用自己写的另一个函数,因为函数名表示这个函数的地址,所以在调用时需要使用函数指针来解引用。

实例:qsort 函数的使用

int int_cmp(const void* p1,const void* p2)
{
	return *(int *)p1-*(int *)p2;
	//void 不能进行解引用,使用数据时需要进行强转为int 或 char类型
}

int main()
{
	int a[10] = { 2, 4, 9, 8, 7, 5, 6, 3, 1, 10 };
	int i = 0;
	qsort(a,sizeof(a)/sizeof(a[0]),sizeof(int),int_cmp);
	for (i = 0; i < 10; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	return 0;
}

  使用回调函数来模拟实现qosrt函数内部的执行过程

//用冒泡排序法模拟实现qsort
int int_cmp(const void* p1, const void* p2)
{
	return *(int *)p1 - *(int *)p2;
}

void swap(void* p1,void* p2,int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp=*((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2+ i) = tmp;
	}
}
void qsort_pop(void* a,int len, size_t size, int (*int_cmp)(void*,void*))
{
	//len-->数组长度  size-->数组的一个数据所占的字节数
	//(*int_cmp)(void*,void*)-->回调函数int_cmp,参数类型为void*
	int i = 0;
	int j = 0;
	for (i = 1; i < len; i++)
	{
		for (j = 0; j < len - 1-i; j++)
		{
			if (int_cmp((char*)a + j*size, (char*)a + (j + 1)*size)>0)
			{
				swap((char*)a + j*size, (char*)a + (j + 1)*size,size);//调用交换函数
			}
		}
	}
}

int main()
{
	int a[10] = { 2, 4, 9, 8, 7, 5, 6, 3, 1, 10 };
	int i = 0;
	qsort_pop(a, sizeof(a) / sizeof(a[0]), sizeof(int), int_cmp);
	for (i = 0; i < 10; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	return 0;
}

        在判断数组中两个数的大小的时候,因为所传的数据类型为void,所以需要进行强转,char类型在内存中占一个字节,可以用数据的第一个字节+一个数据的字节数来表示这个void型数据。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值