文章目录

  • 1 二维指针(指向指针的指针)
  • 2 二维数组
  • 3 二维数组的类型
    • 3.2 如何动态申请二维数组
  • 4 总结

1 二维指针(指向指针的指针)

  • 指针的本质是变量
  • 指针的指针是保存指针变量的地址。如下面的代码:

为什么需要指向指针的存在?还记得之前学习的过程中说的函数传值调用和传址调用么?当要在函数内部修改传进来参数变量的时候,需要传址调用。

同理,如果传进来的本来就是一个指针,想要修改该指针,那么就需要传指向该指针的指针了。道理是一样的。看下面的代码就明白了:

  • 代码34-1.c:函数reset为重新为某一段内存分配一段内存空间(可大可小)
#include <stdio.h>
#include <malloc.h>/* 想要修改p指向的内容,且p的地址也是会变的,就必须使用传址调用。传p的地址。就是双指针 */
int reset(char** p, int old_size, int new_size){int ret = 1;int i = 0;int len = 0;char* pp = *p;char* pt = NULL;char* tmp = NULL;if((NULL!=p) && (new_size>0)){pt = (char*)malloc(new_size);tmp = pt;len = (old_size < new_size) ? old_size : new_size;for(i=0; i<len; i++){*tmp = *pp;tmp++;pp++; }free(*p);*p=pt;}else{ret = 0;}return ret;
}int main(){char* p = (char*)malloc(5);printf("p = %p\n",p);if(reset(&p, 5, 8)){printf("reset p = %p\n",p);}else{printf("no reset!\n");}free(p);return 0;
}
  • 编译运行结果为:

p = 0x8833008
reset p = 0x8833018

分析:

上述代码中reset函数是重新为一块内存分配另一个内存空间。我们知道要分配另一块空间的话,地址肯定是会变的,那么想要最终将原来的地址p改变,就需要进行传址调用。因为p本来就是指针,所以需要传指针的指针进入reset函数。在reset函数中,进行重新分配内存空间并将原有的内存空间中的值拷贝到新的内存地址处。具体自己好好看一下reset函数就可以理解。

由运行可以看出:

  • 地址p确实改变了,说明传址调用起了作用
  • 注意理解指向指针的指针的意义与用法。需要多琢磨。

2 二维数组

在C语言中,没有二维数组的概念。它只是另一种形式的一维数组。

  • 二维数组在内存中是以一维数组的形式排布
  • 二维数组的第一维是一维数组(注意,一维数组相当于一个常量指针,也就说二维数组的第一维是存的指针)
  • 二维数组的第二维是具体存的数值
  • 既然一维数组的数组名可以看成是常量指针,那么二维数组的数组名也同样可以看成是常量指针
  • 结合上述四条看看下图中的二维数组在内存中的样式:


结合下面的代码来认识认识二维数组:

  • 代码:34-2.c :
#include <stdio.h>void print_array(int a[], int size){printf("print_array:sizeof(array) = %d\n",sizeof(a));int i = 0;for(i=0; i<size; i++){printf("%d ",a[i]);}printf("\n");
}
int main(){int a[3][3] = {{0,1,2},{3,4,5},{6,7,8}}; int* p = &a[0][0];int i=0,j=0;for(i=0; i<3; i++){for(j=0; j<3; j++){printf("%d, ", a[i][j]);}printf("\n");}printf("\n");for(i=0; i<3; i++){for(j=0; j<3; j++){printf("%d, ", *(*(a+i)+j));}printf("\n");}printf("a = %p, a+1 = %p\n",a,a+1);printf("&a = %p, &a+1 = %p\n",&a,&a+1);printf("p = %p, p+1 = %p\n",p,p+1);printf("\n");print_array(p,9);return 0;
}
  • 编译运行结果为:

