【C语言】指针内容详细梳理

本文详细介绍了指针与地址的概念,探讨了不同类型的指针(包括一级指针、二级指针、数组指针和函数指针),以及它们之间的关系,如指针变量的内存大小、解引用操作、不同类型指针的运算差异,以及回调函数和转移表的应用。
摘要由CSDN通过智能技术生成

1.指针和地址的关系

 1.1内存和地址

               如果把电脑内存理解为一个个小房间,其最小单位为bit,也就是说每一个小房间中都“住着”一个“1”或者“0”。

        

那么以上就是内存中一个字节,保存数字“8”的表现形式。

        地址的发明是为了好的管理内存,就好像为每个房间标上门牌号一样,地址以字节为单位为每个字节内存编址,方便内存的管理和使用。

                

1.2指针变量和地址

        和int,char等等变量类型一样,指针也是一种变量类型,指针变量是c语言中用于存储地址的变量。

若创建指针变量a,b,c并存入地址,大概会是这样的情形:

1.3指针和地址的大小

        由于指针是存放地址的变量,所以指针和地址所占用的内存大小应该相同。

        32位平台下地址是32个bit位,指针变量⼤⼩是4个字节。

        64位平台下地址是64个bit位,指针变量⼤⼩是8个字节。

注意:

        在相同平台下,所有指针不论什么类型占用内存的大小都相同。

2.不同的指针类型(一级指针)以及用途

        一般类型的指针的表示一般为在变量后加上“*”,例如:

                int -------->int *整型变量的指针

                char-------->char *字符变量的指针

2.1指针解引用操作符“*”和取址操作符“&”

        通过解引用操作符“*”,我们可以找到指针变量所存放地址指向的内容。

        

#include <stdio.h>

int main()
{
	int n = 1;
	int* a = &n;
	printf("%d\n", *a);
	return 0;
}

 

通过取址操作符“&”,可以取出变量的地址:

#include <stdio.h>


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

 

2.1.1不同类型指针解引用的区别 

         不同类型指针有着不同的访问权限,例如:

        int *类型指针解引用时会使用其存放地址向后的4个字节大小的空间,

        char * 类型指针解引用时会使用其存放地址向后的1个字节大小的空间。

        也就是说,指针所指向的变量类型占用多少空间大小,指针解引用时就可以访问多少空间大小。

        

如果用代码来显示其差异:

#include <stdio.h>

int main()
{
	int n = 0x11223344;
	int* a = &n;
	char* b = &n;
	short* c = &n;
	printf("%x\n",*a);
	printf("%x\n",*b);
	printf("%x\n",*c);


	return 0;
}

指针a,b,c中存放相同的内容,解引用后得到的结果如下: 

        

 这是因为在小端机器上,0x11223344的存储方式为:

是高字节位存高地址处,低字节位存低地址处,所以在解引用时产生了如下差异:

        

这就是存放相同地址时,不同指针类型解引用时的不同。

2.1.2不同类型指针+-运算的区别

        不同类型的指针在加减运算时也有区别:

        

#include <stdio.h>

int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;

	printf("n地址:%p\n", &n);
	printf("char*存储:%p\n", pc);
	printf("char*存储+1:%p\n", pc + 1);
	printf("int*存储:%p\n", pi);
	printf("int*存储+1:%p\n", pi + 1);
	return 0;
}

        

可以看到,指针类型为char*时,+1地址也+1,跳过一个字节;指针类型为int*时,+1地址+4,

跳过4个字节。这就是不同指针类型+-运算时的区别——

        不同类型指针进行+ - 运算时,+ - 的一定是整数,并且跳过字节数是其指向空间类型的大小。

        例如:
        int * + 1跳过4个字节;

        char* + 1跳过1个字节;

        short * + 1跳过2个字节;

       (32位系统) int ** + 1跳过4个字节;

        (64位系统)int** + 1跳过8个字节;

3.多级指针和数组指针(一维数组和二维数组)

3.1二级指针及多级指针

        二级指针和多级指针就是指针的指针,用于存放指针变量地址的变量。

        以int为例,其关系大致如图:

形式:

        一级指针:一般为类型后加“*”:如char *,int *,short *;

        二级指针:一般为一级指针类型后加“*”:如char **,int **,short**;

        多级指针:一般为n-1级指针类型后加“*”:如char ***,int****;

        

#include <stdio.h>

int main()
{
	int a = 1;
	int* p1 = &a;
	int** p2 = &p1;
	int*** p3 = &p2;


	return 0;
}

要点:

        C语言指针类型非常严格,每一种变量类型都有其对应的指针类型。

3.1数组指针

         数组指针和指针数组一定要区分清楚。

                数组指针是一种指针------------->指向数组类型的指针

                指针数组是一种数组------------->元素为指针的数组

