学嵌入式C语言,看这一篇就够了(7)

打印 上一主题 下一主题

主题 1009|帖子 1009|积分 3027


C语言数组

变量其实就是在程序中由内核申请的一块内存,只不过为了方便用户访问,允许用户对这块内存进行定名,这样内核就可以把内存地址和变量名称建立映射关系,以是用户可以直接通过名称访问变量
但是如果打算存储多个数据,需要定义多个变量,并且需要为每个变量进行定名,实现起来比力贫困
数组的概念

数组就是数据的集合,简朴的说数组就是由n个数据组合在一起,数组的英文是Array,数组其实就是用户向内核申请的一块空间,只不过内核提供的这块空间的内存地址是连续的,目的就是方便用户存储数据和访问数据
Question1:
  1. 思考:既然用户可以向内核申请一块连续的空间来存储数据,那用户如何访问这块内存呢?
复制代码
Answer1:
  1. 回答:首先向内核申请内存时如果申请成功则操作系统会把内存的起始地址提供给用户,用户可以通过存储单元的地址访问,但是程序的可读性以及可维护性变差,所以C语言允许用户像访问变量一样,可以对数组进行命名,这样系统内核就会把数组名和数组的起始地址建立映射关系
复制代码
Question2:
  1. 思考:既然数组在内存中是一块连续的空间,那如果用户打算存储的数据的类型不一致,计算机如何区分数据的类型?
复制代码
Answer2:
  1. 回答:计算机不用区分,因为只需要让数组中只能存储相同类型的数据,可以避免二义性出现
复制代码
数组的定义

Question3:
  1. 如果用户打算把多个同一个类型的数据写入数组进行存储,那应该如何向内核申请空间?
复制代码
Answer3:
  1. 回答:用户需要说明 数据数量 * 数据宽度 ,对于数据宽度而言指的是数据类型 ,数据的类型可以是基本数据类型(字符型、整型、短整型、浮点型) or 复杂数据类型(结构体、联合体) or 指针类型
复制代码
定义数组的格式: 数据类型 数组名称 [数据个数] ;  // 内存大小:数据类型 * 数据个数


数组的访问

Question4:
  1. 思考:如果把数据存储到数组中,用户如何访问数组中某个数据?
复制代码
可以参考C语言标准

Answer4:
  1. 回答:可以知道C语言标准中想要访问数组中的某个元素,可以使用 E1[E2] 结构,E1是数组对象,其实就是数组名,E2是一个整数,用于作为数组下标,并且E2的值是从0开始。
复制代码
以是数组的下标的范围 E1[0] ~ E1[E2-1],就可以通过这种方式来表示恣意一个数组中的元素!

Question5:
  1. 思考:既然数组名表示数组首个元素的地址,那用户除了通过数组下标访问数组元素外,是否可以直接通过内存地址来访问数组中的数据?如果可以那如何直接通过地址访问数组呢?
复制代码

Answer5:
  1. C语言标准中规定可以通过 下标方式访问数组的元素 E1[E2]  也可以通过 内存地址 方式访问数组中的元素 ( * ( (E1) + (E2) ) ) ,注意:如果E1就是数组名,并且E2就是整型常量,则可以把括号省略,变为 * (E1 + E2) 。
复制代码
Question6:
  1. 思考:为什么C语言中规定*( E1 + E2 )可以访问数组元素,是什么原理? * 有什么作用?
复制代码
Answer6:
  1. 回答:C语言标准中提供多种运算符,* 可以作为二元运算符, * 作为乘法运算符,需要两个操作对象,并且遵循“左结合性”,但是 * 也可以作为一元运算符, * 的含义就是间接运算符。
复制代码
(1)双目运算符


(2)单目运算符


*( E1 + E2 )的解释:E1是一个数组对象,E1也就是数组名称,C语言标准中规定命组名可以作为数组中第一个元素的地址,以是相当于E1是数组中第一个元素的地址,而E2是一个整数,以是 E1 + E2 相当于从E1这个地址向后偏移E2个单元(以元素为单元,以是需要考虑元素的类型),以是E1+E2的结果还是一个地址, *( E1 + E2) 相当于间接访问该地址,相当于得到了(E1+E2)这个地址下的值。 总结: *(E1+E2)  ==  E1[E2]


