C语言----整数在内存中的存储

C语言——整数的存储

这里说的整数主要是指:short 、 char和int类型的数字。本文将详细介绍整数在内存中的存储方式和运算逻辑。

这里首先说明32位机器和64位机器代表的含义:32和64表示内存中地址线的个数。32位表示内存共有2^{_{32}}个地址,每个地址都可以存储一个字节(B)的数据,故32位系统最大可支持的内存大小=2^{32}\div 1024\div 1024=4096Mb,也就是4G内存。

我们常用的整数数据类型有:char(1B)、short(2B)和int(4B)型。而short、char和int型的数字主要分为二类(有符号和无符号)。

char a; 等效于 signed char a;           定义无符号char 型:unsigned char a;       

short a; 等效于 signed short a;           定义无符号short 型:unsigned short a; 

int a; 等效于 signed int a;           定义无符号int 型:unsigned int a; 

我们都知道在内存中存储的数据,都是以字节为单位进行存储。并且任何数据在内存中都被存储成0 1组成的二进制序列。如果是有符号数:则其二进制的最高位为符号位(符号位0表示正数,1表示负数)。

那么数据的地址是怎么分配的?

对于一个简单的程序:

int a=0x11223344;
int b=0x01020304;

a和b在被存储到内存中时,a和b的地址是系统随机分配的吗?二者又有什么关系呢?

这里先给出答案:a和b的地址并不是系统随机取分配的,并且a和b的地址之间存在联系。下面将通过测试给出验证。(本人使用的C语言编译器为Visual Stdio2019)

我们首先验证a和b的地址并不是系统随机分配的。在验证之前我们首先学习二个名词:大端(大端字节序)和小端(小端字节序)。

大端(大端字节序):指数据的低位保存在内存的高地址中。

小端(小端字节序):指数据的低位保存在内存的低地址中。

注释:(1)什么是数据的低位:一个简单的例子是,一个十进制数123,它的最高位是1还是3? 所以,写出来的数据,右边的数据就是低位数据。(比如说一个字节的二进制数:1000 0001,左边的1就是最高位,右边的1就是最低位)。

(2)内存的低地址:上文中说到32位系统的内存有2^{32}个内存地址,这些地址如果用二进制进行编号就是:0x00 00 00 00-0x 11 11 11 11(用二进制需要写32个0或1,这里用16进制表示),这些地址中,我们默认0x 00 00 00 00就是最低的地址,0x 11 11 11 11就是最高的地址。

有了上述概念后,那么a的数据在内存中的分别是怎样的?我们的系统到底是大端还是小端呢?下面编写代码进行验证。

这里需要注意:大端小端针对的是编译器,即同一台电脑,不同的编译器,得到的结果可能不同。(这里我们验证Visual Stdio是大端还是小端)

验证1:数据在内存中是怎么放的?

验思路,程序中查看a b内容的地址分布。

#include < stdio.h > //验证代码
int main()
{
	int a = 0x11223344;
	int b = 0x01020304;
	return 0;
}
a的地址
a在内存中的地址

                       图1

a b的地址
b在内存中的地址

 

                          图2

      由图1可以看出,a的四个字节内容在内存中的分布是连续的,并且a的低位(44)对应的内存地址是最低的。使用Visual Stdio是小端字节序。                                                                              由图2可以看出,b的内容在内存中的分别也符合小端字节序。并且b的起始地址(0x010FFA78)比a的起始地址(0x010FFA84)要低。在程序中,定义a要比定义b早,所以变量在内存中存储时,先使用高地址内存。为了更直观,下面上图说明。                                                                                                                                                                                                               
变量在内存中存储流程
               

验证(2):变量a的起始内存地址和变量b的最高位内存地址并不是连续的,程序中我们定义a和b是连续的,中间并没有穿插其它程序。有一种自欺欺人的解释是:我们的电脑同时可以干很多工作,a和b中间的内存是不是被其它未知程序给占用了? 

