由于简书不能很好支持 LaTeX,需要原文档的可以加我QQ 596928539.
或者只下载PDF版 链接:https://pan.baidu.com/s/1gqlTht0VaOaa7p9b9seHNw 密码:y7pg
遍历数组
//
// Created by binny on 2019/9/20.
//
#include <stdio.h>
int main() {
int array1[3] = {1, 2, 3};
int s1 = sizeof(array1) / sizeof(array1[0]);
for (int l = 0; l < s1; ++l) {
printf("array1[%d]= %d\n", l, array1[l]);
}
printf("通过指针遍历一维数组\n");
int (*pInt1) = array1;
for (int m = 0; m < s1; ++m) {
printf("pInt1[%d]= %d\n", m, *pInt1++);
}
int array2[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int s2 = sizeof(array2) / sizeof(array2[0][0]);
printf("array[3][3]的大小 = %d\n\n", s2);
printf("通过下标遍历数组\n");
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
printf("array[%d][%d] = %d\n", i, j, array2[i][j]);
}
}
printf("通过指针遍历二维数组\n");
int (*pInt2)[3] = array2;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
printf("pInt[%d][%d] = %d\n", i, j, *(*(pInt2 + i) + j));
}
}
int array3[3][3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27,
28, 29};
int (*pInt3)[3][3] = array3;
int s3 = sizeof(array3) / sizeof(array3[0][0][0]);
printf("array3[3][3][3]的大小 = %d\n\n", s3);
printf("通过指针遍历三维数组\n");
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < 3; ++k) {
printf("pInt[%d][%d][%d] = %d\n", i, j, k, *(*(*(pInt3 + i) + j) + k));
}
}
}
return 0;
}
一位数组
int array1[3] = {1, 2, 3};
int (*p1) = array1;
取出一维数组的元素,解指针:*p;
遍历:*p++
int main() {
/*一维数组*/
int array1[3] = {1, 2, 3};
int s1 = sizeof(array1) / sizeof(array1[0]);
for (int l = 0; l < s1; ++l) {
printf("array1[%d]= %d\n", l, array1[l]);
}
printf("通过指针遍历一维数组\n");
int (*pInt1) = array1;
printf("pInt1= %d\n", *pInt1);
for (int m = 0; m < s1; ++m) {
printf("下标法 --- array1[%d]= %d\n", m, array1[m]);
printf("指针法 --- pInt1[%d]= %d\n", m, *pInt1++);
}
return 0;
}
二维数组
int array2[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int (*p2)[3] = array2;
取出二维数组的一个元素元素,解指针:*p;
遍历:*p++:一般化,*p+i,(其中,i=0,1,2,3……)
这是一个二维数组的一个元素,即一个一维数组,相当于p。
取出一维数组的元素,解指针:*(*p+i);
遍历:*(*p+i)++:一般化,*(*p+i)+j,(其中,i,j=0,1,2,3……)
int main() {
/*二维数组*/
int array2[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int (*pInt2)[3] = array2;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
printf("下标法 --- pInt[%d][%d] = %d\n", i, j, array2[i][j]);
printf("指针法 --- array2[%d][%d] = %d\n", i, j, *(*(pInt2 + i) + j));
}
}
return 0;
}
三维数组
int array3[3][3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27,
28, 29};
int (*pInt3)[3][3] = array3;
同理:
遍历:*(*(*p++)++)++:一般化*(*(*p+i)+j)+k
int main() {
/*三维数组*/
int array3[3][3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27,
28, 29};
int (*pInt3)[3][3] = array3;
int s3 = sizeof(array3) / sizeof(array3[0][0][0]);
printf("array3[3][3][3]的大小 = %d\n\n", s3);
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < 3; ++k) {
printf("下标法 --- array3[%d][%d][%d] = %d\n", i, j, k, array3[i][j][k]);
printf("指针法 --- pInt[%d][%d][%d] = %d\n", i, j, k, *(*(*(pInt3 + i) + j) + k));
}
}
}
return 0;
}
<div STYLE="page-break-after: always;"></div>
归纳
多维数组指针的定义
指针遍历多维数组
一维数组:步进,解指针。
二维数组:步进,解指针;步进,解指针。
三维数组:步进,解指针;步进,解指针;步进,解指针。
n 维数组:步进,解指针;步进,解指针;步进,解指针……步进,解指针。
以步进,解指针为单位,需要进行n次。
<font color=red>可以观察出规律:解指针和步进单位关于数组指针p呈结构对称</font>。通过此规律不难写出 n 维数组通过指针遍历的通式,以四维为例:
四维数组的指针:*(*(*(*p+i)+j)+k)+l
int main() {
/*四维数组*/
int array4[2][2][2][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17};
int (*pInt4)[2][2][2] = array4;
int s3 = sizeof(array4) / sizeof(array4[0][0][0][0]);
printf("大小 = %d\n\n", s3);
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
for (int k = 0; k < 2; ++k) {
for (int l = 0; l < 2; ++l) {
printf("下标法 --- array4[%d][%d][%d][%d] = %d\n", i, j, k,l, (array4[i][j][k][l]));
printf("指针法 --- pInt4[%d][%d][%d][%d] = %d\n", i, j, k,l, *(*(*(*(pInt4 + i) + j) + k) + l));
}
}
}
}
return 0;
}
计算数组的长度
使用关键字 sizeof
因为数组是同一类型元素的集合:所以知道数组所占的字节数,除以一个元素的字节数,就可以的鳌数组中元素的个数;利用首元素即可,或者类型。
数组长度 = sizeof(数组名)/sizeof(数组首元素)
数组长度 = sizeof(数组名)/sizeof(数组类型)
性参数组的长度,需要另一个参数来传递:void test(int array[],int size)
,必须显示传递长度。
为什么不能?为什么能?
1、从一个字符串常量里查找出某一个字符
一级指针为什么不能?
是真的不能实现吗?先不回答这个问题。先说一下为什么不能,再给出一个一级指针的实现方案。
/**
* 一级指针
* @param src
* @param ch
* @param ret
* @return
*/
int find2(const char *src, char ch, char *ret) {
char *index = (char *) src;
while (*index) {
if (*index == ch) {
ret = index;//①这个地方导致不行
return 1;
}
index++;
}
return 0;
}
绿色箭头代表调用的时候ret的指向,红色箭头代表函数执行过程中满足条件的指向;find2执行完,返回到主函数,没有对实参的产生影响。就像是见异而迁。
src是被声明为const char *,其所指的内存对src来说是只读的。
而ret是char *,可以通过ret改变其所指内存的值。
修改后的一级指针
功能函数:对其所指向的内存数据进行写操作,前提是其所指定内存是确定的。
/**
* 一级指针
* @param sr
* @param ch
* @param ret
* @return
*/
int find2(const char *sr, char ch, char *ret) {
char *index = (char *) sr;
while (*index) {
if (*index == ch) {
*ret = *index;//这样改就可以了
return 1;
}
index++;
}
return 0;
}
简化后的find2
代码
/**
* 一级指针
* @param sr 原始字符串
* @param ch 待查找字符串
* @param c
* @return
*/
int findCharByP(const char *sr, char ch, char *ret) {
while (*sr) {
if (*sr == ch) {
*ret = *sr;
return 1;
}
sr++;
}
return 0;
}
主函数:
int main() {
printf("利用二级指针去查找 \n");
char str[] = "hello china";
int (*find)(const char *, char, char *) = find2;
char c;
if (find(str, 'a', &c)) {
printf("找到了 %c \n", c);
} else {
printf("没找到 \n");
}
return 0;
}
二级指针
这种方式的实现原理,跟上面修改后的代码是一样的。
<div STYLE="page-break-after: always;"></div>
/**
* 利用二级指针
*
* @param src 指向源字符串
* @param ch 要查找的字符串
* @param dist 保存查到的字符串的首地址的指针
* @return 是否查找成功
*/
int findCharByPP(const char *src, char ch, const char **dist) {
while (*src) {
if (*src == ch) {
*dist = src;
return 1;
}
src++;
}
return 0;
}
方式一:对指针变量取地址
int main() {
printf("利用二级指针去查找 \n");
char str[] = "hello china";
char *pString = NULL;
int (*find)(const char *, char, const char **) = findCharByPP;
if (find(str, 'c', (const char **) &pString)) {
printf("找到了 %c \n", *pString);
} else {
printf("没找到 \n");
}
return 0;
}
在函数内部声明了一个指针变量,指向一个字符型变量的pString,同时声明了一个函数指针find。
当src
移动到需要找的那个字符时,就会将该字符的地址0x7ffedfe8f442
存到pString
所在的内存中,这个时候,也就是这个指针变量指向的地址就是那个字符串的地址。
方式二:二级指针变量
int main() {
printf("利用二级指针去查找 \n");
char str[] = "hello china";
int size = sizeof(str) / sizeof(char);
for (int i = 0; i < size; ++i) {
printf("str[%d]=%c =%p\n", i, str[i], &str[i]);
}
const char **pString = (const char **) (char **) malloc(1);
int (*find)(const char *, char, const char **) = findCharByPP;
if (find(str, 'c', (const char **) pString)) {
printf("找到了 %c \n", **pString);
} else {
printf("没找到 \n");
}
free(pString);
return 0;
}
归根到底,就是你想改变那块内存的值,首先先找到那块内存,然后重新写入数据,赋予新值。而不是修改形参的指向。
指针使用补充
1、一个指针变量可以指向计算机中的任何一块内存,不管该内存有没有被分配,也不管该内存有没有使用权限,只要把地址给它,它就可以指向。
2、未初始化的局部变量的值是不确定的,C语言并没有对此作出规定,不同的编译器有不同的实现,我曾警告大家不要直接使用未初始化的局部变量。上面的代码中,str 就是一个未初始化的局部变量,它的值是不确定的,究竟指向哪块内存也是未知的,大多数情况下这块内存没有被分配或者没有读写权限。
对没有初始化的指针赋值为 NULL:char *str = NULL;
其实,NULL 是在stdio.h中定义的一个宏为:
#define NULL ((void *)0)
(void *)0
表示把数值 0 强制转换为void *类型,最外层的( )把宏定义的内容括起来,防止发生歧义。从整体上来看,NULL 指向了地址为 0 的内存,而不是前面说的不指向任何数据。
在进程的虚拟地址空间中,最低地址处有一段内存区域被称为保留区,这个区域不存储有效数据,也不能被用户程序访问,将 NULL 指向这块区域很容易检测到违规指针。
3、在大多数操作系统中,极小的地址通常不保存数据,也不允许程序访问,NULL 可以指向这段地址区间中的任何一个地址。
注意,C语言没有规定 NULL 的指向,只是大部分标准库约定成俗地将 NULL 指向 0,所以不要将 NULL 和 0 等同起来,例如下面的写法是不专业的:
int *p = 0;
而应该写为:
int *p = NULL;
注意 NULL 和 NUL 的区别:NULL 表示空指针,是一个宏定义,可以在代码中直接使用。而 NUL 表示字符串的结束标志 '\0'
,它是ASCII码表中的第 0 个字符。NUL 没有在C语言中定义,仅仅是对 '\0'
的称呼,不能在代码中直接使用。
实现一个函数回调
int add(int a, int b) {
return a + b;
}
int mul(int a, int b) {
return a * b;
}
int minus(int a, int b) {
return a - b;
}
int dev(int a, int b) {
return a - b;
}
int test2(int a, int b, int(*callback)(int, int)) {
return callback(a, b);
}
int main() {
int a = 100, b = 20;
printf("%d + %d = %d\n",a,b,test2(a,b,add));
printf("%d - %d = %d\n",a,b,test2(a,b,minus));
printf("%d * %d = %d\n",a,b,test2(a,b,mul));
printf("%d / %d = %d\n",a,b,test2(a,b,dev));
return 0;
}