批量组织相同数据类型的基础数据结构——数组

批量组织相同数据类型------数组

  • C语言将数组看作派生类型(建立在其他类型的基础上)
  • []:变地址运算符,表示将指针移动多少个存储单元。如a[n]=a[0]+n*sizeof(a[0]);
  • 数组的索引,即为下标(变地址运算符中的偏移量)。数组索引范围: [ 0 , 数组大小 − 1 ] [0,数组大小-1] [0,数组大小−1]。数组越界(索引超出范围,索引为负数等)是未定义行为(访问了未经申请的内存)。
  • C99之后增加了变长数组(VLA),允许用变量表示数组的维度(包括用const修饰符限定的变量)。注意,VLA并非在运行时可以自动改变数组大小。VLA必须为自动(auto)存储类别,不能为static或extern,且不能在声明时进行初始化。
  • 数组的初始化分为完全初始化与不完全初始化。对于非字符数组类型,初始化数组采用序列表的方式(即为{}的形式),序列表仅允许在初始化时使用。对于不完全初始化,编译器会将未初始化的部分设为0。如果在块作用域中没有初始化数组且为自动数组(auto),会被填充随机值,一般将随机值视为垃圾数据。
  • 指定初始化器:可以在初始化列表中指定待初始化的元素。例:int arr[6]={[5]=0};指定初始化器的用法可以与普通初始化的方法混用。例:int a[6]={1,[3]=5,6,7};中,由于中间使用逗号运算符进行分隔,故执行顺序为从左到右依次进行,a[0]=1,a[3]=5,a[4]=6,a[5]=7;其余为0。若对同一元素出现了多次修改,则以最后面的作为最终结果(逗号运算符特性,后面的会覆盖前面的)。这种下标继承方式类似于枚举中的用法。
    注:指定初始化器的方式进行初始化为C99的扩展,标准C++不支持这种写法,具体实现取决于C++编译器(g++不允许,clang++允许)。
  • 求数组长度:(sizeof(数组名)/sizeof(数组名[0]))(再次注意:sizeof是标识符而非函数)
  • 常量数组:加上const修饰符的数组,一旦创建之后,数组将为只读,内容不可被修改。
  • 不允许直接将一个数组通过赋值表达式给其他数组。
  • 数组名:即为整个数组的地址,同时也是数组中首元素的地址(即数组的基址)。
  • 基址是指用于定位数据结构中元素位置的地址。它通常指向数据结构的起始位置或者某个特定元素的位置。基址通常与偏移量结合使用,通过基址加上偏移量来计算访问数据结构中的特定元素的内存地址。

    在数组中,基址指的是数组的起始地址,即数组的首元素的地址。数组名在大多数情况下可以被视为数组的基址,通过数组名可以访问数组中的元素,时间复杂度为 O ( 1 ) O(1) O(1)。因此,数组中变地址运算符可视为数组的偏移量。

    在函数调用中,基址指的是当前函数栈帧的基址,也就是栈帧中局部变量和参数的起始地址。基址指针在函数调用过程中用于帮助定位局部变量和参数。

    总的来说,基址是用于定位数据结构中元素位置的地址,它是访问数据结构中元素的起点。

  • 数组是一种顺序表,在内存中存储是连续的

多维数组

以最简单的多维数组------二维数组举例:

C语言遵循高维度优先原则,在二维数组中体现为行优先原则。二维数组的初始化,为避免混淆,一般采用嵌套序列表的方式进行(但并不绝对)。

c 复制代码
int a[6][4];//定义了一个6行4列的二维数组

二维数组是若干个一位数组的数组,二维数组的每一个元素都是一维数组。对于上述二维数组a,其本身是一个有6个元素的一维数组(a[3])称为主数组。主数组中的每个元素是包含4个元素的一维数组。

注意:数组是顺序表,在内存中始终是顺序存储的,行和列只是为了方便理解而创造出来的,在内存中存储并不涉及行和列的概念。

从指针角度看二维数组:

  • 数组名a是整个主数组a的地址,也是主数组中首元素a[0]的地址,a[0]是大小4个int的数组,因此a是占有4个int大小对象的地址。
  • a[0]元素本身是一个数组,因此a[0]是a[0]中首元素a[0][0]的地址,因此a[0]是占用1个Int大小对象的地址。
  • 一句话总结:a=&a[0],a[0]=&a[0][0];*a=*(&a[0])=a[0]=&a[0][0],**a=*a[0]=*(&a[0][0])=a[0][0]
  • 以上地址的地址(二级地址)、指针的指针(二级指针),是双重间接

例:

c 复制代码
a+1//a=&a[0],a[0]是4个int大小的数组,因此a+1为+4个int大小,即为行指针下移,变为a[1]
a[0]+1//a[0]=&a[0][0],列指针下移,变为a[0][1]
(a+1)[2]//a=&a[0],行指针下移1个存储单元,变为a[1],之后通过变地址运算符行指针再下移2行,变为a[3]
(a[0]+1)[2]//a[0]=&a[0][0],列指针下移1个存储单元,变为a[0][1],之后通过变地址运算符列指针再右移2个存储单元。再次注意:数组是顺序表,在内存中存储是连续的,因此可跨行移动
(a+1)[0][2]//a=&a[0],行指针下移1个存储单元变为a[1],之后通过变地址运算符转换为列状态,列指针再下移2个存储单元

从存储单元角度理解,会发现容易很多。

  • 二维数组在函数定义中做函数形参时,行可以缺省,但列不能缺省。即使行未缺省,编译器也会自动忽略该值。对于多维数组来说,在函数定义中做形参时,只能省略最左侧方括号中的值。(因为最左侧方括号表明其为一个指针)。
  • VLA的二维数组在函数声明时若要省略维度形参,必须用*来代替省略的维度。在函数定义时则只能缺省最左侧的维度形参,其余维度形参在函数形参表中必须比数组先进行声明。(注意形参永远只能被看作是声明而非定义,因为形参只发生在函数调用时而非函数定义时)

指针数组

注意优先级[]>*

c 复制代码
int *p[2];//定义了大小为2的数组,每个元素都是int*(int型指针)
//其实应该相当于:
int* p[2];

数组指针

注意优先级[]>*

c 复制代码
int (*p)[2];//定义了一个可以指向含有2个int型元素的一维数组的指针

在二维数组中应用数组指针则相当于行指针。

以上内容可以用指针函数和函数指针数组理解

相关推荐
ZSYP-S6 分钟前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos10 分钟前
c++------------------函数
开发语言·c++
程序员_三木22 分钟前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
是小崔啊32 分钟前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
tianmu_sama38 分钟前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
黄公子学安全40 分钟前
Java的基础概念(一)
java·开发语言·python
liwulin050641 分钟前
【JAVA】Tesseract-OCR截图屏幕指定区域识别0.4.2
java·开发语言·ocr
jackiendsc1 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
Oneforlove_twoforjob1 小时前
【Java基础面试题027】Java的StringBuilder是怎么实现的?
java·开发语言
羚羊角uou1 小时前
【C++】优先级队列以及仿函数
开发语言·c++