C语言指针

时间:2022-11-22 14:59:11

1、指针定义

  指针是一个变量, 它保存的是另一个变量的地址, 即​​内存​​位置的直接地址。

C语言指针

int ptr; //整型变量,ptr+1 表示变量值+1;
const int ptr;//只读变量,和int const ptr效果一致;
int *ptr1; //定义一个整型指针, ptr+1 表示地址偏移 int 类型字节;
int ptr[3];//3 个成员的整型数组, ptr+1 表示指向 ptr[1]的地址;
int *ptr[3];//指针数组,ptr[3]是数组,int*再与 ptr[3]结合成指针数组, 表明数组中保存着 int 类型的指针, ptr+1 表示指向 ptr[1][]的地址, 即二维数组 ptr[1][0]的地址;
int (*ptr)[3];//数组指针,()优先级高,(*ptr)是指针, 再与[3]结合, 说明指针所指向的内容是数组, 最后和 int 结合, 表示数组元素类型是 int 类型。ptr+1 表示指向 ptr[1][]的地址, 即 ptr[1][]的地址;
int **ptr;//二维指针变量, 保存一维指针变量的地址;
int const *ptr;//常量指针,ptr指向的内容不可以修改,但可以改变指向;
int *const ptr;//指针常量,ptr的指向不可以修改,但可以修改指向的内容;

2、*p++、 *(p++)、 *++p、 ++*p、 *p+1、 *(p+1)

#include <stdio.h>
int main()
{
int buff[]={10,20,30};
int *p=buff;
/*1 buff[0]的地址:&buff[0]*/
printf("print1 buff addr:%#x\n",buff);
/*2 buff[0]的地址:&buff[0]*/
printf("print2 p addr:%#x\n",p);
/*3 偏移 sizeof(int)字节*/
printf("print3 p+1 addr:%#x\n",p+1);
/*4 取 buff[0]的值*/
printf("print4 *p=%d\n",*p);
/*5 取 buff[0]的值+1*/
printf("print5 *p+1=%d\n",*p+1);
/*6 地址偏移 sizeof(int)再取值, 即 buff[1]*/
printf("print6 *(p+1)=%d\n",*(p+1));
/*7 先取*p 的值,再 p+1*/
printf("print7 *p++=%d\n",*p++);
/*8 先取*p 的值, 再(*p)+1*/
printf("print8 ++*p=%d\n",++*p);
/*9 先 p+1,再取*(p+1)*/
printf("print9 *++p=%d\n",*++p);
}

 运行结果:

[xsw@xsw cc]$ ./a.out
print1 buff addr:0x2332967c
print2 p addr:0x2332967c
print3 p+1 addr:0x23329680
print4 *p=10
print5 *p+1=11
print6 *(p+1)=20
print7 *p++=10
print8 ++*p=21
print9 *++p=30

C语言指针

3.NULL指针和void*指针

  NULL 是标准宏定义, 用来表示空指针常量, 在声明指针变量时若没有确切地址赋值, 可以给指针变量赋 NULL。

#include <stdio.h>
int main()
{
char *ptr=NULL;
printf("ptr addr:%d\n",ptr);
return 0;
}

  执行结果:

[xsw@xsw cc]$ ./a.out
ptr addr:0

​  在大多数操作系统中程序不允许访问地址为 0 的内存, 因为该内存是操作系统保留的。 指针指向地址为 0 的内存表示该指针指向的地址不可访问。 按照惯例 NULL 指针假定为不指向任何东西。

    void 表示”无类型”, void*指针表示”无类型指针”, 即可指向任何数据类型。

#include <stdio.h>
int main()
{
void *p;
int a=100;
char buff[]={0,10,20,30};
p=&a;//保存 int 型变量地址
//void*指针访取值时要保证和所指向的变量类型一致
printf("*p=%d\n",*((int *)p));//强制转换成 int*
p=buff;//指向字符数组首地址
//强制转换成 char*
printf("*p=%d\n",*(char *)p);//buff[0]
printf("*(p+1)=%d\n",*(char *)(p+1));//地址往后偏移 char 字节: buff[1]
printf("*p+1=%d\n",*(char *)p+1);//取出的值+1: buff[0]+1
return 0;
}

 执行结果

