如何理解Go语言的数组

什么是数组

首先下一个定义,数组是对线性的内存区域的抽象。高维数组和一维数组有着同样的内存布局。(大学生考试的时候别借鉴哈,这是自己下的定义,相当于是一篇议论文的论点。)

线性的内存区域说白了就是连续的内存区域。无论一维数组、二维数组、N维数组都处在连续的内存区域中,数据排列是连续的。CPU缓存对连续的内存区域具有较高的亲和性,这也是数组的访问速度要快于链表的一个重要原因。

一维数组

以C语言中的一维数组为例:int array[3] = {1,2,3};,此时array保存的即数组的首地址,以该地址为首的连续的内存区域中,保存了1,2,3的值。

二维数组

二维数组可以理解成:是保存了一维数组首地址(指针)一维数组
这是什么意思呢?以下面三个一维数组:array0array1array2为例,(前提,他们仨处于一块连续的内存区域上,且首尾无间断),假设它们的首地址分别是:0x00000x000C0x0018。看一下这三个数字,它们每两个之间的差都是0xc。因为C语言中int类型占4个字节宽度。所以说每个一维数组占用的内存长度都是12个字节,并且array1数组首地址刚好是array0数组末地址+1的位置,array2和array1的关系亦是如此。

int array0[3] = {1,2,3};
int array1[3] = {4,5,6};
int array2[3] = {7,8,9};

那么这片连续的内存区域可以表示为这个样子:

   +----++----++----++----++----++----++----++----++----+
...|  1 ||  2 ||  3 ||  4 ||  5 ||  6 ||  7 ||  8 ||  9 |...+----++----++----++----++----++----++----++----++----+⬆                 ⬆                 ⬆0x0000            0x000c            0x0018array0            array1            array2

使用C语言定义一个一维指针数组:
int *array[3] = {array0,array1,array2};,数组中保存三个一维数组的首地址。

用该数组指针模拟一下二维数组。

#include "stdio.h"
int main() {int array0[3] = {1,2,3};int array1[3] = {4,5,6};int array2[3] = {7,8,9};int *array[3] = {array0,array1,array2};for(int i = 0; i < 3; i++) {for(int j = 0; j < 3; j++){printf("%d\t",*(*array+j)+i*3);}printf("\n");}
}

输出的内容是:

1       2       3
4       5       6
7       8       9

他的内存布局是这样的:

   +----++----++----++----++----++----++----++----++----+
...|  1 ||  2 ||  3 ||  4 ||  5 ||  6 ||  7 ||  8 ||  9 |...+----++----++----++----++----++----++----++----++----+⬆                 ⬆                 ⬆0x0000            0x000c            0x0018array0            array1            array2+----------++----------++----------+
...|  0x0000  ||  0x000c  ||  0x0018  |...+----------++----------++----------+⬆array

我们不搞的那么麻烦,直接用C语言定义一个普通的二维数组:

#include "stdio.h"
int main() {int array[3][3] =  {{1,2,3},{4,5,6},{7,8,9}};for(int i = 0; i < 3; i++) {for( int j = 0; j < 3; j++) {printf("%d\t",array[i][j]);}printf("\n");}
}

其打印内容也是:

1       2       3
4       5       6
7       8       9

使用访问连续内存区域的方式*((*array)+i),打印该数组:

#include "stdio.h"
int main() {int array[3][3] =  {{1,2,3},{4,5,6},{7,8,9}};for( int i = 0; i < 3 * 3; i++) {printf("%d\t",*((*array)+i));}
}

其输出内容为:

1       2       3       4       5       6       7       8       9

其内存布局是这样的:

   +----++----++----++----++----++----++----++----++----+
...|  1 ||  2 ||  3 ||  4 ||  5 ||  6 ||  7 ||  8 ||  9 |...+----++----++----++----++----++----++----++----++----+⬆array
  • 可以看出,二维数组是将元素连续排列的一维数组。
  • 同理高维数组,也是将元素线性排列的一维数组。
  • 这也佐证了我们 数组是对线性的内存区域的抽象。高维数组和一维数组有着同样的内存布局 的观点。

如何理解Go语言的数组

Go语言中的数组,和C语言的数组大同小异,数组的首地址也指向了一片连续的内存区域。这里的数组指的是array,而不是slice

如何将一个一维数组映射成一个二维数组

仁者见仁,智者见智

第一种方式:

package mainimport "fmt"func main() {array0 := [12]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}var array [3][4]intfor i := 0; i < 12; i++ {array[i/4][i%4] = array0[i]}fmt.Println(array)
}

上述方式展示了利用计算下标的方式进行转化,其结果为:

[[1 2 3 4] [5 6 7 8] [9 10 11 12]]

第二种方式,终极大杀器

在这里插入图片描述

package mainimport ("fmt""unsafe"
)func main() {array0 := [12]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}var array = *(*[3][4]int)(unsafe.Pointer(&array0))fmt.Println(array)
}

结果为:

[[1 2 3 4] [5 6 7 8] [9 10 11 12]]

