数组的递进理解——由一维到多维,数组和指针的关系比较等

2024-06-02 13:58

本文主要是介绍数组的递进理解——由一维到多维,数组和指针的关系比较等,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近写程序发现自己对数组方面的概念知识不是特别的清晰,然后就在网上查了些资料,把书上关于数组的知识复习了一遍,现在把我学习数组的过程记录下来,让自己对数组有更加深刻的影响。我主要是介绍了数组的一些基本概念,然后还有数组和指针之间的一些关系,例如:数组指针和指针数组等。下面就开始我对数组理解的一个递进的过程,希望能和大家一起分享讨论。当然这里不是纯粹的讲解数组的知识,如果是那样的话还不如自己去看书本呢,其实这里更多的是记录我在学习数组的过程中容易犯的错误以及我平时的一些总结(这些地方我都用红色给标出来了),好让大家以后少走一些弯路。
1、一维数组
1.1一维数组的初始化
int array1_int[5] = {0, 1, 2, 3, 4};
int array2_int[5] = {0, 1};
int array3_int[] = {0, 1, 2, 3, 4};
数组的下标是从’0‘开始的,所以定义了int array1_int[5],并不能达到array1_int[5]。当元素个数不够时,后面的元素初始化为0,但是不能忽略中间的值,忽略的值只能从后面开始。最后一个初始化式子说明,如果没有定义数组长度的时候,编译器就把数组长度设置为刚好能够容纳所有初始值的长度。调试结果如图:

char array1_char[6] = {'h', 'e', 'l', 'l', 'o', 0};
char array2_char[6] = "hello";
字符数组的初始化有如上两种方式,字符串要有结束标志符。其实我们平时用的更多的是下面的这种方式,因为它会自动添加结束标志符。调试结果如图:

这里有个要注意的地方,第二个式子中的“hello”看上去像是个字符串常量,实际上不是。当用于初始化一个字符数组时,它就是一个初始化列表。在其他任何地方,它都表示一个字符串常量。
char array2_char[6] = "hello";
char *parray = "hello";
上面这两种方式就是典型了,第一种方式中字符串是初始化列表;第二种方式是字符串常量。当然这里有的编译器(本人使用IDE:CodeBlocks,gcc编译器)会有warning: deprecated conversion from string constant to 'char*',意思就是“不赞成把字符串常量转换为char*,那么怎样才不会警告呢,就是把第二种方式改为:
char const *parray = "hello";
这样就不会有警告了,为什么呢?其实就是char*本身就是可以让你改变的意思,把一个不能改变的东西赋给一个可以改变它的变量,编译当然会发出警告了。
虽然只是警告也应该引起高度的重视,下面有个例子就说明了这个问题:
#include <stdio.h>int main(void)
{char *parray = "hello";*parray = 'a';printf("parray = %s\n", parray);return 0;
}
这个程序编译时,只会警告(如上面的警告),但是运行时就会出问题。如果你把这个程序修改一下,改成下面这种再看下:
#include <stdio.h>int main(void)
{char const *parray = "hello";*parray = 'a';printf("parray = %s\n", parray);return 0;
}
这个时候就不能通过编译了,会提示错误。这样我们就可以及时发现错误。
所以,如果我们及早改正这些问题的话,那么就可以节省很多调试的时间。

