指针-中

0 阅读8分钟

文章目录

  • 系列文章目录
  • 前言
  • 1、使⽤指针访问数组
  • 2、⼀维数组传参的本质
  • 3、⼆级指针
  • 4、指针数组
  • 5、数组指针变量
  • 6、函数指针变量
  • 7、函数指针数组
  • 总结

前言

继上一篇博客我们了解了关于指针的基础知识,下面我们来接着来学习关于指针方面更为深入的一些问题吧,一起来看看吧!


1、使⽤指针访问数组

数组名就是数组⾸元素(第⼀个元素)的地址,但是有两个例外:

1、sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩, 单位是字节

2、&数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素 的地址是有区别的)

我们来看一下下面这些例子:

#include <stdio.h>
int main()
{
 int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
 printf("&arr[0] = %p\n", &arr[0]);
 printf("&arr[0]+1 = %p\n", &arr[0]+1);
 printf("arr = %p\n", arr);
 printf("arr+1 = %p\n", arr+1);
 printf("&arr = %p\n", &arr);
 printf("&arr+1 = %p\n", &arr+1);
 return 0;
}

编译结果:

&arr[0] = 0077F820&arr[0]+1 = 0077F824arr = 0077F820arr+1 = 0077F824&arr = 0077F820&arr+1 = 0077F848

由此我们可以看出,&arr[0]和&arr[0]+1相差4个字节,arr和arr+1 相差4个字节,是因为&arr[0] 和 arr 都是 ⾸元素的地址, +1就是跳过⼀个元素。

但是 &arr 和 &arr+1相差40个字节,这就是因为&arr取的是数组的地址,+1 操作是跳过整个数组的。

2、⼀维数组传参的本质

        那么在函数内部我们写 sizeof(arr) 计算的是⼀个地址的⼤⼩(单位字节)⽽不是数组的⼤⼩(单位字节)。正是因为函 数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。

        ⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

void test(int arr[])//参数写成数组形式,本质上还是指针
{
 printf("%d\n", sizeof(arr));
}
void test(int* arr)//参数写成指针形式
{
 printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 test(arr);
 return 0;
}

3、⼆级指针

      在C语言中,二级指针是一个指向另一个指针的指针。它不仅保存了一级指针的地址,而且还可以通过解引用来访问最初指向的值。理解二级指针对于深入学习C语言中的指针概念至关重要。

       一个一级指针指向一个普通变量,并保存该变量的地址。相比之下,二级指针指向一个一级指针,并保存该一级指针的地址。这种关系可以通过以下代码示例来展示:

int a = 10; // 普通变量
int *p = &a; // 一级指针,指向变量a
int **s = &p; // 二级指针,指向指针p

      在这个例子中,a 是一个普通的 int 类型变量,p 是一个指向 a 的一级指针,而 s 是一个指向 p 的二级指针。通过对 s 进行一次解引用( *s),我们得到的是 p 的值,即 a 的地址;通过对 s 进行两次解引用( **s),我们可以直接访问 a 的值。

       对二级指针的操作涉及到指针的解引用和地址的计算。例如,假设我们有三个连续的 int 类型变量 a1a2 和 a3,以及它们对应的一级指针 p1p2 和 p3。我们可以通过二级指针 s 来操作这些变量:

int a1 = 1, a2 = 2, a3 = 3;
int *p1 = &a1, *p2 = &a2, *p3 = &a3;
int **s = &p1;

        在这种情况下,s + 1 会使二级指针 s 指向 p2,而  *s + 1 会使 p1 指向 a2 的地址。这些操作的结果取决于指针指向的数据类型的大小。

        二级指针在C语言中有多种应用,例如在 main 函数的参数中使用。main 函数可以接受两个参数:argc 参数计数)和 argv(参数向量)。argv 是一个二级指针,它指向一个字符串数组,这些字符串是程序运行时传递给 main 函数的参数。