数组指针定义(int数组指针为例):

int (*p)[10];

表示p变量为指向一个大小为10个元素的int数组的指针变量; 

指针数组定义(int*数组为例):

int *p[10];

        表示p是一个大小为10个元素的数组,其中元素的类型为int *;

        

3.1.1数组指针和二维数组的关系

    

#include <stdio.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int(*p)[9] = arr;//此时arr代表首元素地址------>数组名为数组首元素的地址
	int* p1 = p + 1;
	printf("%d\n", *p1);
	return 0;
}

运行结果为:

 

通过分析可知,数组指针p+1跳过了9个字节的地址,也就是说:

        数组指针进行+ -  n运算时,实际地址会+ - 其指向数组大小的倍数*n;

        数组指针+1运算时会跳过整个数组(指针类型和数组类型相对应时)来到数组末尾;

又因为int (*)[](数组指针)解引用后得到整型指针int *,

由此可以使用数组指针模拟实现二维数组。

        

#include <stdio.h>

int main()
{
	int a[2][2] = {1,2,3,4};
	int b[4] = { 1,2,3,4 };
	int(*c)[2] = b;
	printf("%d\n", a[1][0]);
	printf("%d\n", c[1][0]);

	return 0;
}

数组指针即为二维数组数组名的本质,这也是为什么函数传参传二维数组时,函数定义参数中的

二维数组行数可以省略而列数不能省略。

4.函数指针及函数指针数组

4.1函数指针

        

        其定义方式如下:


  int (*pf3) (int x, int y)
//|     |     ------------
//|     |          |
//|     |          pf3指向函数的参数类型和个数的交代
//|     函数指针变量名
//pf3指向函数的返回类型
  int (*) (int x, int y) //pf3函数指针变量的类型

         函数指针即为指向函数的指针,实际上函数名本质上就是函数指针。

        所以两者在使用上类似于数组和指针,并无差异:

        

#include <stdio.h>

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int ret;
	int ret1;
	int (*add)(int, int) = Add;
	ret = Add(1, 2);
	ret1 = add(1, 2);
	printf("%d\n", ret);
	printf("%d\n", ret1);
	return 0;
}

 

4.1.1回调函数

        回调函数就是⼀个通过函数指针调用的函数。 如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被用来调用其所指向的函数 时,被调用的函数就是回调函数回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

        在某些库函数例如qsort的使用中,我们需要用到自己定义的回调函数。

4.2函数指针数组

        函数指针数组就是由函数指针变量组成的数组。

        使用函数指针数组有时可以大大减少代码量(转移表),

以简易计算器的实现为例子:

        不使用函数指针数组时:

#include <stdio.h>
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;
 do
 {
 printf("*************************\n");
 printf(" 1:add 2:sub \n");
 printf(" 3:mul 4:div \n");
 printf(" 0:exit \n");
 printf("*************************\n");
 printf("请选择:");
 scanf("%d", &input);
 switch (input)
 {
 case 1:
 printf("输⼊操作数:");
 scanf("%d %d", &x, &y);
 ret = add(x, y);
 printf("ret = %d\n", ret);
 break;
 case 2:
 printf("输⼊操作数:");
 scanf("%d %d", &x, &y);
 ret = sub(x, y);
 printf("ret = %d\n", ret);
 break;
 case 3:
 printf("输⼊操作数:");
 scanf("%d %d", &x, &y);
 ret = mul(x, y);
 printf("ret = %d\n", ret);
 break;
 case 4:
 printf("输⼊操作数:");
 scanf("%d %d", &x, &y);
 ret = div(x, y);
 printf("ret = %d\n", ret);
 break;
 case 0:
 printf("退出程序\n");
 break;
 default:
 printf("选择错误\n");
 break;
 }
 } while (input);
 return 0;
}

        使用函数指针数组时

#include <stdio.h>
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 }; //转移表
 do
 {
 printf("*************************\n");
 printf(" 1:add 2:sub \n");
 printf(" 3:mul 4:div \n");
 printf(" 0:exit \n");
 printf("*************************\n");
 printf( "请选择:" );
 scanf("%d", &input);
 if ((input <= 4 && input >= 1))
 {
 printf( "输⼊操作数:" );
 scanf( "%d %d", &x, &y);
 ret = (*p[input])(x, y);
 printf( "ret = %d\n", ret);
 }
 else if(input == 0)
 {
 printf("退出计算器\n");
 }
 else
 {
 printf( "输⼊有误\n" ); 
 }
}while (input);
 return 0;
}

可以看到使用指针数组时类似函数的调用可以用p[input](x,y)来统一概括,大大简化了代码。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值