1.2一维数组和指针的关系
(1)数组名
int array[5];
数组名的值是一个指针常量,也就是数组一个元素的地址,类型是”指向int的常量指针“。从这里我们就可以发现数组名和指针是不一样的(本人在初学的时候总觉得它们是一样的概念),例如:不能自增array++是错误的,但是当数组名作为实参传递给函数参数的时候效果就一样了。
只有在两种场合下,数组名并不用指针常量来表示——就是当数组名作为sizeof操作符或单目操作符&的操作数时。siziof返回整个数组的长度,而不是指向数组的指针的长度。取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量值得指针。
(2)下标引用
除了优先级之外,下标引用和间接访问完全相同。例如下面这两个表达式是等同的:
array[5]
*(array + 5)
既然知道了这些,所以我们可以通过下面这个例子看到这个技巧的应用:
#include <stdio.h>int main(void)
{int i, array[5] = {0, 1, 2, 3, 4};int *parray = array;for (i=0; i<5; i++)printf("%d\n", parray[i]);return 0;



这里还有个让你很难接受的例子:
5[array]
做好心理准备吧,没错,这个式子是合法的,把它转换成对等的间接访问表达式如下:
*(array + 5)
这个诡异技巧之所以可行,缘于C实现下标的方法。对编译器来说,这两种形式并无差别。但是,你绝不应该编写5[array],因为它会大大影响程序的可读性。
(3)指针数组和数组指针
指针数组:array of pointers,指针组成的数组,就是每个数组元素都是指针。
int *parray[5](这里强调的是数组,数组中的每个元素的类型是:int *)
数组指针:a pointer to an array,一个指针,这个指针指向的是一个数组。
int (*parray)[5](这里强调的是指针,这个指针的类型是:int *[5])
(4)声明数组参数
当一个数组名参数传递给函数的时候,函数的原型可以定义如下:
void function(char *array_char);
void function(char *array_char[]);
你两种形式都可以用,但是哪个”更加准确“呢?答案是指针,因为实参实际上是个指针。
2、多维数组
多维数组和一维数组原理上是差不多的,但是理解上有一些要注意,就是把多维数组的看作一维数组,这个一维数组的每一个元素又是一个数组,那么后面就好理解了。    
初始化的时候注意用花括号来定界,这个技巧在初始化多维数组只有少数几个元素时尤其有效。下面看个例子(《c和指针》书本上的编程练习题):
#include <stdio.h>int main(void)
{unsigned char char_value[3][6][4][5] = {{},{{},{{},{0,' '}},{{},{},{0,0,0,'A'},{0,0,0,0,'x'}},{{},{},{0,0,0xf3}},{{},{},{0,0,0,'\n'}},{},},{{},{{},{0,0,0320}},{{},{0,'0'},{0,0,'\''},{0,'\121'}},{},{{},{},{},{0,0,'3',3}},{{},{},{},{0,0,0,0,125}},}};printf("1223=%x\n", char_value[1][2][2][3]);printf("1111=%x\n", char_value[1][1][1][1]);printf("1322=%x\n", char_value[1][3][2][2]);printf("2432=%x\n", char_value[2][4][3][2]);printf("1423=%x\n", char_value[1][4][2][3]);printf("2231=%x\n", char_value[2][2][3][1]);printf("2433=%x\n", char_value[2][4][3][3]);printf("2534=%x\n", char_value[2][5][3][4]);printf("1234=%x\n", char_value[1][2][3][4]);printf("2112=%x\n", char_value[2][1][1][2]);printf("2222=%x\n", char_value[2][2][2][2]);printf("2211=%x\n", char_value[2][2][1][1]);return 0;
}
多维数组在作为函数参数时,函数原型应按如下:
char array[3][5];void function(char (*array_char)[5]);
void function(char array_char[][5]);

PS:当然数组的知识远不止这些,由于本人能力有限,对数组的理解和总结就写到这里,希望大家一起讨论、学习。





这篇关于数组的递进理解——由一维到多维,数组和指针的关系比较等的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1024167

相关文章

go 指针接收者和值接收者的区别小结

《go指针接收者和值接收者的区别小结》在Go语言中,值接收者和指针接收者是方法定义中的两种接收者类型,本文主要介绍了go指针接收者和值接收者的区别小结,文中通过示例代码介绍的非常详细,需要的朋友们下... 目录go 指针接收者和值接收者的区别易错点辨析go 指针接收者和值接收者的区别指针接收者和值接收者的

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

深入理解Apache Kafka(分布式流处理平台)

《深入理解ApacheKafka(分布式流处理平台)》ApacheKafka作为现代分布式系统中的核心中间件,为构建高吞吐量、低延迟的数据管道提供了强大支持,本文将深入探讨Kafka的核心概念、架构... 目录引言一、Apache Kafka概述1.1 什么是Kafka?1.2 Kafka的核心概念二、Ka

Java Optional避免空指针异常的实现

《JavaOptional避免空指针异常的实现》空指针异常一直是困扰开发者的常见问题之一,本文主要介绍了JavaOptional避免空指针异常的实现,帮助开发者编写更健壮、可读性更高的代码,减少因... 目录一、Optional 概述二、Optional 的创建三、Optional 的常用方法四、Optio

Tomcat版本与Java版本的关系及说明

《Tomcat版本与Java版本的关系及说明》:本文主要介绍Tomcat版本与Java版本的关系及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Tomcat版本与Java版本的关系Tomcat历史版本对应的Java版本Tomcat支持哪些版本的pythonJ

C++原地删除有序数组重复项的N种方法

《C++原地删除有序数组重复项的N种方法》给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度,不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(... 目录一、问题二、问题分析三、算法实现四、问题变体:最多保留两次五、分析和代码实现5.1、问题分析5.

Java中数组转换为列表的两种实现方式(超简单)

《Java中数组转换为列表的两种实现方式(超简单)》本文介绍了在Java中将数组转换为列表的两种常见方法使用Arrays.asList和Java8的StreamAPI,Arrays.asList方法简... 目录1. 使用Java Collections框架(Arrays.asList)1.1 示例代码1.

解决java.lang.NullPointerException问题(空指针异常)

《解决java.lang.NullPointerException问题(空指针异常)》本文详细介绍了Java中的NullPointerException异常及其常见原因,包括对象引用为null、数组元... 目录Java.lang.NullPointerException(空指针异常)NullPointer

python安装whl包并解决依赖关系的实现

《python安装whl包并解决依赖关系的实现》本文主要介绍了python安装whl包并解决依赖关系的实现,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录一、什么是whl文件?二、我们为什么需要使用whl文件来安装python库?三、我们应该去哪儿下