可以知道,如果E1是数组名,E2是整型常量,则E1[E2]可以等价于 E2[E1],这两种方式都可以访问数组中的元素。
Question7:
  1. 思考:如果用户定义了一个整型数组 int buf[5]; 那么 (buf+1) 指的是数组地址向后偏移一个元素对应的单元大小,也就是地址向后偏移了4字节,请问 (&buf+1) 表示什么意思,应该如何解释?
复制代码
Answer7:
  1. 回答:(&buf + 1)表达式中存在地址运算符,&可以是一元运算符,也可以是二元运算符,使用规则如下:
复制代码
(1)二元运算符


(2)一元运算符




C语言标准中提到数组名可以用于表示数组的第一个元素的地址,但是此时有两种例外情况。

  • 第一种情况: 当 数组名 和 &地址运算符 一起使用时,数组名就不表示数组首元素的地址,而表示数组自己。

    (&buf+1) 可以知道 &取地址符和数组名一起使用时,数组名不表示数组第一个元素的地址,而表示数组自己的地址,以是+1的动作是向后偏移整个数组的大小。

  • 第二种情况:当 数组名 和 sizeof()运算符 单独使用的时候,数组名就不表示数组首元素地址,而表示数组自己。

数组初始化

Question8:
  1. 思考:用户为数组申请的内存空间是由内核挑选的,那内存地址中是否会存储一些意想不到的值,如果会,那用户如何对数组进行初始化?
复制代码
Answer8:
  1. 回答:C语言标准中规定数组可以进行初始化
  2. 注意:只有定义数组的同时进行赋值才叫初始化!
复制代码

  • 格式:数据类型  数组名[数组容量]  = {0};   比如 int  buf[5] = {0,0,0,0,0};   //数组初始化
Question9:
  1. 思考:语句 int buf[10] = {0}; 可以把数组的每个元素都设置为0,那 int buf[10] = {1}; 是否表示把数组的每个元素都设置为1?
复制代码


Answer9:
  1. 只是将第一个数设置为1,其他的都为0
复制代码
Question10:
  1. 思考:如果用户在定义数组时还没想好要存储的数据的个数,那数组[]里面是否可以空着不写? 语法上是否符合? 比如  int  buf[]; //用户没有填写数据元素的个数
复制代码
Answer10:
  1. 回答:语法是符合的,可以在定义数组时不去指定数组的元素个数,但是一般需要在定义数组的同时进行初始化。
复制代码

Question11:
  1. 思考:如果用户定义数组时并未在[]中说明数组元素个数,但是在定义数组时已经对数组进行初始化,所以系统会自动计算数组所需要占用的内存大小,请问如何计算出数组的有效长度以及如何计算数组元素个数?
复制代码
Answer11:
  1. 回答:可以利用sizeof()运算符来计算数组的容量,计算出的数组大小是以字节为单位,然后再用数组容量 / 数组中元素的类型就可以得到数组中元素的个数。
复制代码

Question12:
  1. 思考:用户定义了一个数组,并且也对数据正确进行了初始化,但是用户后面准备存储新的元素到数组中,想要把之前存储的元素清空,由于定义数组已经做过初始化的,是否意味着只能把数组中的元素一个一个单独清空?
复制代码
Answer12:
  1. 回答:不需要,可以调用库函数 bzero() 以及 memset() ,可以专门对数组进行处理,尤其是清空数组的处理。memset() 比 bzero() 更灵活。
复制代码
(1)        bzero()



(2)        memset()



Question13:
  1. 思考:用户定义了一个数组,并且也对数据正确进行了初始化,但是用户不小心把超过数组大小的数据赋值给数组,请问编译器是否会报错?以及用户是否可以这么操作?
