第 七篇,二维指针和二维数组知识讲解。

一、复杂指针定义?  --  二级指针。


1、什么是复杂指针?


无论是简单的指针,还是复杂指针。都是用于存放内存空间上的地址值,例如:0x100
简单指针:该地址指向一些基本数据类型     char*/int*/double*     --> 字符指针/整型指针/浮点型指针
复杂指针:该地址指向一些非基本数据类型  

指针/数组/函数/结构体  

--> 二级指针/数组指针/函数指针/结构体指针

2、二级指针?


1)什么是二级指针?
二级指针就是指向指针变量的指针。

2)如何定义二级指针?

int a = 100;
int *pa = &a;
int **px = &pa

1>. 先写一个*                                                                           *
2>. 在*后面写一个变量名                                                         *px
3>. 确认一下你想指向的东西长什么样子的?                          int *pa
4>. 将第3步的结果的变量名去掉                                              int *
5>. 将第4步的结果写在第2步结果的前面                                 int **px

结果: int **px
数据类型: int **
变量名: px

3)解引用。

#include <stdio.h>

int main(int argc,char *argv[])
{
    int a = 100;
    int *pa = &a;
    int **px = &pa;
    
    //printf("a = %d\n",a);   // a的值
    //printf("pa = %p\n",pa); // a的地址
    //printf("px = %p\n",px); // pa的地址
    
    printf("*px = %p\n",*px);    //*px = *(&pa) = pa = &a
    printf("**px = %d\n",**px);  //  **px = **(&pa) = *pa = *(&a) = a = 100
    
    return 0;
}

4)一般二级指针常见场景就是传参问题。

void func(int **px)  //那么我就使用一个二级指针来接住这个变量的地址
{

}

int main()
{
    int a;
    int *pa = &a;
    func(&pa);   //你传递了一个int*变量的地址给我
}

二、复杂指针定义?  --   数组指针


1、什么是数组指针?
一个指针变量,这个指针变量是存放着整个数组的地址。

2、如何定义数组指针?
定义整型指针如下:

int a;
int *pa = &a;

分析:int *pa
数据类型: int *   --> 这种类型的变量是专门用于存放整型变量的地址。
变量名:pa


定义数组指针如下:

int A[3];
int(*p)[3] = &A;

1>. 先写一个*                                                                              *
2>. 在*后面写一个变量名,使用圆括号将结果括住。                (*p)
3>. 确认指针指向的内容是什么?                                               int A[3]
4>. 将第3步的结果的变量名去掉                                                 int [3]
5>. 将第2步的结果写在第4步结果的数据类型与元素个数之间    int(*p)[3]

结果:int(*p)[3]
数据类型:  int(*)[3]   --> 这种类型的变量是专门用于存放具有3个int类型数据的数组的地址
变量名: p

3、能不能这样写?

int A[3];
int (*p)[3] = &A;    --> 正确   左边类型  int (*)[3]    右边类型 int (*)[3]
int (*p)[3] = A;     --> 错误   左边类型  int (*)[3]    右边类型 int *

int *px = A;         --> 正确   左边类型  int *         右边类型 int *
int *px = &A;        --> 错误   左边类型  int *         右边类型 int (*)[3]

4、解引用。
int a = 100;
int *pa = &a;
请问*pa得到什么?  -> 100

  分析: *pa = *(&a) = a = 100
  结论: 解引用一个整型指针,就可以得到这个指针指向的整型变量的值

int A[3] = {1,2,3};
int (*pA)[3] = &A;
请问*pA得到什么?

分析: *pA = *(&A) = A = &A[0]
结论: 解引用一个数组指针,就可以得到这个数组首元素的地址

5、已知以下代码,请问以下几个式子是什么意思?
int A[3] = {1,2,3};
int (*p)[3] = &A;

p[0]     --> 数组首元素的地址
p[1]     --> 段错误
(*p)[0]  --> 数组首元素的值
(*p)[1]  --> 数组第2个元素的值


 练习: 如果有以下的说明:
 int B[10];
 int (*p)[10] = &B;
 int *px = B;

则以下能够正确引用数组的元素的是: CEF 
A. *p+1    B.  *(p[1])   C. (px+3)[2]   D. (*px)[3]   E. (*p)[0]    F. *(px+1)


三、复杂指针定义?  --  函数指针


1、什么是函数指针?


在程序运行时,函数也是在内存空间上申请空间的,所以函数也是有地址的,存放这些函数的地址的指针变量就称之为函数指针变量。