验证这一点也很容易,我们定义三个变量(int a=0x11223344;  int b=0x01020304; int c=0x11223344;),如果这三个变量之间间隔的内存空间大小是随机的(即a和b中间间隔m个字节,b和c中间间隔n个字节。如果m和n都是随机的,每次运行程序m和n的值都不一样,则说明a和b中间的内存是被其它未知程序给占用了)

#include < stdio.h >
int main()
{
	int a = 0x11223344;
	int b = 0x01020304;
	int c = 0x11223344;
	return 0;
}
变量a b c在内存中地址

       由左图可以看出,c与b之间间隔8个字节,a与b间隔8个字节。并且本人共测试5次,每次的结果都是一致的。所以程序中连续定义的变量在内存中的地址是存在联系的。但其规律并不好把控,如果a b c 都是整型,他们的间隔是8个字节。如果a还是int型,将b改为char型,c改为short型,那么a b间隔11个字节,b c间隔8个字节,并没有明显的规律性。  为了搞明白这个问题,我去百度。 百度的结果说:不要试图理解编译器的行为。(好吧!)

     数据在内存中的存储方式与编译器有关,那么研究相邻变量的地址关系是不是就没用了呢?  下面我将列举一个经典的例题。

#include <stdio.h>
int main()
{
    int i = 9; //定义为9 方便在内存中查看
    int arr[] = { 1,2,3,4,5,6,7,8,9,10};
    for (i = 0; i <= 12; i++)  
    {
        arr[i] = 0;
        printf("hello bit\n");
    }
    return 0;
}

请问:程序的运行结果是什么?

先公布答案:死循环。

为什么是死循环。首先我们需要明确一点:数组访问在逻辑上不能越界(即,定义一个arr[10],那么逻辑上我们最多访问10个元素,这样才能保证程序不出问题),但实际上是可以越界访问数组的。为了说明这一点,我们查看变量i和数组arr在运行时内存数值的变化。

   这里需要说明的是:arr中元素的数据类型为int,使用arr[i]和arr[i-1]的内存地址相差4个字节(这是因为int型数据在内存中占4个字节的空间)     

从图1中可以看出,i变量的起始地址与arr的最高地址(arr[9]的地址)相差8个字节。这是因为i和arr都是int型变量,在Visual Studio编译环境下,二个相邻的int 型变量之间间隔8个字节(这一点上面已经试验验证过了)。从图2可以看出,当i=12时,程序运行arr[12]=0;居然将i的内容清零了。这也是程序死循环的根本原因。

        实际上,将程序改为:

#include <stdio.h>
int main()
{
    int i = 9;
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    for (i = 0; i <= 11; i++) 
    {
        arr[i] = 0;
        printf("hello bit\n");
    }
    return 0;
}

程序会打印出11个hello bit,并没有陷入死循环。

如果将程序改为:

#include <stdio.h>
int main()
{
   
    int arr[] = { 1,2,3,4,5,6,7,8,9,10};
    int i = 0;
    for (i = 9; i >= -3; i--)  //64位编译器  i的地址为arr[end]+3
    {
        arr[i] = 9;
        printf("hello bit\n");
    }
    return 0;
}

   程序又会陷入死循环,原理是一样的。因为先定义arr后定义i,使用i的最高位地址与arr的起始地址相差8个字节,并且i的内存地址比arr低,所以当程序运行到arr[-3]=9;时,i的值又被初始化,所以才会陷入死循环。                                                                                                                                                                                                                                                                                                                                 

结论:

(1)内存为变量分配地址时,总是从高地址开始分配。并且分配空间的大小取决于变量的数据类型。

(2)在Visual Studio编译环境下,当一个变量获得一块内存空间后,变量的低位数据从内存的低地址开始存储(VS编译器是小端字节序)

本人的下一篇博客将介绍整数在内存中的运算。主要内容有:整形提升、char、short 和int型数据的运算(+ -)在内存中是如何实现的。

 

 

 

 

 

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值