复制代码
Answer13:
  1. 回答:编译一般是不会报错,甚至于警告都不会出现,但是在程序运行阶段可能会导致内存错误(段错误),现在的现象是数组出现越界访问的情况,如果刚好访问的内存是有访问权限的,则运行也不会报错,但是如果访问的内存是没有访问权限的,就会段错误,所以就需要用户设计程序要谨慎细心。
复制代码

Question14:
  1. 思考:用户定义一个数组,但是在定义数组之后并没有进行初始化,而是在定义数组之后想要对数组初始化,请问是否可以,如果可以,怎么做?
复制代码

字符型数组

一样平常实际开发中,使用数组一样平常都是为了存储字符序列,C语言中的字符串也属于字符序列,字符串需要使用双引号””进行限制,双引号””表示字符串的首字符的地址,字符串的结束以’\0’作为结束。
Question15:
  1. 思考:用户定义了一个字符数组 char buf[5]; 用户想要把一个字符序列abcde这5个字符存储到字符数组中,提供两种方案: char buf[5] = “abcde”;  char  buf[5] ={‘a’,’b’,’c’,’d’,’e’}; 请问两种方案有什么区别?
复制代码

Answer15:
  1. 回答:如果数组的容量刚好和字符串常量中的有效字符的数量一致时,就会导致数组越界,因为字符串常量的末尾有一个转义字符’\0’,也是需要占用1个字节的存储单元。
复制代码

数组型数组

Question16:
  1. 思考:既然数组中可以存储某个类型的数据,那数组本身也是一个类型,那能否在数组中存储数组呢?如果可以,应该怎么做?
复制代码
Answer16:
  1. 回答:对于数组型数组而言,就称为多维数组,但是注意:维度是针对用户而言,内存是线性的,是不分行和列的,所以多维数组其实和一维数组的本质是一样的,都是为了申请一块连续的内存空间,并且内存空间存储的数据的类型是一致的,这个只需要把数组作为元素来看待即可。
复制代码
注意:不管是几维数组,数组的定义规则: 数组名[元素数目]  + 元素类型   比如 int buf[5]
二维数组定义格式 :  元素类型 数组名称[元素数目][元素数目]    比如 int buf[2][3] = {0};
Question17:
  1. 思考:如果定义的是多维数组,那如何去访问多维数组中的某个元素? 应该如何设计程序?
复制代码
Answer17:
  1. 回答:就可以通过下标的方式或者地址的方式进行访问,下标的方式: int buf[3][4]; 则如果打算访问 buf[1][1] ,就表示访问元素如下图
复制代码

通过地址的方式访问: int buf[3][4]; 则如果打算访问 buf[1][1] ==>  * ( (  *(buf + 1)  ) + 1 )


柔性型数组

Question18:
  1. 思考:用户定义一个数组,但是在定义数组的时候没有想清楚数组的元素数量,所以能否使用一个变量来代替数组元素个数呢?如果可以,那是否意味着用户可以在运行程序的时候通过键盘对变量赋值,从而实现手动控制数组元素个数?
复制代码
Answer18:
  1. 回答:柔性数组在C89标准中是不支持的,是C99标准引入的概念,柔性数组也被称为变长数组,但是注意:当数组的内存一旦确定,则不会因为变量发生变化导致数组长度变化!
复制代码

匿名型数组

C99标准中支持匿名数组,但是匿名数组一样平常都是在函数参数中或者在多维数组中使用,很少单独使用。
比如二维数组  int buf[3][4];  --->  buf[3] 数组的每个元素的类型是 int [4] ,就是匿名数组。

零长度数组

GNU构造在C99标准的柔性数组的基础之上拓展了一个新的概念,叫做零长数组,也就是数组的长度可以是0,但是由于数组长度是0,以是操作系统是不会提供内存单元给数组的!

注意:零长度数组是不会得到内存,但是是可以访问的,一样平常都是团结C语言的布局体一起使用,可以用于对布局体进行拓展,以是零长度数组也属于柔性数组的一种。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

钜形不锈钢水箱

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表