博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
12.C语言提高(二)
阅读量:6905 次
发布时间:2019-06-27

本文共 9189 字,大约阅读时间需要 30 分钟。

1.二维数组的本质

二维数组的本质是一个数组指针,放宽来说多维数组的本质也是一个数组指针

int arr[i][j]

(arr+i)代表第i行的地址 二级指针

*(arr+i)代表第i行首元素的地址 一级指针 ,第i行地址和第i行首元素地址虽然是相同的
但是指针的表示形式不同
*(arr+i)+j == arr[i][j] 代表第i行第j列元素的地址
((arr+i)+j) 代表第i行第j列元素的值

img_a18437cb1d3c5c522f8e12310c53e09b.png
二维数组与指针.png
#define _CRT_SECURE_NO_WARNINGS#include 
#include
#include
void main() { int arr[3][5]; int i = 0; int j = 0; int temp = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 5; j++) { arr[i][j] = temp++; } } for (i = 0; i < 3; i++) { for (j = 0; j < 5; j++) { printf("%d ", arr[i][j]); } } printf("\n"); //观察这两行打印,看地址有什么特点,arr+1相对于arr移动了 //20,这刚好等于二维数组一行的字节数(5*4(一个int4个字节)) printf("arr %d,arr+1 %d\n",arr,arr+1); //&arr+1相对于&arr移动了60个字节,刚好等于一个二维数组 //的总字节数(5*4*3) printf("&arr %d,&arr+1 %d\n",&arr,&arr+1); //定义一个指向数组的指针变量,指向以int[5]为元素的二维数组 int(*p)[5]; //指向arr p = arr; //打印,你会发现p[i][j]和arr[i][j]打印结果是一样的,其实二维数组的本质 //就是一个数组指针,arr和p是等价的 for (i = 0; i < 3; i++) { for (j = 0; j < 5; j++) { printf("%d ", p[i][j]); } } //(arr+i)代表第i行的地址 二级指针 //*(arr+i)代表第i行首元素的地址 一级指针 ,第i行地址和第i行首元素地址虽然是相同的 //但是指针的表示形式不同 //*(arr+i)+j == arr[i][j] 代表第i行第j列元素的地址 //*(*(arr+i)+j) 代表第i行第j列元素的值 system("pause");}
2.数组做函数参数的退化问题

1.C语言中只会以机械式的值拷贝方式传递参数(实参把值传递给形参)。

下边两个方法参数不同,但是打印结果是相同的,sizeof(数组)得到的值都是4,为什么?一个char 一个int,这是因为一维数组做函数参数会退化为一个一维指针,所以结果是一样的,解释一下C语言中只会以机械式的值拷贝方式传递参数这句话,当一个数组作为地址传递到一个方法中,实参向形参传递值的时候,并没有在内存中重新创建一个数组,将数组元素一个一个拷贝进去,而是将实参数组的地址传递给了形参,形参也指向了那个数组,这就是值传递,地址值的传递

原因:c语言的高效性体现在这里,这样做更高效

int fun(char a[20],size_t b){    printf("%d  %d",b,sizeof(a));}int fun(int a[20],size_t b){    printf("%d  %d",b,sizeof(a));}

2.二维数组做参数

//一维数组做参数退化过程void fun(int a[5])   ---> void fun(inta[]) ---> void fun(int *a)//二维数组做参数退化过程void fun(int a[3][5]) ---> void fun(int a[][5]) --->void fun(int (*a)[5])
数组做函数参数的等效性

一维数组 char a[30] 等价的指针参数 char*a

指针数组 char *a[30] 等价的指针参数 char *a
二维数组 char a[20][30] 等价的指针参数 char(
a)[30]

4.三种判断字符串到达结尾的方式

'\0' 数字0 NULL都可以作为判断字符串结尾的方式,原因在于stdlib.h中有这样的定义

img_241ea35753966a042ae2a15081b39d5e.png
NULL定义.png
#define _CRT_SECURE_NO_WARNINGS#include 
#include
#include
void main() { char *a[5] = { "helloe", "world", "\0" }; char *b[5] = { "today", "yesterday", 0 }; char *c[5] = { "good", "bad", NULL }; for (int i = 0; a[i] != NULL;i++) { printf("%s", a[i]); } printf("\n"); for (int i = 0; b[i] != NULL; i++) { printf("%s", b[i]); } printf("\n"); for (int i = 0; c[i] != NULL; i++) { printf("%s", c[i]); } system("pause");}
5.结构体做形参和结构体指针做形参