这种强制转化的方式,是利用了高维数组和一维数组有着同样的内存布局的这个原理,直接在类型层面做了一层转化,此时array 是 zero copy的,也就是说跟原数组共用同一片内存。二者只要更改任意一方元素,都会影响到对方。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/309347.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Kubernetes】什么是 kubectl ?

什么是 kubectl &#xff1f; 1.什么是 kubectl &#xff1f;2.Kubernetes 内部结构3.Kubernetes API 的作用 1.什么是 kubectl &#xff1f; 在学习如何更有效地使用 kubectl 之前&#xff0c;您应该对它是什么以及它如何工作有一个基本的了解。从用户的角度来看&#xff0c;…

贪心算法—会议安排

与其明天开始&#xff0c;不如现在行动&#xff01; 文章目录 1 安排会议1 题目描述2 解决思路3 代码实现 &#x1f48e;总结 1 安排会议 1 题目描述 一些项目要占用一个会议室宣讲&#xff0c;会议室不能同时容纳两个项目的宣讲。 给你每一个项目开始的时间和结束的时间 你来…

leetcode贪心算法题总结(三)

本章目录 1.合并区间2.无重叠区间3.用最少数量的箭引爆气球4.整数替换5.俄罗斯套娃信封问题6.可被三整除的最大和7.距离相等的条形码8.重构字符串 1.合并区间 合并区间 class Solution { public:vector<vector<int>> merge(vector<vector<int>>&…

【并发】AtomicInteger很安全

AtomicInteger 简介与常规用法 AtomicInteger 是 Java 中 java.util.concurrent.atomic 包下的一个类。用于实现原子操作的整数。它是一个基于CAS&#xff08;Compare-And-Swap&#xff09;实现的原子整数类。它提供了一系列的原子操作&#xff0c;确保对整数的操作是原子性的&…

C#高级 08Json操作

1.概念 Json是存储和交换文本信息的语法。类似于XML。Json比XML更小、更快、更易解析。Json与XML一样是一种数据格式。Json是一种轻量级的数据交换格式。它基于ECMAScript的一个子集。Json采取完全独立于语言的文本格式&#xff0c; 但是也使用了类似于C语言的习惯。这些特性使…

DevEco Studio4.0 Beta2集成ArkUI-X(开发鸿蒙,安卓.ios应用)/ACE Tools脚手架

ArkUI-X简介 ArkUI-X进一步将ArkUI扩展到了多个OS平台&#xff1a;目前支持OpenHarmony、HarmonyOS、Android、 iOS&#xff0c;后续会逐步增加更多平台支持。开发者基于一套主代码&#xff0c;就可以构建支持多平台的精美、高性能应用 该框架对应的IDE版本为 4.0 Beta2 &…

【数据结构复习之路】查找(严蔚敏版)万字详解

专栏&#xff1a;数据结构复习之路 复习完上面四章【线性表】【栈和队列】【串】【数组和广义表】【树和二叉树】【图】&#xff0c;我们接着复习 查找&#xff0c;这篇文章我写的非常详细且通俗易懂&#xff0c;看完保证会带给你不一样的收获。如果对你有帮助&#xff0c;看在…

Spring-5-切入点的高级使用

Spring提供了两个额外的Pointcut实现&#xff0c;分别是ComposablePointcut和ControlFlowPointcut,它们提供了所需的灵活性。 使用控制流切入点 由ControlFlowPointcut类实现的Spring控制流切入点类似于许多其他AOP实现中可用的cflow构造&#xff0c;尽管功能上没有那么强大。…

集群部署篇--Redis 集群分片模式

文章目录 前言一、Redis 分片集群介绍1.1 介绍&#xff1a;1.2 工作机制&#xff1a;1.2.1 节点&#xff1a;1.2.2 槽 slot&#xff1a;1.2.3 故障转移&#xff1a; 二、Redis 分片集群搭建:2.1 配置文件&#xff1a;2.2 redis 部署&#xff1a;2.3 redis 集群创建&#xff1a;…

大数据背后的绿色收割:基于Hadoop的农产品价格信息智能分析

大数据背后的绿色收割&#xff1a;基于Hadoop的农产品价格信息智能分析 引言正文1. 数据获取与准备2. 数据清洗与处理3. Hadoop数据分析引擎的运用4. MySQL数据库的集成5. 创新性的可视化6. 结论与展望 结语 引言 随着信息技术的不断发展&#xff0c;农业领域也在数字化的浪潮…

padStart(),padEnd()

今天获取当前时间的时候&#xff0c;gpt输出这样的代码&#xff0c;padStart(2, 0) function getCurrentDateFormatted() {const currentDate new Date();const month (currentDate.getMonth() 1).toString().padStart(2, 0);const day currentDate.getDate().toString().…

10.定时器各功能分析及编码

知识汇总&#xff1a; STM32的定时器有三种&#xff0c;高级定时器&#xff0c;通用定时器&#xff0c;基本定时器 就是功能多与少的差别&#xff0c;下面来逐个解释功能&#xff1a;在此之前&#xff0c;需要对几个概念有认知 几个概念&#xff1a; 1.定时器时钟频率&…