分析:

  • 上述代码并不是很难,所以不做详细分析。只做以下几点说明:
  1. 可以像这样访问二维数组:*(*(a+i)+j) 。可以想这样理解: 其中a+i 代表是一维数组的第i个元素(即指针),*(a+i)代表找到第i个一维数组的起始地址,*(a+i)+j 表示第i个一维数组中的第j个元素的地址。最终*(*(a+i)+j) 表示取出元素。可以参考上面的二维数组的内存图。

  2. 由:a = 0xbfb71b00, a+1 = 0xbfb71b0c 知道:二维数组的名字a可以看成是一个常量指针,它的值为二维数组首元素(这个首元素相当于是一个一维数组)的地址值。a+1就直接跨过一个一维数组的长度(这里是12,三个int)。

  3. 由:&a = 0xbfb71b00, &a+1 = 0xbfb71b24 知道: &a 代表整个数组的地址(这与一维数组很相似)。&a+1 就直接跨过整个数组的大小到数组末尾

  4. 由print_array 函数的参数是一维数组知道,二维数组在内存中排布是一维数组的形式。参考上图。

3 二维数组的类型

  • 之前学过以为数组的类型如下:

int a[5] ==>>> a的类型为: int*

  • 二维数组的类型为:

int a[2][5] ==>>> a的类型为:int(*)[5]

  1. 二维数组名可以看做是指向数组的常量指针
  2. 二维数组可以看成是一维数组中存的元素类型是一个同类型的一维数组

3.2 如何动态申请二维数组

从下面的代码来学习如何动态申请二维数组(参考下面的二维数组的内存模型就可以理解下面的代码):

  • 代码:34-3.c:
#include <stdio.h>
#include <malloc.h>int** malloc2d(int row, int col){int** ret = NULL;if(row>0 && col>0){ret = (int**)malloc(row*sizeof(int*)); //相当于申请一个一维数组,存的元素是指针int* p = (int*)malloc(row*col*sizeof(int));//相当于二维数组在内存中的一维排布,指针p指向这个一维排布if(NULL!=p && NULL!=ret){int i = 0;for(i=0; i<row; i++){ret[i] = p + i*col;  // ret[i]存的是一个个指针指向的数组,每个数组长度是col,可以参考下图的二维数组内存模型}}else{free(ret);free(p);ret = NULL;}}return ret;
}void free2d(int** p){if(NULL != *p){free(*p);}free(p);
}
int main(){int** a = malloc2d(3,3);int i=0,j=0;for(i=0; i<3; i++){for(j=0; j<3; j++){printf("a[%d][%d] = %d,", i,j,a[i][j]);}printf("\n");}    free2d(a);return 0;
}
  • 编译运行代码如下:

虽然上述的数组中的各个值都是0,但是我们要知道malloc申请后的内存中的值是不确定的,并不一定是0

分析:

  • 上述代码中的核心代码已经标注,多画图分析即可
  • 可以参考下面的二维数组的内存模型图进行分析:

至此,就学会了如何动态的申请二维数组了。一位数组的动态申请比较简单,之前的文章也有学习过。

4 总结

  • C语言中只支持一维数组,所谓的二维数组在内存中依然是以一维数组的形式排布
  • C语言中的数组大小,必须在编译期就作为常数确定。(毕竟数组的大小是数组类型的一部分,类型都不确定好,如何编译)
  • 二维数组就是一个一维数组存的元素是同类型的数组而已