看下边的函数,为什么通过结构体赋值失败,而通过结构体指针就成功了,我来画一下内存结构示意图,就明白了

#define _CRT_SECURE_NO_WARNINGS#include 
#include
#include
typedef struct Worker { char *name; int age; int id;} Worker;void copy(Worker from,Worker to){ to = from;}void copy2(Worker *from, Worker *to) { *to = *from;}void main() { Worker w1 = {"zhangsan",12,1}; Worker w2; Worker w3; //通过等号,会将结构体中的值拷贝到w2中 w2 = w1; printf("w2.name=%s", w2.name); //通过函数进行赋值 //copy(w1, w3); //运行直接报错。使用了未初始化的局部变量w3,但是在copy函数中 //我们已经赋值了 //printf("w3.name=%s", w3.name); //换一种方式,通过传递结构体指针,打印成功 copy2(&w1,&w3); system("pause");}

针对第一个copy函数

结构体通过等号赋值,做的是内存中值的拷贝,不是地址,这一点要和数组做形参区别开。因为是值拷贝,所以w2赋值给to之后,二者在除了值相同,是没有联系的,to的改变不会影响到w2,所以打印w2失败

img_bf76af9bd9c642ce20e8ee98ea981c51.png
结构体变量做形参.png

针对第二个copy2函数

img_1a04a1143f97913bc4a7e25818643b37.png
结构体指针做形参.png
6.结构体做参数
#define _CRT_SECURE_NO_WARNINGS#include 
#include
#include
typedef struct Worker { char *name; int age; int id;} Worker;void printWorker(Worker *worker, int num) { for (int i = 0; i < num; i++) { //printf("age:%d\n", (*(worker+i)).age); //printf("age:%d\n", (worker + i)->age); printf("age:%d\n", worker[i].age); }}void sortWorker(Worker *worker, int num) { int i, j; Worker tmp; for (i = 0; i < num; i++) { for (j = i + 1; j < num; j++) { /*if ((worker + i)->age > (worker + j)->age) { tmp = *(worker + i); *(worker + i) = *(worker + j); *(worker + j) = tmp; }*/ if (worker[i].age >worker[j].age) { tmp = worker[i]; worker[i] = worker[j]; worker[j] = tmp; } } }}void main() { int i = 0, num = 3; Worker Array[3]; for (i = 0; i < num; i++) { printf("请输入age:"); scanf("%d", &(Array[i].age)); } printf("\n排序前\n:"); printWorker(Array, num); sortWorker(Array, num); printf("\n排序后\n:"); printWorker(Array, num); system("pause");}
7.结构体二级指针做参数

注意三个createWorker方法

#define _CRT_SECURE_NO_WARNINGS#include 
#include
#include
typedef struct Worker { char *name; int age; int id;} Worker;Worker *createWorker1(int num) { Worker *tmp = (Worker*)malloc(sizeof(Worker)*num); if (tmp == NULL) { return NULL; } return tmp;}int createWorker2(Worker **worker, int num) { Worker *tmp = NULL; tmp = (Worker*)malloc(sizeof(Worker)*num); if (tmp == NULL) { return -1; } //这样做的效果是让二级指针Worker **worker指向了tmp,并没有改变 //Worker *pWorker这个指针的指向,可以分析一下,但函数执行时,定义了一个 //二级指针worker指向了Worker *pWorker,worker = &tmp这样操作改变的只是 //临时变量worker,对pWorker没有影响,所以后边操作pWorker导致崩溃 worker = &tmp; return 0;}int createWorker3(Worker **worker, int num) { Worker *tmp = NULL; tmp = (Worker*)malloc(sizeof(Worker)*num); if (tmp == NULL) { return -1; } *worker = tmp; return 0;}void printWorker(Worker *worker, int num) { for (int i = 0; i < num; i++) { //printf("age:%d\n", (*(worker+i)).age); //printf("age:%d\n", (worker + i)->age); printf("age:%d\n", worker[i].age); }}void sortWorker(Worker *worker, int num) { int i, j; Worker tmp; for (i = 0; i < num; i++) { for (j = i + 1; j < num; j++) { /*if ((worker + i)->age > (worker + j)->age) { tmp = *(worker + i); *(worker + i) = *(worker + j); *(worker + j) = tmp; }*/ if (worker[i].age >worker[j].age) { tmp = worker[i]; worker[i] = worker[j]; worker[j] = tmp; } } }}void main() { int i = 0, num = 3; Worker *pWorker = NULL; //在堆内存中申请空间返回内存地址 //pWorker = createWorker1(num); //createWorker2(&pWorker, num); createWorker3(&pWorker, num); for (i = 0; i < num; i++) { printf("请输入age:"); scanf("%d", &(pWorker[i].age)); } printf("\n排序前\n:"); printWorker(pWorker, num); sortWorker(pWorker, num); printf("\n排序后\n:"); printWorker(pWorker, num); system("pause");}
8.练习,将前两个内存中的元素拷贝到一个新的二级指针指向的空间,并排序
#define _CRT_SECURE_NO_WARNINGS#include 
#include
#include
/*把第一种内存模型第二种内存模型结果copy到第三种内存模型中,并排序打印*/int sort(char **myp1, int num1, char(*myp2)[30], int num2, char ***myp3, int *num3) { char **p3 = NULL; int i = 0, j = 0, k = 0; int tmplen = 0; char *tmpP = NULL; //p3指向的内存空间用于接收指向两个数组的值,所以他的大小应该是前两个空间的和 p3 = (char **)malloc((num1 + num2) * sizeof(char*)); if (p3 == NULL) { return -1; } //将第一个数组的值拷贝到申请的空间中 for (i = 0; i < num1; i++) { //得到myp1指向的第i个元素的长度(+1是结束符空间) tmplen = strlen(myp1[i]) + 1; //开辟等长的空间 p3[i] = (char *)malloc(tmplen * sizeof(char)); //拷贝到开辟的空间中 strcpy(p3[i], myp1[i]); } printf("i = %d\n", i); //同样道理拷贝myp2 for (j = 0; j < num2; j++,i++){ tmplen = strlen(myp2[j]) + 1; p3[i] = (char *)malloc(tmplen * sizeof(char)); if (p3[i] == NULL) { return -3; } strcpy(p3[i], myp2[j]); } //排序 tmplen = num1 + num2; //*num3 = num1+num2; for (i = 0; i < tmplen; i++) { for (j = i + 1; j < tmplen; j++) { if (strcmp(p3[i], p3[j]) > 0) { tmpP = p3[i]; p3[i] = p3[j]; p3[j] = tmpP; } } } //间接赋值 *num3 = tmplen; *myp3 = p3; return 0;}void sortFree1(char **p, int len) { int i = 0; if (p == NULL) { return; } for (i = 0; i < len; i++) { free(p[i]); } free(p);}/* 把二级指针指向的二维内存释放掉,同时间接修改了实参的值*/void sortFree2(char ***myp,int len) { int i = 0; char **p = NULL; if (myp == NULL) { return; } //还原成二级指针 p = *myp; if (p == NULL) { return; } for (i = 0; i < len; i++) { free(p[i]); } //间接赋值是指针存在的最大意义 *myp = NULL;}void main() { int ret = 0; char *p1[] = {"aaaaaaa","cccccc","bbbbbb"}; char buf2[10][30] = {"11111","333333","222222"}; char **p3 = NULL; int len1, len2, len3=0, i = 0; len2 = 3; len1 = sizeof(p1) / sizeof(*p1); printf("len1=%d", len1); ret = sort(p1, len1, buf2, len2, &p3, &len3); if (ret != 0) { printf("func sort err:%d \n",ret); return ret; } for (i = 0; i < len3; i++) { printf("%s\n", p3[i]); } system("pause");}

转载地址:http://xjmdl.baihongyu.com/

你可能感兴趣的文章
SSH连接不上的几个解决思路
查看>>
安卓IPC机制之Binder详解
查看>>
【python】python彻底卸载的方法【windows安装版卸载的示例】
查看>>
【maven + hibernate(注解) +spring +springMVC】 使用maven搭建项目
查看>>
微信浏览器关闭H5页面
查看>>
ANDROID开机动画分析
查看>>
Android 数字签名学习笔记
查看>>
以Lockbits的方式访问bitmap
查看>>
Javassist介绍
查看>>
Robot Framework 快速入门_中文版
查看>>
Java 开源博客——B3log Solo 0.6.0 正式版发布了!
查看>>
反射IsGenericType
查看>>
django 其他地址访问不了问题
查看>>
hibernate 需要的jar包
查看>>
Ubuntu 14.04 Remmina远程桌面连接Windows计算机
查看>>
iphone 判断某一目录是否包含文件夹
查看>>
[转载] iphone 创建iPhone锁定划动条的方法
查看>>
C# winfrom下绘制圆角窗体
查看>>
浏览器新开页面或刷新页面标志
查看>>
第12课:HTML+CSS的基础用法
查看>>