2、如何定义函数指针?


=======================已知函数,求函数指针==================
整型指针: 
int a;         //定义好目标
int *pa = &a;  //定义指针变量

  *pa求出a的空间: *pa = *(&a) = a

函数指针:
void func(int a,int b);       //定义好目标
void (*pf)(int,int) = &func;  //定义指针变量

等价于
void (*pf)(int,int) = func;   //在C语言中,函数的名字就是函数的地址。
                  //所以以上的两种写法都是正确的。

  *pf = *(&func) = func  等价于  &func   
  结论:解引用函数的指针也是得到函数的地址

分析结果:void (*pf)(int,int)
数据类型:void (*)(int,int)    --> 这种类型的变量是专门用于存放返回值为void,有两个int类型的变量形式参数的函数的地址
变量名:pf

========================已知函数指针,求函数====================
已知:void (*p)(int,char)
结果:void func(int a,char b);

已知:void *(*start_routine) (void *)
结果:void *func(void *a)

   例题: 求出两个数字的最大值,使用函数指针来完成。

#include <stdio.h>

//1. 定义好目标
int get_max(int x,int y)
{
    return x > y ? x : y;
}

int main(int argc,char *argv[])
{
    //2. 定义指针变量
    int (*p)(int,int) = get_max;
    
    //3. 只需要把函数指针变量名当作是函数名来使用即可。
    //int ret = p(10,20);
    int ret = (*p)(10,20);
    
    printf("ret = %d\n",ret);
    return 0;
}

四、数组作为函数参数/返回值时候,情况是怎么样的?
1、回顾学习过的传参情况。

例子1: 传递一个int类型的数据
void func(int x)  //x = a

int a;
func(a);   --> 传递了整个a过去

例子2: 传递普通指针地址
void fun(int *x)  //x = &a

int a;
int *p = &a;
func(p);   --> 传递了整个p过去

例子3: 传递函数
void func( void(*p)(int) )  // p = my_func

void my_func(int a);
func(my_func)   --> 传递了函数的地址过去


2、如果传递了一个数组过去,会怎么样呢?

void func(int x[])   //x = A = &A[0]
void func(int x[3])  //x = A = &A[0]
void func(int *x)    //x = A = &A[0]
{

}

int main()
{
    int A[3];
    func(A);   //A是数组名
           //这里没有sizeof()
           //所以A在这里,是代表数组首元素的地址
    
    其实以上的这行代码,等价于
    func(&A[0]);

}

   练习:   指针习题(C语言).doc  9  10

五、数组作为返回值的情况,会怎么样呢?
#include <stdio.h>

char* func()
{
    char A[10] = {'a','b','c','d'};  //栈区
    printf("A = %p\n",A);  //打印首元素的地址             0x7ffe9cbee030
    printf("A = %s\n",A);  //以字符串形式打印数组的内容   abcd
    return A;
    //数组的空间全部都会释放
}

int main(int argc,char *argv[])
{
    char *p = func();
    printf("p = %p\n",p);  //打印首元素的地址              null  
    printf("p = %s\n",p);  //以字符串形式打印数组的内容    null  
    return 0;
}

结果:
gec@ubuntu:/mnt/hgfs/GZ2180/01 C语言/07/code$ ./return_array
A = 0x7ffe9cbee030
A = abcd
p = (nil)
p = (null)


如果真的有必须返回数组,那么怎么办?
1)将数组设置为全局变量。
#include <stdio.h>

char A[10] = {'a','b','c','d'};  //数据段

char* func()
{
    //char A[10] = {'a','b','c','d'};  //栈区
    printf("A = %p\n",A);  //打印首元素的地址             0x7ffe9cbee030
    printf("A = %s\n",A);  //以字符串形式打印数组的内容   abcd
    return A;
    //数组的空间全部都会释放
}

int main(int argc,char *argv[])
{
    char *p = func();
    printf("p = %p\n",p);  //打印首元素的地址              null  
    printf("p = %s\n",p);  //以字符串形式打印数组的内容    null  
    return 0;
}

2)在数组前面添加一个static,让这个局部变量变成静态变量。
静态变量也是在数据段申请空间,不会在栈区申请。
#include <stdio.h>

char* func()
{
    static char A[10] = {'a','b','c','d'};  //数据段
    printf("A = %p\n",A);  //打印首元素的地址             0x7ffe9cbee030
    printf("A = %s\n",A);  //以字符串形式打印数组的内容   abcd
    return A;
    //数组的空间全部都会释放
}