【C语言进阶深度学习记录】三十 二维数组与二维指针相关推荐

  1. 【C语言进阶深度学习记录】十九 #pragma使用与分析

    文章目录 1 #pragma 概念简介 1.1 #pragma message 的用法 1.2 #pragma once 的用法 1.3 #pragma pack 的用法 1.31 struct占用的 ...

  2. 【C语言进阶深度学习记录】十六 静态库与动态库的创建与使用

    上一篇文章学习了编译的过程,点击链接查看:[C语言进阶深度学习记录]十五 编译过程简介,每一个C源文件编译后将会生成目标文件,那么这些目标文件,还需要链接起来,生成可执行文件. 文章目录 1 链接的意 ...

  3. 【C语言进阶深度学习记录】十五 编译过程简介

    文章目录 1 初识编译器 2 总结 1 初识编译器 我们平时口中所说的编译器,是广泛的编译器.实际上,编译器包括了以下四个部分: 一个C代码被编译为可执行代码,包括以下几个过程: 下面就对上述的各个过 ...

  4. 【C语言进阶深度学习记录】十四 C语言中 三目运算符和逗号表达式

    文章目录 1 三目运算符 1.1 三目运算符的返回类型的代码案例分析 2 逗号表达式 2.1 逗号表达式代码案例分析 2.2 如何用一行代码实现 strlen函数 3 总结 1 三目运算符 三目运算符 ...

  5. 【C语言进阶深度学习记录】十八 条件编译的使用与分析

    文章目录 1 基本概念 1.1 代码分析 1.2 通过命令行定义宏 2 #include 的本质 2.1 解决重复包含头文件的问题 3 条件编译的应用 4 总结 1 基本概念 条件编译的行为类似于C语 ...

  6. 【C语言进阶深度学习记录】十二 C语言中的:字符和字符串

    文章目录 1 C语言中的单引号和双引号 1.1 双引号带来的BUG 2 总结 1 C语言中的单引号和双引号 C语言中的单引号用来表示字符字面量 C语言中的双引号用来表示字符串字面量,存储于全局的只读存 ...

  7. 【C语言进阶深度学习记录】十 C语言中:struct的柔性数组和union分析

    本文并不讲C语言的基础 文章目录 1 空struct的大小 2 结构体与柔性数组 2.1 柔性数组的使用方法 2.2 柔性数组使用代码案例分析 3 C语言中的union分析 3.1 使用union判断 ...

  8. 【C语言进阶深度学习记录】二十六 C语言中的字符串与字符数组的详细分析

    之前有一篇文章是学习了字符和字符串的,可以与之结合学习:[C语言进阶深度学习记录]十二 C语言中的:字符和字符串 文章目录 1 字符串的概念 1.1 字符串与字符数组 1.2 字符数组与字符串代码分析 ...

  9. 【C语言进阶深度学习记录】三十五 程序中的堆、栈以及静态存储区(数据区)

    学习交流加 个人qq: 1126137994 个人微信: liu1126137994 学习交流资源分享qq群: 962535112 在我之前学习底层的知识的时候,也写过相关的内容.可以对比的学习:[软 ...

  10. 【C语言进阶深度学习记录】三十八 C/C++语言中的函数声明与函数定义

    文章目录 1 函数的声明和定义 1.1 代码分析 2 总结 1 函数的声明和定义 声明的意义在于告诉编译器程序单元的存在.只是告诉编译器它存在但是不在声明这里定义,有可能在当前文件中的其他地方或者其他 ...

最新文章

  1. java高并发(三)并发编程的基础
  2. Vue基础之Class和Style绑定
  3. jupyter notebook介绍、安装以及使用教程
  4. 太畅销了!AirTag送货时间延长至4-5周
  5. 腾讯广告计算提速25%,腾讯云星星海SA2云服务器提供助力
  6. 正定矩阵(positive definite matrix)
  7. Pytorch完成线性回归
  8. 236.Lowest Common Ancestor of a BinaryTree
  9. snmp扫描工具linux,SugarNMSTool-SugarNMSTool(snmp工具)下载 v2.0官方版--pc6下载站
  10. pyserial串口通信之红外线测距模块
  11. 远程数据传输使用的几个软件
  12. 奥的斯维修服务器无响应,奥的斯GEN-2电梯故障现象:不定层的平层停梯,外呼无用断电或打检修会恢复还有运行至某层不开门自动去找平...
  13. 设为主页代码及添加到收藏夹代码大全
  14. PyEcharts 直角坐标系图表之箱形图
  15. 解决Win10系统桌面图标异常问题(恢复步骤)
  16. Hololens2开机无法启动无法开机问题
  17. 计算机毕业论文怎样写系统的意义,毕业论文写作的目的意义及步骤-计算机论文...
  18. ES 索引创建及查询
  19. 从键盘读入10个的整数,判断正数和负数的个数
  20. Android 对接 dropbox Chooser

热门文章

  1. Java面试题16 牛客 以下java程序代码,执行后的结果是()
  2. 第九十四期:GitHub 发布 2019 年年度报告
  3. html:(24):内联式css和嵌入式css
  4. java学习(166):socket服务端和客户端连接
  5. graphpad导出图片不居中_从GraphPad Prism一键导出满足期刊要求的图表
  6. 死锁产生的原因及条件和手写死锁
  7. 20175204 张湲祯 2018-2019-2《Java程序设计》 第一周学习总结
  8. Centos7安装部署Zabbix3.4
  9. 初学者最常问的几个问题
  10. 如何在intellj Idea中给新建的项目添加jar包?