学嵌入式C语言,看这一篇就够了(7)
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322144819721-549448743.pngC语言数组
变量其实就是在程序中由内核申请的一块内存,只不过为了方便用户访问,允许用户对这块内存进行命名,这样内核就可以把内存地址和变量名称建立映射关系,所以用户可以直接通过名称访问变量
但是如果打算存储多个数据,需要定义多个变量,并且需要为每个变量进行命名,实现起来比较麻烦
数组的概念
数组就是数据的集合,简单的说数组就是由n个数据组合在一起,数组的英文是Array,数组其实就是用户向内核申请的一块空间,只不过内核提供的这块空间的内存地址是连续的,目的就是方便用户存储数据和访问数据
Question1:
思考:既然用户可以向内核申请一块连续的空间来存储数据,那用户如何访问这块内存呢?Answer1:
回答:首先向内核申请内存时如果申请成功则操作系统会把内存的起始地址提供给用户,用户可以通过存储单元的地址访问,但是程序的可读性以及可维护性变差,所以C语言允许用户像访问变量一样,可以对数组进行命名,这样系统内核就会把数组名和数组的起始地址建立映射关系Question2:
思考:既然数组在内存中是一块连续的空间,那如果用户打算存储的数据的类型不一致,计算机如何区分数据的类型?Answer2:
回答:计算机不用区分,因为只需要让数组中只能存储相同类型的数据,可以避免二义性出现数组的定义
Question3:
如果用户打算把多个同一个类型的数据写入数组进行存储,那应该如何向内核申请空间?Answer3:
回答:用户需要说明 数据数量 * 数据宽度 ,对于数据宽度而言指的是数据类型 ,数据的类型可以是基本数据类型(字符型、整型、短整型、浮点型) or 复杂数据类型(结构体、联合体) or 指针类型定义数组的格式: 数据类型 数组名称 [数据个数] ;// 内存大小:数据类型 * 数据个数
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322145824695-869572596.png
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322145828603-1766832295.png
数组的访问
Question4:
思考:如果把数据存储到数组中,用户如何访问数组中某个数据?可以参考C语言标准
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150001437-131075134.png
Answer4:
回答:可以知道C语言标准中想要访问数组中的某个元素,可以使用 E1 结构,E1是数组对象,其实就是数组名,E2是一个整数,用于作为数组下标,并且E2的值是从0开始。所以数组的下标的范围 E1 ~ E1,就可以通过这种方式来表示任意一个数组中的元素!
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150025536-834063584.png
Question5:
思考:既然数组名表示数组首个元素的地址,那用户除了通过数组下标访问数组元素外,是否可以直接通过内存地址来访问数组中的数据?如果可以那如何直接通过地址访问数组呢?https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150356467-1867982077.png
Answer5:
C语言标准中规定可以通过 下标方式访问数组的元素 E1也可以通过 内存地址 方式访问数组中的元素 ( * ( (E1) + (E2) ) ) ,注意:如果E1就是数组名,并且E2就是整型常量,则可以把括号省略,变为 * (E1 + E2) 。Question6:
思考:为什么C语言中规定*( E1 + E2 )可以访问数组元素,是什么原理? * 有什么作用?Answer6:
回答:C语言标准中提供多种运算符,* 可以作为二元运算符, * 作为乘法运算符,需要两个操作对象,并且遵循“左结合性”,但是 * 也可以作为一元运算符, * 的含义就是间接运算符。(1)双目运算符
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150522875-1120029111.png
(2)单目运算符
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150529695-683865691.png
*( E1 + E2 )的解释:E1是一个数组对象,E1也就是数组名称,C语言标准中规定数组名可以作为数组中第一个元素的地址,所以相当于E1是数组中第一个元素的地址,而E2是一个整数,所以 E1 + E2 相当于从E1这个地址向后偏移E2个单位(以元素为单位,所以需要考虑元素的类型),所以E1+E2的结果还是一个地址, *( E1 + E2) 相当于间接访问该地址,相当于得到了(E1+E2)这个地址下的值。 总结: *(E1+E2)==E1
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150624825-1479986164.png
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150630022-1809458309.png
可以知道,如果E1是数组名,E2是整型常量,则E1可以等价于 E2,这两种方式都可以访问数组中的元素。
Question7:
思考:如果用户定义了一个整型数组 int buf; 那么 (buf+1) 指的是数组地址向后偏移一个元素对应的单元大小,也就是地址向后偏移了4字节,请问 (&buf+1) 表示什么意思,应该如何解释?Answer7:
回答:(&buf + 1)表达式中存在地址运算符,&可以是一元运算符,也可以是二元运算符,使用规则如下:(1)二元运算符
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150753309-176458114.png
(2)一元运算符
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150803466-1007436531.png
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150808079-1480295874.png
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150813429-72111991.png
C语言标准中提到数组名可以用于表示数组的第一个元素的地址,但是此时有两种例外情况。
[*]第一种情况: 当 数组名 和 &地址运算符 一起使用时,数组名就不表示数组首元素的地址,而表示数组本身。
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150849024-1883733678.png
(&buf+1) 可以知道 &取地址符和数组名一起使用时,数组名不表示数组第一个元素的地址,而表示数组本身的地址,所以+1的动作是向后偏移整个数组的大小。
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322150927733-1114804469.png
[*]第二种情况:当 数组名 和 sizeof()运算符 单独使用的时候,数组名就不表示数组首元素地址,而表示数组本身。
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151005682-1773161153.png
数组初始化
Question8:
思考:用户为数组申请的内存空间是由内核挑选的,那内存地址中是否会存储一些意想不到的值,如果会,那用户如何对数组进行初始化?Answer8:
回答:C语言标准中规定数组可以进行初始化注意:只有定义数组的同时进行赋值才叫初始化!
[*]格式:数据类型数组名[数组容量]= {0}; 比如 intbuf = {0,0,0,0,0}; //数组初始化
Question9:
思考:语句 int buf = {0}; 可以把数组的每个元素都设置为0,那 int buf = {1}; 是否表示把数组的每个元素都设置为1?https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151159965-70225409.png
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151204419-40002812.png
Answer9:
只是将第一个数设置为1,其他的都为0Question10:
思考:如果用户在定义数组时还没想好要存储的数据的个数,那数组[]里面是否可以空着不写? 语法上是否符合? 比如intbuf[]; //用户没有填写数据元素的个数Answer10:
回答:语法是符合的,可以在定义数组时不去指定数组的元素个数,但是一般需要在定义数组的同时进行初始化。https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151355354-210962701.png
Question11:
思考:如果用户定义数组时并未在[]中说明数组元素个数,但是在定义数组时已经对数组进行初始化,所以系统会自动计算数组所需要占用的内存大小,请问如何计算出数组的有效长度以及如何计算数组元素个数?Answer11:
回答:可以利用sizeof()运算符来计算数组的容量,计算出的数组大小是以字节为单位,然后再用数组容量 / 数组中元素的类型就可以得到数组中元素的个数。https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151454062-716016833.png
Question12:
思考:用户定义了一个数组,并且也对数据正确进行了初始化,但是用户后面准备存储新的元素到数组中,想要把之前存储的元素清空,由于定义数组已经做过初始化的,是否意味着只能把数组中的元素一个一个单独清空?Answer12:
回答:不需要,可以调用库函数 bzero() 以及 memset() ,可以专门对数组进行处理,尤其是清空数组的处理。memset() 比 bzero() 更灵活。(1) bzero()
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151548134-618555620.png
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151554117-1696896809.png
(2) memset()
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151605388-1575207408.png
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151610261-1247654077.png
Question13:
思考:用户定义了一个数组,并且也对数据正确进行了初始化,但是用户不小心把超过数组大小的数据赋值给数组,请问编译器是否会报错?以及用户是否可以这么操作?Answer13:
回答:编译一般是不会报错,甚至于警告都不会出现,但是在程序运行阶段可能会导致内存错误(段错误),现在的现象是数组出现越界访问的情况,如果刚好访问的内存是有访问权限的,则运行也不会报错,但是如果访问的内存是没有访问权限的,就会段错误,所以就需要用户设计程序要谨慎细心。https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151711407-341912200.png
Question14:
思考:用户定义一个数组,但是在定义数组之后并没有进行初始化,而是在定义数组之后想要对数组初始化,请问是否可以,如果可以,怎么做?https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151738141-1190307135.png
字符型数组
一般实际开发中,使用数组一般都是为了存储字符序列,C语言中的字符串也属于字符序列,字符串需要使用双引号””进行限制,双引号””表示字符串的首字符的地址,字符串的结束以’\0’作为结束。
Question15:
思考:用户定义了一个字符数组 char buf; 用户想要把一个字符序列abcde这5个字符存储到字符数组中,提供两种方案: char buf = “abcde”;charbuf ={‘a’,’b’,’c’,’d’,’e’}; 请问两种方案有什么区别?https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151849525-1995065232.png
Answer15:
回答:如果数组的容量刚好和字符串常量中的有效字符的数量一致时,就会导致数组越界,因为字符串常量的末尾有一个转义字符’\0’,也是需要占用1个字节的存储单元。https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322151928677-1272660660.png
数组型数组
Question16:
思考:既然数组中可以存储某个类型的数据,那数组本身也是一个类型,那能否在数组中存储数组呢?如果可以,应该怎么做?Answer16:
回答:对于数组型数组而言,就称为多维数组,但是注意:维度是针对用户而言,内存是线性的,是不分行和列的,所以多维数组其实和一维数组的本质是一样的,都是为了申请一块连续的内存空间,并且内存空间存储的数据的类型是一致的,这个只需要把数组作为元素来看待即可。注意:不管是几维数组,数组的定义规则: 数组名[元素数量]+ 元素类型 比如 int buf
二维数组定义格式 :元素类型 数组名称[元素数量][元素数量] 比如 int buf = {0};
Question17:
思考:如果定义的是多维数组,那如何去访问多维数组中的某个元素? 应该如何设计程序?Answer17:
回答:就可以通过下标的方式或者地址的方式进行访问,下标的方式: int buf; 则如果打算访问 buf ,就表示访问元素如下图https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322152114119-1777325989.png
通过地址的方式访问: int buf; 则如果打算访问 buf ==>* ( (*(buf + 1)) + 1 )
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322152127743-1145052083.png
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322152132635-1613969521.png
柔性型数组
Question18:
思考:用户定义一个数组,但是在定义数组的时候没有想清楚数组的元素数量,所以能否使用一个变量来代替数组元素个数呢?如果可以,那是否意味着用户可以在运行程序的时候通过键盘对变量赋值,从而实现手动控制数组元素个数?Answer18:
回答:柔性数组在C89标准中是不支持的,是C99标准引入的概念,柔性数组也被称为变长数组,但是注意:当数组的内存一旦确定,则不会因为变量发生变化导致数组长度变化!https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322152224844-925240621.png
匿名型数组
C99标准中支持匿名数组,但是匿名数组一般都是在函数参数中或者在多维数组中使用,很少单独使用。
比如二维数组int buf;--->buf 数组的每个元素的类型是 int ,就是匿名数组。
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322152239730-1916331091.png
零长度数组
GNU组织在C99标准的柔性数组的基础之上拓展了一个新的概念,叫做零长数组,也就是数组的长度可以是0,但是由于数组长度是0,所以操作系统是不会提供内存单元给数组的!
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322152252407-1156615491.png
注意:零长度数组是不会得到内存,但是是可以访问的,一般都是结合C语言的结构体一起使用,可以用于对结构体进行拓展,所以零长度数组也属于柔性数组的一种。
https://img2024.cnblogs.com/blog/3602479/202503/3602479-20250322152304606-2049036223.png
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]