int main(int argc,char *argv[])
{
    char *p = func();
    printf("p = %p\n",p);  //打印首元素的地址              null  
    printf("p = %s\n",p);  //以字符串形式打印数组的内容    null  
    return 0;
}

六、二维数组。
1、什么是二维数组?
二维数组在内存中呈线性排列,不存在行与列的关系。
二维数组其实也是一个一维数组,只是这个一维数组每一个成员都是一个一维数组。

例子:
整型数组   int A[3]   --> 每一个成员都是int类型的数据
二维数组   数组 A[3]  --> 每一个成员都是数组来的

2、定义数组时,必须交代好两件事情。
1)数组元素的个数
2)数组中每一个元素的数据类型。   --> 基本数据类型/非基本数据类型

定义数组的步骤:
例子1: 定义具有5个int类型数据的数组。

1>. 确定数组名                                                   A
2>. 确定数组中元素的个数,使用[]括号括住它,然后跟在数组名后面    A[5]
3>. 确定数组中每一个成员是什么                                   int x
4>. 将第3步的结果的变量名去掉                                    int
5>. 将第4步的结果写在第2步结果的前面就可以                       int A[5]   --> 最终结果


例子2:定义一个二维数组,有2个成员,每一个成员都是具有3个int类型的数据的数组。

1>. 确实数组名                                                  B
2>. 确定数组中元素的个数,使用[]括号括住他,然后跟在数组名后面   B[2]
3>. 确定数组中每一个成员是什么                                 int x[3]
4>. 将第3步的结果的变量名去掉                                  int [3]
5>. 将第2步的结果放在第4步结果的中间                           int B[2][3]  -> 最终结果

分析结果:
int B[2][3]
代表这个数组有2个成员,每一个成员都具有3个int类型数据的数组。


3、二维数组的赋值?
int B[2][3] = {{1,2,3},{4,5,6}};   --> 整体赋值

B[0][0] = 1;           --> 单个赋值
B[0][1] = 2;
....
B[1][2] = 6;

int B[][3] = {{1,2,3},{4,5,6}};   --> 2可以省略,因为有两个初始化列表,决定了数组有2个成员。
//int B[2][] = {{1,2,3},{4,5,6}};   --> 3不可以省略,因为如果省略了,编译器无法知道数组每一个成员多大。

4、研究二维数组的名字
具体分析: 二维数组名字的理解.jpg

5、解引用。
int B[2][3] = {{1,2,3},{4,5,6}};

1)解引用二维数组的名字,可以得到什么?
*B = *(&B[0]) = B[0] = &B[0][0]   --> 可以得到二维数组首元素的首元素的地址

2)解引用二维数组首元素的名字,可以得到什么?
*B[0] = *(&B[0][0]) = B[0][0]     --> 可以得到二维数组首元素的首元素的值

   练习: 指针习题(C语言).doc  11 12 13

七、指针数组。


1、什么是指针数组?  什么是数组指针?
数组指针是一个指针来的,这个指针是指向一个数组的。
指针数组是一个数组来的,这个数组中每一个成员都是一个指针。

2、如何定义指针数组?
例子1: 定义具有5个int类型数据的数组。

1>. 确定数组名                                                   A
2>. 确定数组中元素的个数,使用[]括号括住它,然后跟在数组名后面    A[5]
3>. 确定数组中每一个成员是什么                                   int x
4>. 将第3步的结果的变量名去掉                                    int
5>. 将第4步的结果写在第2步结果的前面就可以                       int A[5]   --> 最终结果

例子2: 定义具有5个int*类型数据的数组。

1>. 确定数组名                                                   B
2>. 确定数组中元素的个数,使用[]括号括住它,然后跟在数组名后面    B[5]
3>. 确定数组中每一个成员是什么                                   int *x
4>. 将第3步的结果的变量名去掉                                    int *
5>. 将第4步的结果写在第2步结果的前面就可以                       int *B[5]

3、以下几句话是什么意思?

   int x[5];  -> 整型数组   在内存中连续申请20个字节,然后使用变量x间接访问这片空间


   int *p[5]; -> 指针数组   在内存中连续申请40个字节,然后使用变量p间接访问这片空间


   int (*p)[5];  -> 数组指针  在内存中连续申请8个字节,然后使用变量p间接访问这片空间

  • 2
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肖爱Kun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值