*p=100
*p=0
*(p+1)=10
*p+1=1

  注:void 几乎只能用于指针的声明和函数返回值以及函数形参, 不能用于变量的定义。

4、二维指针

  二维指针:指向指针的指针。 保存一维指针的地址。
  指针声明:int **ptr;

#include <stdio.h>
int main()
{
int a=0x12;
int *ptr=&a;//一维指针保存变量地址
int **pptr=&ptr;//二维指针保存一维指针本身地址
//1 ptr==&a
printf("line1:a addr:%#x\n",ptr);
//2 *(&ptr)==&a
printf("line2:*(&ptr)=%#x\n",*(&ptr));
//3 pptr==&ptr
printf("line3:pptr :%#x\n",pptr);
//4 *ptr==*(&a)==a
printf("line4:*ptr=%#x\n",*ptr);
//5 *(&ptr)==ptr==&a
printf("line5:*pptr=%#x\n",*pptr);
//6 **pptr==**(&ptr)==*ptr=*(&a)
printf("line6:**pptr=%#x\n",**pptr);
//7 ***(&pptr)==**(&ptr)==*ptr==*(&num)=0x12
printf("line7:**pptr=%#x\n",***(&pptr));
}

 执行结果:

[xsw@xsw cc]$ ./a.out
line1:a addr:0xbf90384c
line2:*(&ptr)=0xbf90384c
line3:pptr :0xbf903848
line4:*ptr=0x12
line5:*pptr=0xbf90384c
line6:**pptr=0x12
line7:**pptr=0x12

5、数组指针

 指针定义:int (p)[n];指向数组整体的指针。 数组指针类型:int ()[n];

 ()优先级高, 所以 p 是一个指针, 指向一个整型的一维数组, 一维数组的成员个数位 n, 也就是说 p 的步长为 n。 即 p+1 偏移的地址为 n 个 int 型长度。 因此数组指针也称为行指针。

 优先级: () > [] > *

 示例一:数组指针指向一维数组示例。

#include <stdio.h>
int main()
{
int buff[]={11,22,33,44,55,66};
int (*ptr)[6];//数组指针,指向数组整体
ptr=&buff;
int i;
//数组指针 ptr+1 偏移指向的数组整体大小,即偏移 6*sizeof(int)=24 字节
printf("ptr addr:%#x\n",ptr);
printf("(ptr+1) addr:%#x\n",ptr+1);
/*数组指针遍历 buff*/
for(i=0;i<6;i++)
{
printf("%d ",*(*ptr+i));//或*(ptr[0]+i)或 ptr[0][i]
}
printf("\n");
return 0;
}

  执行结果:

[root@xsw cc]# ./a.out
ptr addr:0xbfbc8ce0
(ptr+1) addr:0xbfbc8cf8
11 22 33 44 55 66

  示例二:数组指针指向二维数组示例:

#include <stdio.h>
int main()
{
int buff[][3]={1,10,3,4,5,6};//每行有 3 个元素
int (*p)[3]=buff;
printf("*p[0]=%d\n",*p[0]);//等价于 buff[0][0]
printf("*(p[0]+1)=%d\n",*(p[0]+1));//等价于 buff[0][1]
printf("*p[1]=%d\n",*p[1]);//等价于 buff[1][0]
}

6、​指针数组​

      p[n];数组中保存 int指针。指针数组类型:int *[n], p的类型是 int **,p+1 偏移的地址为:sizeof(int *)。

  • 示例一:将一维数组赋值给指针数组。
#include <stdio.h>
int main()
{
int buff[]={11,22,33,44,55,66};
/*
//初始化直接赋值
int *ptr[10]={&buff[0],&buff[1],&buff[2],&buff[3],&buff[4],&buff[4]}
*/
int *ptr[10];//指针数组,数组中保存 int 型指针
/*数组指针赋值*/
int i;
for(i=0;i<6;i++)
{
*(ptr+i)=&buff[i];//或:buff+i
}
for(i=0;i<6;i++)
{
printf("%d ",*(*ptr+i));
}
printf("\n");
return 0;
}

  运行结果:

[root@xsw cc]# ./a.out
11 22 33 44 55 66
  • 示例二:将二维数组赋值给指针数组示例
#include <stdio.h>
void tow_array(int (*buff)[3],int line);
int main()
{
int buff[][3]={1,10,3,4,5,6};//每行有 3 个元素
int *p[3];
*p=buff[0];
p[0]=buff[0];
*(p+1)=buff[1];
printf("%d\n",(*p)[0]);//等价于:p[0][0]
printf("%d\n",(*p)[1]);//等价于 p[0][1]
printf("%d\n",*(p[0]+1));//等价于 p[0][1]printf("%d\n",*(p+1)[0]);//等价于 p[1][0], *(p+1)指向 buff[1][0]地址
printf("%d\n",*(*(p+1)+1));//等价于 p[1][1]
}
printf("%d\n",*(p+1)[0]);//等价于 p[1][0], *(p+1)指向 buff[1][0]地址
printf("%d\n",*(*(p+1)+1));//等价于 p[1][1]

注:二维数组赋值给指针数组时 p=buff 是错误的。

7、函数指针

  函数指针:指向函数的指针变量。 本质是指针变量。

#include <stdio.h>
int func(int a,int b);
int main()
{
//pfunc 表示函数指针变量
int (*pfunc)(int ,int );//定义函数指针,可以在定义时初始化
pfunc=func;//初始化函数指针,也可写成 pfunc=&func
int data=pfunc(10,20);
printf("%d\n",data);
}
int func(int a,int b)
{
return a+b;
}

 执行结果:

[xsw@xsw cc]$ ./a.out
30

  注:定义函数指针时必须保证和所指向的函数形参返回值一致

8、回调函数

     回调函数就是一个被作为参数传递的函数。

​     因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的​​被调用函数​​。简而言之,回调函数就是允许用户把需要调用的函数的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。

      ​回调函数在实际中有许多作用。假设有这样一种情况:我们要编写一个库,它提供了某些​​排序算法​​​的实现(如​​冒泡排序​​、​​快速排序​​、​​shell排序​​、shake排序等等),为了能让库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,能让库可用于多种数据类型(int、float、string),此时,可以使用​​函数指针​​,并进行回调。

​       C语言的回调函数只能通过函数指针实现,在C++中则可以使用匿名函数(lambda)或​​仿函数​​(functor)作为回调函数。

  • 回调函数示例:
#include <stdio.h>
int func_1(int a,int b);
int func_2(int a,int b);
int func_3(int a,int b);
void func_Callback(int a,int b,int (*pfunc[])(int,int),int num);
int main()
{
int data;
int i=0;
//定义函数指针数组
int (*pfunc[3])(int ,int)={func_1,func_2,func_3};
func_Callback(30,20,pfunc,sizeof(pfunc)/sizeof(int *));
}
int func_1(int a,int b)
{
printf("\t[%s] %s line:%d %d+%d=%d\n",__FILE__,
__FUNCTION__,
__LINE__,
a,b,a+b
);
return a+b;
}
int func_2(int a,int b)
{
printf("\t[%s] %s line:%d %d*%d=%d\n",__FILE__,
__FUNCTION__,
__LINE__,
a,b,a*b
);
return a*b;
}
int func_3(int a,int b)
{
printf("\t[%s] %s line:%d %d%%%d=%d\n",__FILE__,
__FUNCTION__,
__LINE__,
a,b,a%b
);
return a%b;
}
/***************************回调函数******************
**
**
**形参:int a,int b ---函数指针所需要动形参
** int (*pfunc[])(int,int) --- 函数指针数组
** int num ---函数指针数组成员个数
**
*********************************************************/
void func_Callback(int a,int b,int (*pfunc[])(int,int),int num)
{
int i;
int data;
for(i=0;i<num;i++)
{
data=pfunc[i](a,b);
printf("data=%d\n",data);
}
}

  执行结果:

[xsw@xsw cc]$ ./a.out
[func.c] func_1 line:17 30+20=50
data=50
[func.c] func_2 line:22 30*20=600
data=600
[func.c] func_3 line:27 30%20=10
data=10

C语言指针