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

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

相关文章

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

vue如何监听对象或者数组某个属性的变化详解

《vue如何监听对象或者数组某个属性的变化详解》这篇文章主要给大家介绍了关于vue如何监听对象或者数组某个属性的变化,在Vue.js中可以通过watch监听属性变化并动态修改其他属性的值,watch通... 目录前言用watch监听深度监听使用计算属性watch和计算属性的区别在vue 3中使用watchE

百度/小米/滴滴/京东,中台架构比较

小米中台建设实践 01 小米的三大中台建设:业务+数据+技术 业务中台--从业务说起 在中台建设中,需要规范化的服务接口、一致整合化的数据、容器化的技术组件以及弹性的基础设施。并结合业务情况,判定是否真的需要中台。 小米参考了业界优秀的案例包括移动中台、数据中台、业务中台、技术中台等,再结合其业务发展历程及业务现状,整理了中台架构的核心方法论,一是企业如何共享服务,二是如何为业务提供便利。

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

POJ1269 判断2条直线的位置关系

题目大意:给两个点能够确定一条直线,题目给出两条直线(由4个点确定),要求判断出这两条直线的关系:平行,同线,相交。如果相交还要求出交点坐标。 解题思路: 先判断两条直线p1p2, q1q2是否共线, 如果不是,再判断 直线 是否平行, 如果还不是, 则两直线相交。  判断共线:  p1p2q1 共线 且 p1p2q2 共线 ,共线用叉乘为 0  来判断,  判断 平行:  p1p