在C编程期间如何存储数据?

时间:2018-03-23 06:31:53

标签: c int storage

一个例子;

main()
{
    int x=123;
}

所以,xint类型变量,为int分配了2个字节(当前假设,这可能因机器而异。

我们可以说,地址20002001已分配给x。那么,如何使用这些地址存储数据123? 我是C的初学者,所以简单的语言会有所帮助。

我指的是E Balagurusamy [Mc Graw Hill Education]的“计算基础和C语言程序设计”。

2 个答案:

答案 0 :(得分:9)

当我考虑在C中存储变量时,我主要考虑与机器无关的盒子。所以给出了

int x = 123;

我的第一个念头就是它看起来像这样:

   +-----------+
x: |    123    |
   +-----------+

由于这是一个局部变量,我知道这个小盒子在堆栈上。 (更多内容见下文。)

现在,您询问了各个字节,并且您想象从地址int开始的双字节2000。所以这里的内容将更详细。为了强调各个字节的内容,我将切换到十六进制:

         +------+
x: 2000: | 0x7b |
         +------+
   2001: | 0x00 |
         +------+

你可能已经想到了这一点,但是这个数字0x7b123的十六进制表示的一部分。十进制数123具有十六进制表示0x007b。这确实假设一个两字节的整数,虽然值得注意的是,这些天你可能使用的大多数机器将使用四个字节。我还展示了“小端”存储的情况,这是大多数机器今天使用的惯例。较低编号的字节是最不重要的字节。

由于123实际上只是一个7位数字,因此它只占用两个字节中的一个,另一个为零。为了确定我们理解两个字节是如何布局的,假设我们为x:

分配了一个新值
x = 12345;

12345的十六进制表示形式为0x3039,因此内存中的图片更改为:

         +------+
x: 2000: | 0x39 |
         +------+
   2001: | 0x30 |
         +------+

最后,为了比较,假设我说

long int y = 305419896;

假设这是在具有相反的big-endian字节顺序的机器上。假设long int是四个字节,并假设编译器选择将y放在地址3000处。那个随机数字305419896具有十六进制表示0x12345678,所以内存中的情况(再次,假设大端字节顺序)看起来像这样:

         +------+
y: 3000: | 0x12 |
         +------+
   3001: | 0x34 |
         +------+
   3002: | 0x56 |
         +------+
   3003: | 0x78 |
         +------+

对于big-endian存储,低位地址包含最重要的字节,这意味着该数字在内存中从左向右(此处从上到下)读取。但正如我所说,你今天可能使用的大多数机器都是小端的。

正如我所提到的,由于示例中的x是局部变量,因此它通常存储在函数调用堆栈中。 (正如一位评论者所指出的,不能保证C甚至有一个堆栈,但大多数都是这样,所以让我们继续这个假设。)看看局部变量和全局变量之间的区别,并展示其他几种数据类型是如何形成的。存储,让我们看一个稍微大一点的例子,让我们扩展我们的范围来想象所有的内存。假设我写了

int g = 456;
char s[] = "hello";

int main() {
    int x = 123;
    char s2[] = "world";
    char *p = s;
}

通常,全局变量存储在内存的下半部分,而堆栈存储在“顶部”,然后逐渐减少。所以我们可以想象我们的计算机内存看起来像这样。正如你所看到的,我已经颠倒了我在前面图片中使用的惯例。在此图片中,内存地址 up 页面。 (此外,内存地址现在也是十六进制的,我正在删除0x符号。此外,我将回到little-endian字节顺序,但保留了16位机器的概念。我要将字符值显示为自己,而不是十六进制。此外,我将未知字节显示为??。)

          +------+
    ffec: |  ??  |
          +------+
    ffeb: |  00  |
          +------+
x:  ffea: |  7b  |
          +------+
    ffe9: |  00  |
          +------+
    ffe8: | 'd'  |
          +------+
    ffe7: | 'l'  |
          +------+
    ffe6: | 'r'  |
          +------+
    ffe5: | 'o'  |
          +------+
s2: ffe4: | 'w'  |
          +------+
    ffe3: |  02  |
          +------+
p:  ffe2: |  80  |
          +------+
    ffe1: |  ??  |
          +------+
             .
             .
             .
          +------+
    0282: |  ??  |
          +------+
    0281: |  00  |
          +------+
    0280: | 'o'  |
          +------+
    0283: | 'l'  |
          +------+
    0282: | 'l'  |
          +------+
    0281: | 'e'  |
          +------+
s:  0280: | 'h'  |
          +------+
    0283: |  01  |
          +------+
g:  0282: |  c8  |
          +------+
    0281: |  ??  |
          +------+

这当然是一个巨大的简化,但它应该给你一个基本的想法,我仍然觉得以这种方式思考它是有用的,尽管评论者正在忙着讨论的所有深奥的可能的复杂性。下一步可能是展示malloc内存的外观,以及当我们在堆栈上激活了多个函数调用时的情况,但是这个答案太长了,所以我们将保存为另一天。

答案 1 :(得分:6)

您不应该关心如何存储数据,但您应该考虑您的程序的行为(因此请多考虑semantics)。

你的代码错了。您至少需要int main(void)

请注意编译器和optimizations可能的"as-if" rule

您的编译器可以:

  • 完全忘记变量。在您的示例中,x没有可观察到的影响,编译器可以删除int x = 123;

  • 仅将变量存储在某些processor register中(因此该变量不在内存中并且没有内存地址)

  • 将变量放在call stack当前堆栈帧的某个插槽中(或者可能是内存中的其他位置)。

  • 等......(包括之前案例的一些组合)

当然,如果在printf("x=%d\n", x);定义side-effect之后添加一些可观察的automatic variable(可能是int x = 123;作为语句,编译器将处理该变量非常不同。

C标准(读n1570)不仅指定(英文)语法,还指定C编程语言的语义,即程序的可观察行为。一个重要且棘手的概念是undefined behavior(UB;你问题中的玩具程序没有任何UB,但你很快就会编写有一些UB的错误程序,你需要学习如何避免UB)。是UB的scared

有些副作用无关紧要。例如,加热处理器。

当前的实现(编译器和系统)可以帮助您理解程序的行为。我建议使用所有警告和调试信息进行编译(gcc -Wall -Wextra -g GCC}和using the gdb debugger