深入理解数组和结构体

数组概述

Array Allocation

对于数据类型 T 和 整型常数 N,声明如下 T A[N],这个声明有如下效果:

  1. 首先,计算机在内存中分配一个 L * N 字节的连续区域,这里 L 指的是数据类型 T 的大小。
  2. 数组引入标识 A, 可以用 A 来作为指向数组开头的指针,这个指针的值表示为 Xa,可以用 0 ~ N-1 的整数索引来访问该数组元素,第 i 个数组元素会被存放在地址为 Xa + L*i 的地方。

访问数组

Array Access

C 语言允许对指针进行运算,而计算出来的值会根据该指针引用的数据类型的大小进行伸缩。也就是说,如果 p 是一个指向类型为 T 的数据的指针,p 的值为 xp, 那么表达式 p+i 的值为 xp + L*i,这里 L 是数据类型 T 的大小。操作符 & 可以产生指针,操作符 * 可以产生间接引用指针。也就是说,对于一个表示某个对象的表达式 Expr, &Expr 是给出该对象地址的一个指针。对于一个表示地址的表达式 AExpr, *AExp 是给出该地址处的值。因此,表达式 Expr 和 *&Expr 是等价的。可以对数组和指针应用数组下标操作,数组引用 A[ i ] 等价于表达式 *(A+i),它计算第 i 个数组元素的地址,然后访问这个内存地址。

嵌套数组

Nested Array

当我们创建数组类型的数组时,数组分配和引用的一般原则也是成立的。创建了一个 T 数据类型的嵌套数组 A [R][C],表示数组有 R 行,C 列,一个 T 数据类型需要 k 个字节存储。 整个数组 A 的大小就是 R * C * k 字节。要访问嵌套数组的元素,编译器会以数组起始为基地址,偏移量为索引,计算出期望的元素的偏移量。对于 A[R][C] 来说, &A[i][j] 的内存地址为 &A[R][C] = x + k * ( C * i + j ),其中 x 为数组 A 的起始地址, k是数据类型 T 以字节为单位的大小。

数据内存排列顺序

二维数组在内存中的排列顺序是以行为主,如上图所示,int A[R][C] 中 R 的值先从 0 开始,接着 C 的值从 0 ~ (c-1), 之后 R 的值到 1, C 的值再从 0 ~ (c-1), 直到 R 的值为 R-1。

多维数组

回忆一下,上面提到的嵌套数组。

Nested Array Example

那么接下来讲讲多维数组,这两个概念可是不一样的哦!

Multi-Level Array Example

上图中,首先定义了三个 zip_dig 类型的数组 cmu,uw,ucb。然后定义了一个 univ 的指针数组,该指针数组保存了 cmu,uw,ucb 的值。注意一下,univ 保存的是指针。

那么怎么访问嵌套数组呢?又怎么访问多维数组呢?

Array Element Accesses

对于嵌套数组来说,数组在内存的排列是连续的。所以数组元素的内存地址的计算方式是 Mem [ sea + 20 * index + 4 *dig ] ,其中 sea 是数组的起始地址,4 是数组中每个元素的字节大小, 20 是嵌套数组每行的内存容量由 4 * 5 得来,Mem表示内存数组。

嵌套数组

对于多维数组来说,数组在内存的排列则是非连续性的,依赖于指针的值。数组元素的内存地址的计算方式需要经过 2 个步骤,第一个步骤是计算出一维数组的指针地址 Mem[univ + 4 * index],第二个步骤才是计算出数组元素的地址 Mem[ Mem[univ + 4 * index] + 4* dig]。其中 univ 是多维数组的起始位置, 4 是数组中每个元素的字节大小,Mem表示内存数组。

多维数组

结构体

Structures

C 语言的 struct 声明创建一个数据类型,将可以能不同类型的对象聚合到一个对象中,用名字来引用结构中的各个组成部分。类似于数组的实现,结构中的所有组成部分都存放在内存中一段连续的区域内,而指向结构的指针就是结构第一个字节的地址,结构体中的变量的偏移地址是由编译器在编译时期确定的。

Structures

对于 struct 的实例来说,我们可以使用点(.)操作符来引用 struct 的变量

struct rec r1; 
r1.i = val; 

也可以使用指针来表示 struct,如 struct rec *r = &r1;
使用指针和点(.)操作符来访问 struct 变量

struct rec r1; 
(*r1).i = val; 

使用箭头(->)操作符来访问 struct 变量

r->i = val;

结构体对齐

Structures & Alignment

数据对齐的实现是依赖于机器的,IA32 Linux,x86-64 Linux 和 window 都是有差异的。

Unaligned Data

如上图所示,结构体 S1 的内存排列顺序在未进行对齐的情况下是这样的。按结构体的变量声明顺序排列,每个变量的容量大小按变量类型的字节大小分配。

Aligned Data

在数据对齐中,有 2 个重要的点,第

  1. 假设变量的数据类型的字节大小 k 。
  2. 对于数据类型的字节大小为 k 的变量,这个变量的地址必须是 k 的倍数。

对于结构体 S1, char c 分配 1 个字节, int i [2] 分配 8 个字节, double v 分配 8 个字节。

  1. 从结构体对齐的角度来说,结构体 S1 的第一个变量 c 的地址必须是结构体 S1 的最大数据类型 double 的字节大小的倍数。
  2. 结构体 S1 的第二个变量是 int 类型的 i[0],根据 “对于数据类型的字节大小为 k 的变量,这个变量的地址必须是 k 的倍数” 的原则, i[0] 的地址必须是 4 的倍数,所以需要在 c 之后增加 3 个 字节的位置之后再放置 i[0]。
  3. 结构体 S1 的第二个变量是 int 类型的 i[1],由于 i[0] 的地址是 4 的倍数,而且 i[0] 占用 4 个位置,所以 i[0] 之后不需要增加额外的位置,可以直接放置 i[1]。
  4. 结构体 S1 的第三个变量 v 是 double 类型,所以 v 的地址需要是 8 的倍数,i[1] 的地址是 8 的倍数,而 i[1] 的容量是 4,所以需要再增加额外的 4 个字节之后再放置 v。
  5. 所以结构体 S1 的内存对齐格式如上图所示。

总结

数组和结构体是 C 语言编程过程中经常使用到的数据类型,对它们的理解越多越深,更能避免使用过程中的各种错误。

参考

本文是华盛顿大学的公开课 《 The Hardware / Software Interface 》的课程笔记,该课程的参考书籍是大名鼎鼎的 CSAPP 也就是《 深入理解计算机系统 》这书。文章截图来源于课程,文章的内容也参考了 CSAPP 的书本内容。

  1. https://courses.cs.washington.edu/courses/cse351/17wi/videos.html
  2. https://book.douban.com/subject/26912767/
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容

  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,419评论 3 44
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,531评论 18 399
  • 版权声明:本文为 gfson 原创文章,转载请注明出处。注:作者水平有限,文中如有不恰当之处,请予以指正,万分感谢...
    gfson阅读 2,888评论 0 6
  • 最近在看动画片《银魂》,感觉有些词特别好,就摘录出来。以前就知道《银魂》,但一直没有看,因为实在太长了。现在人坑之...
    无霜之夜阅读 455评论 0 0
  • 文/图 南楚人 太阳像个巨大的橙子在山脊上弹跳,一下蹦到山那边去了。 猛踩一脚油门,小车就着下坡的劲冲上坡,一直轰...
    老媒子阅读 1,106评论 0 4