最近一直在研究C语言,总结出一个结论:C开发者就是和内存与数据结构在打交道。

这篇文章先整理一下内存这块学习到的知识以免后面忘记了。

我们先讨论下数组和指针之间的关系,代码如下:

#include <stdio.h>

int main() {

    int arr[3] = {77777,88888,99999};                           // 定义一个长度为3 的数组,取名为:arr
int *pa = arr; // 创建一个指针变量指向 arr 数组
printf("arr 数组的指向:%p , pa变量的指向:%p \n" , arr,pa); return 0;
}

可以看到 arr 和 pa 两个变量是指向一个内存地址。

继续看代码:

int main() {

    int arr[3] = {77777, 88888, 99999};           // 定义一个长度为3 的数组,取名为:arr
int *pa = arr; // 创建一个指针变量指向 arr 数组
printf("arr 数组的指向:%p , pa变量的指向:%p \n", arr, pa);
printf("数组第一个元素指的地址 = %p , 第一个元素值 = %d \n", &pa[0], *(pa)); // 如果p是个指针变量,则:p[i] 永远等价于 *(p+i) 
printf("数组第二个元素指的地址 = %p , 第二个元素值 = %d \n", &pa[1], *(pa + 1));
printf("数组第三个元素指的地址 = %p , 第三个元素值 = %d \n", &pa[2], *(pa + 2));
return 0
}

可以看到 数组 arr 变量的指向 和 数组内第一个元素的内存指向的内存空间是一样的,我们可以总结出以下结论:

    • 数组是一段连续的内存空间,用于存储同一类型的元素
    • 数组名作为指针:当数组名被用作指针时,它会退化成指向数组第一个元素的指针。
    • 使用指针操作数组:可以使用指针和偏移量来访问和操作数组元素,对于一个 int* 指针 p(假设 int 是4字节),p + 1 会指向4字节后的地址(下一个元素),而不是紧接着的下一个字节。

回到代码继续探究一下数组内元素地址和内存地址之间的关系:

#include <stdio.h>

int main() {

    int arr[3] = {77777, 88888, 99999};           // 定义一个长度为3 的数组,取名为:arr
int *pa = arr; // 创建一个指针变量指向 arr 数组 printf("arr 数组的指向:%p , pa变量的指向:%p \n", arr, pa);
printf("数组第一个元素指的地址 = %p , 第一个元素值 = %d \n", &pa[0], *(pa));
printf("数组第二个元素指的地址 = %p , 第二个元素值 = %d \n", &pa[1], *(pa + 1));
printf("数组第三个元素指的地址 = %p , 第三个元素值 = %d \n", &pa[2], *(pa + 2));
printf("\n"); int *p = &pa[0]; // 将数组下标为0的第一个元素内存地址赋值给 p指针变量
printf("p指针:%p \n" , p); return 0;
}

这里将数组下标为0的第一个元素内存地址赋值给 p指针变量,可以看到 p 指针变量和 arr变量 、pa 指针变量指向的都是一块内存地址。

而这块内存地址保存着正是数值 77777  这个元素。

我们都知道在传统 x86 / x64 系统中,像 C/C++、JAVA 这类编译语言中 , int 类型一般是占用 4个字节,每个字节 8位,既32位保存一个 int 类型的数字。

像 int 类型占 4个字节,我们称为多字节变量而像 char 这种字符类型,只需要 1个字节即8位可以保存一个元素,我们称为单字节变量。

我们现在直接说结论:

  • 变量地址:每个变量都存储在内存中,而且每个变量都有一个起始地址,这通常是该变量的第一个字节的地址,例如,如果你有一个 int 类型的变量 x,那么&x将给出这个变量的起始地址。
  • 多字节变量:对于只需要一个字节的数据类型(例如 char),变量的地址与其唯一的字节的地址相同。但对于需要多个字节的数据类型(例如 int, float, double 等),变量的地址是这些连续字节的第一个字节的地址。

演示代码:

#include <stdio.h>

