C语言基础(七)----指针(二)_二级指针



目录


一、指针和数组

区别:


指针可以申请一块内存当作数组使用

数组直接定义使用


相同点


  • 都可以管理一块内存
  • 指针和数组都可以用 * 和 [ ] 访问内存

#include<stdio.h>
int main()
{
int arr[4] = { 0,1,2,3 };
int *temp = arr;
//arr[1]和*(arr+1)等效
printf("arr[1]:%d\n*(temp+1):%d", *(arr + 1),temp[1]);
return 0;
}

不同点


  • sizeof(数组名)得到数组大小,sizeof(指针)得到指针大小
  • 指针可以++,–,数组名不可以
  • 指针可以重新指向 可以重新申请一块更大的内存(动态数组大小可变),静态数组定义之后大小就固定了

注意


  • 数组名作为函数实参传参的时候退化成指针
  • sizeof(任何类型的指针)的值都是4(32位系统下)

在解释指针数组和数组指针之前先来解释下数组的存储结构

//定义一个二维数组
int a[3][4] = { { 1,3,5,7 },{9,11,13,15},{17,19,21,23} };

那么它的存储结构如下图:

a[0]是a[ 0 ] [ 0 ]的地址,也是a数组的首地址

同理,C语言基础(七)----指针(二)_二级指针_02a[1]是a[ 1 ] [ 0 ]的地址,a[2]是a[ 2 ] [ 0 ]的地址

指针数组


指针数组:存放指针的数组 本质是数组,数组元素是指针


int*  arr[3];   //指针数组  10个int*
  • 指针数组和数组的搭配使用
#include<stdio.h>
int main()
{
int arr[3][4] = { { 1,3,5,7 },{9,11,13,15},{17,19,21,23} };
int *p[3];
for(int i=0;i<3;i++)
{
p[i] = arr[i]; //指针数组指向二维数组每行的首地址
}
//输出指针数组指向的数组
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
printf("%d\t", p[i][j]);
putchar(10); //换行
}
return 0;
}

数组指针


也称行指针表示方法:*int(p)[n]

数组指针:指向一个整型的一维数组,这个一维数组的长度是n


  • 数组指针一般和数组搭配使用
#include<stdio.h>
int main()
{
int arr[3][4] = { { 1,3,5,7 },{ 9,11,13,15 },{ 17,19,21,23 } };
int(*p)[4] = arr; //指针数组指向数组arr
//打印指针数组指向的值
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
printf("%d\t", *(*(p + i) + j)); //printf("%d\t",p[i][j]);
putchar(10);
}
getchar();
return 0;
}
在这里我想解释下为什么 *(*(p + i) + j)) 和 p[i][j] 等价;
指针数组的表示方法:int(*p)[n]
p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n;
n也可以说是p的步长,也就是说执行p+1时,p要跨过n个整型数据的长度。

*(p+i) == p[i] == &p[i][0] == arr[i] == &arr[i][0] (==是等价的意思)
*(*(p + i) + j) == *(p[i] + j) == p[i][j]

在数组的存储结构里就是第i行的第j个元素

二、二级指针


指向指针的指针,称为 二级指针


任何数据都有地址,一级指针的值虽然是地址,但是这个地址做为一个数据也需要空间来存放,而二级指针就是就是来存储这个地址的。

二级指针定义和使用

  • 二级指针的简单用法
#include<stdio.h>
int main()
{
int a = 10;
int *p = &a;
int **pp = &p;
printf("&a:%p\np:%p\n*pp:%p\n**pp:%d\n", &a, p, *pp, **pp); //%p打印地址
return 0;
}

打印结果:

&a:000000B0CB1EF964
p:000000B0CB1EF964
*pp:000000B0CB1EF964
**pp:10
二级指针的一次解引用 *pp    得到所指向一级指针的地址,也就是p的值

二级指针的两次解引用 **pp 得到所指向一级指针指向的值 也就是a的值

二级指针和指针数组

二级指针可以直接访问二维数组吗?答案当然是不行的,那么如何访问二维数组呢?

通过指针数组间接访问数组

#include<stdio.h>
int main()
{
int arr[3][4] = { { 1,3,5,7 },{9,11,13,15},{17,19,21,23} };
int *p[3];
for (int i = 0; i < 3; i++)
{
p[i] = arr[i]; //指针数组指向二维数组
}
int **pp = p; //二级指针指向指针p
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
printf("%d\t", pp[i][j]);
putchar(10); //换行
}
return 0;
}

三、指针函数和函数指针

指针函数


返回值为指针的函数,本质是一个函数,而该函数的返回值是一个指针


例如 malloc() 函数,或者自定义的函数如:

int* add(int x,int y);

所谓的指针函数也没什么特别的,和普通函数相比就是指针函数返回一个指针

函数指针


指向函数的指针


  • 定义格式:类型 (*指针名) (参数)
int (*p)(int x, int y);
  • 函数指针的调用
#include<stdio.h>
int add(int x, int y)
{
return x + y;
}
int main()
{
int (*p)(int x, int y); //定义函数指针
p = add; //函数指针p指向函数add
printf("第一种方法:%d\n第二种方法:%d", (*p)(1, 2), p(2, 3));//函数指针的两种调用方法
return 0;
}

四、和指针有关的传参

根据传参类型分类

  • 普通变量 int a;
//函数原型
void fun(int x);
//函数调用
fun(a);
  • 地址 int a;
//函数原型
void fun(int* p);
//函数调用
fun(&a);
  • 数组

一维数组:int arr[3];

//函数原型
void fun(int* p);
void fun(int arr[]); //推荐这种,让人一看就知道你要传的是数组
//函数调用
fun(arr);

二维数组:int arr[3] [3];

//函数原型
void fun(int(*p)[3]);
void fun(int[][3]); //行省略,列不能省略
//函数调用
fun(arr);

指针数组:int*p[3];

//函数原型
void fun(int* p[3]);
void fun(int** p);
//函数调用
fun(p);

函数名:

#include<stdio.h>
void add()
{
printf("调用add()函数");
}
void fun(void(*p)())
{
p(); //通过函数地址调用add函数
}
int main()
{
fun(add);//函数地址作为实参
return 0;
}

可以思考下为什么要把一个函数的地址作为参数传递给另一个函数,要知道在C语言中,一个函数内部是可以直接调用其他函数的,既然可以直接调用,为什么还要用这么麻烦的办法去把函数当做参数来传递呢。