4、指针数组

    定义为:int p[n];* (注意优先级:()>[]> *)[]> * ,所以p是数组,是一个由n个指针类型元素组成的指针数组,或者说这个当一个数组里含有的元素为指针类型的时候,它就被成为指针数组。当p+1时,则p指向下一个数组元素。(需注意,p=a;这种赋值方法是错的,因为p是一个不可知变量,只存在p[0],p[1],p[2],但可以这样 p=a; 这里p表示指针数组第一个元素的值,a的首地址的值)
将二维数组赋值给指针数组:

int *p[3];     //定义指针数组
int a[3][4];
for(i=0;i<3;i++)
p[i]=a[i];     //通过循环将a数组每行的首地址分别赋值给p里的元素  

5、数组指针变量

       数组指针也称为行指针,定义为:*int( * p)[n]; (注意优先级:()>[]> (int (*p)[5]定义了一个指向含有5个元素的一维数组的指针。)

当数组指针指向一个一维数组时:

int( * p)[n];    //定义了指向含有n个元素的一维数组的指针
int a[n];        //定义数组
p=a;             //将一维数组首地址赋值给数组指针p

      ()优先级高,说明p是指针,指向一个整型的一维数组。这个一维数组的长度是n,也可以说p的步长为n。当p+1时,p指针会跨过n个整型数据的长度。

6、函数指针变量

       函数存放在内存的代码区域内,它们同样有地址.如果我们有一个 int test(int a) 的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。

1、函数指针的定义方式

data_types (*func_pointer)( data_types arg1, data_types arg2, ...,data_types argn);

int (*fp)(int a); // 这里就定义了一个指向函数(这个函数参数仅仅为一个 int 类型,函数返回值是 int 类型)的指针 fp。

示例:

int test(int a)
{
    return a;
}
int main(int argc, const char * argv[])
{
    
    int (*fp)(int a);
    fp = test;
    cout<<fp(2)<<endl;
    return 0;
}

函数指针所指向的函数一定要保持函数的返回值类型,函数参数个数,类型一致。

2、我们可以用typedef 定义可以简化函数指针的定义

int test(int a)
{
    return a;
}
 
int main(int argc, const char * argv[])
{
    
    typedef int (*fp)(int a);
    fp f = test;
    cout<<f(2)<<endl;
    return 0;
}

3、 函数指针同样是可以作为参数传递给函数的

int test(int a)
{
    return a-1;
}
int test2(int (*fun)(int),int b)
{
    
    int c = fun(10)+b;
    return c;
}
 
int main(int argc, const char * argv[])
{
    
    typedef int (*fp)(int a);
    fp f = test;
    cout<<test2(f, 1)<<endl; // 调用 test2 的时候,把test函数的地址作为参数传递给了 test2
    return 0;
}

4、利用函数指针,我们可以构成函数指针数组,更明确点的说法是构成指向函数的指针数组。

void t1(){cout<<"test1"<<endl;}
void t2(){cout<<"test2"<<endl;}
void t3(){cout<<"test3"<<endl;}
 
int main(int argc, const char * argv[])
{
    
    typedef void (*fp)(void);
    fp b[] = {t1,t2,t3}; // b[] 为一个指向函数的指针数组
    b[0](); // 利用指向函数的指针数组进行下标操作就可以进行函数的间接调用了
    
    return 0;
}

7、函数指针数组

       我们前面知道可以把一个整型类型的指针放入一个数组,比如:int * arr[10],可以把一个字符类型的指针放入一个数组,比如:char *arr[5],那么可不可以把统一类型的函数指针也放入一个数组里面呢。

我们来看一下函数指针数组的形式:

*指针:int p

*函数指针:int (p)(int ,int)

*指针数组:int  parr[10]

那么函数指针数组首先肯定是数组,数组的每一个元素应该是函数类型的指针那么函数指针数组的形式: int (*parr[5])(int,int)

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

#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;
}

​编辑

​编辑


总结

关于指针---中这一篇章就结束了 ,有什么错误或需要改正的地方可以提出来,我会虚心接受的,后续我会带来关于指针方面的更多内容,如果文章对你有帮助的话可以点个赞支持一下博主