int main() {

    int arr[3] = {77777, 88888, 99999};           // 定义一个长度为3 的数组,取名为:arr
int *pa = arr; // 创建一个指针变量指向 arr 数组 printf("arr 数组的指向:%p , pa变量的指向:%p \n", arr, pa);
printf("数组第一个元素指的地址 = %p , 第一个元素值 = %d \n", &pa[0], *(pa));
printf("数组第二个元素指的地址 = %p , 第二个元素值 = %d \n", &pa[1], *(pa + 1));
printf("数组第三个元素指的地址 = %p , 第三个元素值 = %d \n", &pa[2], *(pa + 2));
printf("\n"); int *p = &pa[0]; // 将数组下标为0的第一个元素内存地址赋值给 p指针变量
printf("p指针:%p \n" , p); char* charPer = (char*)p; // 将数组第一个元素 77777 指针转换成 char 指针,4个字节 转换成 1个字节,对每个字节进行遍历 printf("指针变量p的值: %d\n", *p);
printf("指针变量p的地址(起始字节的地址): %p\n", charPer); printf("\n遍历指针变量p的每个字节:\n"); // 遍历int变量的每一个字节
for (size_t i = 0; i < sizeof(*p); i++) {
printf("字节 %zu 地址: %p -> 值: %x\n", i+1, charPer + i, (unsigned char)charPer[i]);
} printf("\n" );
// 输出 x 16进制的内存地址
printf("指针变量p地址16进制: = %X" ,*p); printf("\n" ); return 0;
}

通过上面代码可以发现,int 类型的元素内存占4个字节,变量指向的是第一个字节的内存地址。

本篇博文总结出一个主要的观点:

  • 在C语言中,数组是一块连续的内存空间,数组变量名指向第一个元素的内存地址。
  • 数组第一个元素指又指向第一个字节的内存地址。
  • 即数组内存地址 = 数组第一个元素的第一个字节的内存地址。 

扩展内容,上面遍历每个字节的内存地址是在小端序Linux系统上输出的结果:

遍历指针变量p的每个字节:
字节 1 地址: 00000068a7fffa74 -> 值: d1
字节 2 地址: 00000068a7fffa75 -> 值: 2f
字节 3 地址: 00000068a7fffa76 -> 值: 1
字节 4 地址: 00000068a7fffa77 -> 值: 0 指针变量p地址16进制: = 12FD1

如果是在像 AIX 这种大端序的系统上输出的结果可能正好相反

模拟 AIX系统上输入的结果:

字节 1 地址: [相应的内存地址] -> 值: 0
字节 2 地址: [相应的内存地址 + 1] -> 值: 1
字节 3 地址: [相应的内存地址 + 2] -> 值: 2f
字节 4 地址: [相应的内存地址 + 3] -> 值: d1 字节 1 地址: 00000068a7fffa74 -> 值: 0
字节 2 地址: 00000068a7fffa75 -> 值: 1
字节 3 地址: 00000068a7fffa76 -> 值: 2f
字节 4 地址: 00000068a7fffa77 -> 值: d1 指针变量p地址16进制: = 12FD1

在小端序系统(Linux系统)中,数据的最低有效字节存储在最低的内存地址,而最高有效字节存储在最高的内存地址

而在大端序系统中,情况正好相反:数据的最高有效字节存储在最低的内存地址,而最低有效字节存储在最高的内存地址。

之前我知道这个系统有分大小端字节序之间的区别,但是一直不理解这个内存字节高低位置是个什么意思,通过学习C语言,也算是搞清楚这里面的逻辑。

