C语言复习: 二级指针和多级指针
二级指针内存模型建立
|
void main2() { int i = 0;
//指针数组 char * p1[] = { "123", "456", "789" };//二级指针的第一种内存模型
//二维数组 char p2[3][4] = { "123", "456", "789" };//二级指针的第二种内存模型
//手工二维内存: 二级指针的第三种内存模型 char **p3 = (char **)malloc(3 * sizeof(char *)); //int array[3]; for (i = 0; i<3; i++) { p3[i] = (char *)malloc(10 * sizeof(char)); //char buf[10]
sprintf(p3[i], "%d%d%d", i, i, i); } } |

数组类型和多维数组本质
数组概念
概念
- 1)元素类型角度:数组是相同类型的变量的有序集合
- 2)内存角度:联系的一大片内存空间

数组初始化
- //数组元素的个数可以显示或隐式指定
- //分析数组初始化{0}与memset比较 →https://blog.csdn.net/ace_fei/article/details/7448368 这一个博客已经分析的很透彻了.向大神膜拜!
|
int main() { int i = 0; int a[10] = { 1,2 }; //其他初始化为0 int b[] = { 1, 2 }; int c[20] = { 0 };
for (i = 0; i<10; i++) { printf("%d ", a[i]); } memset(a, 0, sizeof(a)); getchar(); return 0; } |
数组名的技术盲点
- 1)数组首元素的地址和数组地址是两个不同的概念
- 2)数组名代表数组首元素的地址,它是个常量。
- 解释如下:变量本质是内存空间的别名,一定义数组,就分配内存,内存就固定了。所以数组名起名以后就不能被修改了。
- 3)数组首元素的地址和数组的地址值相等
- 4、怎么样得到整个一维数组的地址?
C语言规定:
int a[10];
printf("得到整个数组的地址a: %d \n", &a);
printf("数组的首元素的地址a: %d \n", a);
//虽然值是一样的,但是代表的意思是不一样的.
怎么样表达int a[10]这种数据类型? 为int[10]类型.
数组类型、数组指针类型、数组指针类型变量
数组类型
- 1数据类型分为基础、非基础,思考角度应该发生变化
- 2 C语言中的数组有自己特定的类型
- 数组的类型由元素类型和数组大小共同决定
- 例:int array[5]的类型为int[5]
- 3定义 数组类型,并用数组类型定义变量
|
int main() { typedef int i = 0; MYINT5 array; for (i = 0; i<5; i++) { array[i] = i; }
for (i = 0; i<5; i++) { printf("%d ", array[i]); }
getchar(); return 0; } |
数组指针类型
- 数组指针用于指向一个数组
int a[10]
数组名是数组首元素的起始地址,但并不是数组的起始地址
通过将取地址符&作用于数组名可以得到整个数组的起始地址
//定义数组指针 有两种
1)通过数组类型定义数组指针:
typedef
int(ArrayType)[5];
int *a;
ArrayType* pointer;
2) 声明一个数组指针类型
typedef int(*MyPointer)[5];
MyPointer myPoint;
3)直接定义:
int (*pointer)[n];
pointer 为数组指针变量名
type 为指向的数组的类型
n 为指向的数组的大小
注意这个地方是type类型(比如 int (*pointer)[10])
- 数组指针:用数组类型加*定义一个数组指针
|
int mian() { int a[5]; //声明一个数组类型 typedef //用数组类型 加*,定义一个数组指针变量 MYINT5 *array; array = &a; for (i = 0; i<5; i++) { (*array)[i] = i; } // for (i = 0; i<5; i++) { printf("\n%d %d", a[i], (*array)[i]); } } |
数组指针:定义一个数组指针类型,然后用类型定义变量
|
int b[5]; //声明一个数组指针类型 typedef //用数组指针类型,去定义一个变量 MyPointer mypoint; mypoint = &b; for (i = 0; i<5; i++) { (*mypoint)[i] = i; } // for (i = 0; i<5; i++) { printf("\n%d %d", b[i], (*mypoint)[i]); } } |
//3数组指针:直接定义一个数组指针变量
|
{ int c[5]; //直接声明一个数组指针变量 int(*pointer)[5] = &c; for (i = 0; i<5; i++) { (*pointer)[i] = i; } for (i = 0; i<5; i++) { printf("\n%d %d", c[i], (*pointer)[i]); } } |
多维数组本质技术推演
|
//int a[10]; //char myarray[3][5] PK int(*p)[5] //myarray名称到底是什么? //多维数组char a[i][j] == > *(*(a + i) + j)转换技巧分析 void main() { int a[3][5];
int c[5]; //&c + 1; int b[10]; //b代表数组首元素的地址 &b代表这个数组的地址 &b+1相当于 指针后移4*10个单位
//a代表什么什么那?a是一个数组指针 指向低维数组的指针 //a +1; → 跳5*4(int)个字节的内存 printf("a:%d, a+1:%d \n", a, a + 1); //4*5
{ int i = 0, j = 0, tmp = 0; for (i = 0; i<3; i++) { for (j = 0; j<5; j++) { a[i][j] = ++tmp; } }
printf("\n"); for (i = 0; i<3; i++) { for (j = 0; j<5; j++) { printf("%d \n", a[i][j]); } } }
//a的本质是一个数组指针,每次往后跳一维的维数 { int i = 0, j = 0; //定义了一个数组指针 变量 int(*myArrayPoint)[5]; //告诉编译给我开辟五个四字节内存 myArrayPoint = a; printf("\n"); for (i = 0; i<3; i++) { for (j = 0; j<5; j++) { //myArrayPoint[i][j] = ++tmp; printf("%d \n", myArrayPoint[i][j]); } } }
/* char cbuf[30]; // cbuf(1级指针) 代表数组首元素的地址 &cbuf(二级指针) 代表整个数组的地址 char array[10][30]; //array是二级指针 (array+i) //相当于 整个第i行的数组地址 //二级指针 &cbuf
(*(array+i))//一维数组的首地址 cbuf
(*(array+i))+j //相当于第i行第j列的地址 &array[i][j]
*((*(array+i))+j) //相当于第i行第j列的元素 <====> array[i][j] */ } |

结论:a是一个指向int myarray[5]的数组指针 a+1 向后跳5*4,跳一行。
多维数组做函数参数退化原因大剖析
|
//证明一下多维数组的线性存储 //线性打印
void printfArray411(int *array, int { int i = 0; for (i = 0; i<num; i++) { printf("%d ", array[i]); } }
void printfArray412(int(*array)[5], int { return; }
void printfArrr333(int { return; } void main() { int a[3][5]; int c[3][4][5]; int i, j = 0; int tmp = 0; for (i = 0; i<3; i++) { for (j = 0; j<5; j++) { a[i][j] = tmp++; } }
printfArray411((int *)a, 15);
system("pause"); } |
多维数组做函数参数技术推演
|
|
int fun(char a[20], size_t b){ printf("%d\t%d",b,sizeof(a)); } |
|
原因1:高效 原因2:C语言处理a[n]的时候,它没有办法知道n是几,它只知道&n[0]是多少,它的值作为参数传递进去了;虽然c语言可以做到直接int fun(char a[20]),然后函数能得到20这个数字,但是,C没有这么做。 |
|
2、二维数组参数同样存在退化的问题 |
|
二维数组可以看做是一维数组 二维数组中的每个元素是一维数组 二维数组参数中第一维的参数可以省略 void f(int a[5]) ====》void f(int a[]); ===》 void f(int* a); void g(int a[3][3])====》 void g(int a[][3]); ====》 void g(int (*a)[3]); |
|
3、等价关系 |
|
数组参数 等效的指针参数
一维数组 char a[30] 指针 char* 指针数组 char *a[30] 指针的指针 char **a 二维数组 char a[10][30] 数组的指针 char(*a)[30] |
指针数组的应用场景
指针数组的两种用法(菜单 命令行(argv*[]))
操作系统拉起应用 在框架下干活
字符数组自我结束标志
// NULL 0 '\0'
实际上NULL==0=='\0'=0
强化训练
int sort(char *p[], int count, char **p, int *ncount);
int sort(char *p[], int count, char (*p)[30], int *ncount);
int sort(char (*p)[30], int ncount, char **p, int *ncount);
//把第一种内存模型和第二种内存模型的结果copy到第三种内存模型中,并排序,打印
char ** sort(char **p1, int num1, char (*p)[30], int num2, int *num3 );
|
int getArray3_Free(char **p3, int { int i; if (p3 == NULL) { return -1; } for (i = 0; i<p3num; i++) { if (p3[i] != NULL) { free(p3[i]); } } free(p3); }
int getArray3_Free2(char ***p3, int { int i; char **tmp = NULL;
if (p3 == NULL) { return -1; } tmp = *p3;
for (i = 0; i<p3num; i++) { if (tmp[i] != NULL) { free(tmp[i]); } } free(tmp);
*p3 = NULL; //通过间接赋值,去间接的修改实参的值,成0 }
int getArray3_2(char **myp1, int { int ret = 0; int i, j; int tmpNum3 = 0;
char **tmpp3 = NULL; char *temp;
/* printf("111111111"); if (*myp3 ==NULL ) { printf("222222222"); } */ printf("33333"); if (myp1 == NULL || myp2 == NULL || num3 == NULL || myp3 == NULL) { ret = -1; return ret; } //准备内存 tmpNum3 = num1 + num2; //分配第一维 tmpp3 = (char **)malloc(tmpNum3 * sizeof(char *)); if (tmpp3 == NULL) { return NULL; }
//分配第二维 把第一种内存模型数据和第二种内存模型数据,copy到第3中内存模型中 for (i = 0; i<num1; i++) { tmpp3[i] = (char *)malloc(strlen(myp1[i]) + 1); if (tmpp3[i] == NULL) { puts("out of space"); return NULL; } strcpy(tmpp3[i], myp1[i]); } for (j = 0; j<num2; j++, i++) { tmpp3[i] = (char *)malloc(strlen(myp2[j]) + 1); //note modify if (tmpp3[i] == NULL) { puts("out of space"); return NULL; } strcpy(tmpp3[i], myp2[j]); }
//排序 for (i = 0; i<tmpNum3; i++) { for (j = i + 1; j<tmpNum3; j++) { if (strcmp(tmpp3[i], tmpp3[j])>0) { temp = tmpp3[i]; tmpp3[i] = tmpp3[j]; tmpp3[j] = temp; } } }
//通过间接赋值,把结果甩给实参 *num3 = tmpNum3; *myp3 = tmpp3; //*0 = 100; return ret; }
char **getArray3(char **myp1, int { int i, j; int tmpNum3 = 0;
char **tmpp3 = NULL; char *temp;
if (myp1 == NULL || myp2 == NULL || num3 == NULL) { return NULL; } //准备内存 tmpNum3 = num1 + num2; //分配第一维 tmpp3 = (char **)malloc(tmpNum3 * sizeof(char *)); if (tmpp3 == NULL) { return NULL; }
//分配第二维 把第一种内存模型数据和第二种内存模型数据,copy到第3中内存模型中 for (i = 0; i<num1; i++) { tmpp3[i] = (char *)malloc(strlen(myp1[i]) + 1); if (tmpp3[i] == NULL) { puts("out of space"); return NULL; } strcpy(tmpp3[i], myp1[i]); } for (j = 0; j<num2; j++, i++) { tmpp3[i] = (char *)malloc(strlen(myp2[j]) + 1); //note if (tmpp3[i] == NULL) { puts("out of space"); return NULL; } strcpy(tmpp3[i], myp2[j]); }
//排序 for (i = 0; i<tmpNum3; i++) { for (j = i + 1; j<tmpNum3; j++) { if (strcmp(tmpp3[i], tmpp3[j])>0) { temp = tmpp3[i]; tmpp3[i] = tmpp3[j]; tmpp3[j] = temp; } } }
*num3 = tmpNum3; return tmpp3; }
void main() { int num3 = 0, i = 0; int ret = 0; char *p1[] = { "222222", "1111111", "33333333" }; char p2[4][30] = { "bbbbb", "aaaaa", "zzzzzz", "ccccccc" }; char **p3 = NULL; char ***myerrp3 = NULL;
//p3 = getArray3(p1, 3, p2, 4, &num3); //ret = getArray3_2(p1,3, p2, 4, &p3, &num3); ret = getArray3_2(p1, 3, p2, 4, 0, &num3); //错误做法 if (ret != 0) { return; } for (i = 0; i<num3; i++) { printf("%s \n", p3[i]); }
//getArray3_Free(p3, num3); // p3=NULL; getArray3_Free2(&p3, num3);
printf("p3:%d \n", p3);
system("pause"); } |
C语言复习: 二级指针和多级指针的更多相关文章
- [转] C语言多维数组与多级指针
http://c.biancheng.net/cpp/html/477.html 多维数组与多级指针也是初学者感觉迷糊的一个地方.超过二维的数组和超过二级的指针其实并不多用.如果能弄明白二维数组与二级 ...
- 嵌入式C语言4.4 C语言内存空间的使用-多级指针
多级指针 int **p; 存访地址的地址空间
- [C++]指针与多级指针(图解)
声明:如需引用或者摘抄本博文源码或者其文章的,请在显著处注明,来源于本博文/作者,以示尊重劳动成果,助力开源精神.也欢迎大家一起探讨,交流,以共同进步- 0.0 演示: /* @author:John ...
- C语言复习:字符串和一级指针
字符串基本操作 字符数组初始化方法 int main() { //1 {}号法 初始化列表 //数组初始化有2种方法 默认元素个数.指定元素个数 char buf1[] = { ...
- C语言指针系列 - 一级指针.一维数组,二级指针,二维数组,指针数组,数组指针,函数指针,指针函数
1. 数组名 C语言中的数组名是一个特殊的存在, 从本质上来讲, 数组名是一个地址, 我们可以打印一个指针的值,和打印一个数组的值来观察出这个本质: int nArray[10] ={ 0 }; in ...
- C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)
前提:一维数组和一维指针为什么可以替换使用? ] = { , , }; int *p = a; ; i < ; i++) printf("%d ", *(p + i)); 上 ...
- Android For JNI(五)——C语言多级指针,结构体,联合体,枚举,自定义类型
Android For JNI(五)--C语言多级指针,结构体,联合体,枚举,自定义类型 我们的C已经渐渐的步入正轨了,基础过去之后,就是我们的NDK和JNI实战了 一.多级指针 指针的概念我们在前面 ...
- Android JNI编程(四)——C语言多级指针、数组取值、从控制台输入数组
版权声明:本文出自阿钟的博客,转载请注明出处:http://blog.csdn.net/a_zhon/. 目录(?)[+] 一:前面我们介绍了一级指针的相关概念和用发,今天我们就来说一说多级指针. 1 ...
- C语言 详解多级指针与指针类型的关系
//V推论①:指针变量的步长只与‘指针变量的值’的类型有关(指针的值的类型 == 指针指向数据的类型) //指针类型跟指针的值有关,指针是占据4个字节大小的内存空间,但是指针的类型却是各不相同的 // ...
随机推荐
- Jmeter(二十八)Jmeter-Question之“HTTPS请求”
前面在Jmeter-Question中有提到若干问题,有时间呢,我也会进行继续编写随笔,梳理自己的知识,本篇呢,便来记Jmeter发送https请求的过程 内容大致与http://blog.csdn. ...
- [UE4]蓝图调试
1.蓝图下断点:选择蓝图节点按F9下断点:再按一下F9就会去掉断点. 2.游戏运行到断点会自动这暂停,鼠标移到某个变量上面,会显示该变量的值. 3.按F10执行下一步. 4.蓝图调试没有跳出函数的功能 ...
- Linux双网卡绑定
Linux双网卡绑定 作者:Eric 微信:loveoracle11g eth0和eth1绑定为bond0 [root@rac-node1 ~]# cat /etc/sysconfig/network ...
- 1449 - The user specified as a definer ('test'@'%') does not exist
最近在做一个项目,由于服务器切换,所以需要将原有服务器的mysql数据表以及存储过程导入到另一个服务器的mysql数据库中.导入完成之后以为一切是那么的简单,却没有想到总还是出现了一些莫名其妙的问题. ...
- 网络基础和python
·五层协议 物理层,数据链路层,网络层,传输层,应用层 ·用户上网流程 1.本机获取 2.打开浏览器,,输入网址. 3.dns协议(基于udp协议) 4.HTTP部分的内容 5 TCP协议 6 IP协 ...
- python利用socket写一个文件上传
1.先将一张图片拖入‘文件上传’的目录下,利用socket把这张图片写到叫‘yuan’的文件中 2.代码: #模拟服务端 import subprocess import os import sock ...
- 背景图片的移动----background-attach-----background-repeat
background-repeat:默认是平铺的,也即是还有空间的话将一张接着一张显示 设置为 no-repeat 就最多显示一张 background-attachment:设置是否固定图片,在有 ...
- [Unity工具]嵌套Prefab
在父Prefab中嵌套子Prefab,那么如果对这个嵌套Prefab进行修改,改变将不会应用到子Prefab中:同理,对子Prefab的修改,也不会应用到这个嵌套Prefab中.因此,就会出现一些问题 ...
- 改变jupyter notebook默认初始文件路径 - 关于快捷方式
jupyter notebook home path changing - %USERFROFILE% and Configure file 如何改变jupyter notebook默认初始文件路径, ...
- 25.安装配置phantomjs
1.官网下载windows版本:http://phantomjs.org/download.html2.下载完解压,将PhantomJS可执行文件配置到环境变量里.比如: 将 E:\Soft\soft ...