探讨C语言中数组、元素内存地址之间的关系的更多相关文章

  1. 搬运1:关于对C语言中数组名取地址加减等操作的一点探究

    对于数组名取地址强制转换的操作 偶然在晚上学了C语言指针后网页闲逛找题时,被一个数组名取地址搞糊涂了,在自己试验加探索后我稍微悟了一点东西. 代码如下: #include<stdio.h> ...

  2. C语言中指针和取地址符&的关系

    一 概念定义: 严格说起来,应该这么讲:指针存的是地址,而&运算符是取变量的地址. 指针原理: 其实计算机中的内存就像一个个抽屉(一兆内存就相当于1百万个抽屉),每个抽屉都有一个编号,用于存放 ...

  3. c语言中数组相关问题

    c语言中数组相关问题: 1.数组基本定义: 相同数据类型的元素按一定顺序排列的集合,就是把有限个类型相同的变量用一个名字命名,然后用编号区分他们的变量的集合,这个名字称为数组名,编号称为下标.组成数组 ...

  4. Java 中数组的内存分配

    Java 中数组的内存分配 1.Java 程序在运行时,需要在内存中分配空间.为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据和内存管理方式. 2.数组基本概念 数组是 ...

  5. 对于C语言中数组名是指针的理解

    我们都知道,c语言中数组名是一个指针,比如下面这段代码 #include<iostream>using namespace std;int main(){ int a[4]={1,2,3, ...

  6. C语言中数组名作为参数进行函数传递

    用数组名作函数参数与用数组元素作实参有几点不同. 1) 用数组元素作实参时,只要数组类型和函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的.因此,并不要求函数的 ...

  7. C语言精要总结-内存地址对齐与struct大小判断篇

    在笔试时,经常会遇到结构体大小的问题,实际就是在考内存地址对齐.在实际开发中,如果一个结构体会在内存中高频地分配创建,那么掌握内存地址对齐规则,通过简单地自定义对齐方式,或者调整结构体成员的顺序,可以 ...

  8. C语言中数组使用负数值的标记

    ·引 对数组的认知 在c语言中,我们经常使用的一个结构便是数组,在最开始学习数组的时候,它被描述成这样(以一维二维数组为例):一维数组是若干个数连续排列在一起的集合,我们可以通过0-N的标记(N为数组 ...

  9. 别人不会给你说的---C语言中数组名和指针的区别 及 sizeof用法

    引自: http://blog.csdn.net/tianyue168/article/details/5781924 #i nclude <iostream.h> int  main( ...

  10. C语言中数组定义方式

    <1>前言 大家首先来思考一个问题,若是我们想要定义两个变量,求这两个数的平均数,该怎么求呢? 例如:int a = 10,b = 20 int average = (a + b) / 2 ...

随机推荐

  1. 【MAUI Blazor踩坑日记】3.Windows标题栏自定义颜色,运行时改变颜色

    目录 修改默认颜色 修改运行时颜色 效果图 MAUI中Windows默认的标题栏颜色是灰色的,有一点丑. 如果去掉默认的标题栏,自己画一个,可能会出现问题,也比较麻烦. 想要自定义默认标题栏的颜色,官 ...

  2. java无法加载maper.xml问题

    项目依赖其他模块,模块中有 mapper,本项目也有mapper,导致项目无法正常运行. 解决办法: 1.配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.X ...

  3. 解决pandas 读取csv文件报错

    使用encoding参数: pd.read_csv(path,sep=",",encoding='utf-16') 注意:该参数之后的编码格式,并不是固定的,需要用记事本打开csv ...

  4. 2023-07-31:用r、e、d三种字符,拼出一个回文子串数量等于x的字符串。 1 <= x <= 10^5。 来自百度。

    2023-07-31:用r.e.d三种字符,拼出一个回文子串数量等于x的字符串. 1 <= x <= 10^5. 来自百度. 答案2023-07-31: 大体步骤如下: 1.初始化一个字符 ...

  5. 去中心化组件共享方案 —— Webpack Module Federation(模块联邦)

    在大型应用中, 我们可能会对其进行拆分,分成容器.主应用和多个子应用,使拆分后的应用独立开发与部署,更加容易维护.但无论是微应用.公共模块应用,都需要放到容器中才能使用. 如果多个应用之间希望资源共享 ...

  6. debian11安装mysql5.7

    前言 mysql官网5.7版本的只找到debian10的,没有debian11的,试了下也能用. 系统版本:debian 11 mysql版本:5.7.35 步骤 下载bundle的tar包.官网地址 ...

  7. C# 中关于 T 泛型【C# 基础】

    〇.前言 C# 里面的泛型不仅可以使用泛型函数.泛型接口,也可以使用泛型类.泛型委托等等.在使用泛型的时候,它们会自行检测你传入参数的类型,因此它可以为我们省去大量的时间,不用一个个编写方法的重载.与 ...

  8. SpringBoot3集成Kafka

    目录 一.简介 二.环境搭建 1.Kafka部署 2.Kafka测试 3.可视化工具 三.工程搭建 1.工程结构 2.依赖管理 3.配置文件 四.基础用法 1.消息生产 2.消息消费 五.参考源码 标 ...

  9. VScode软件的安装以及C/C++环境配置的方法

    今天和大家分享一下VScode软件的安装以及C/C++环境配置的方法.手把手教大家入门. 1,下载VScode编译器 (1)    官网下载链接:https://code.visualstudio.c ...

  10. ES集群&kibana安装

    一.elasticsearch介绍 Elasticsearch 是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful 风格接口,多数据源,自动搜索负 ...