【问题标题】:How is memory allocated in c , and why diffrence between two contiguous is always 4?c 中的内存是如何分配的,为什么两个连续的内存之间的差异总是 4?
【发布时间】:2017-04-17 12:27:49
【问题描述】:
#include <stdio.h>
#include <cs50.h>

int main (void)
{
    int *x;
    x = malloc(sizeof(long long)*3);
    scanf("%i %i %i",x, (x+1), (x+2));
    printf("%i\t %i\t %i\n",(int)x, (int)(x+1), (int)(x+2));
    printf("%i\t %i\t %i\n",*x, *(x+1), *(x+2));
    free(x);
}

这个程序对于输入 12,2,3 的输出是:

43171856         43171860        43171864
12       2       3

所以,我的问题是为什么地址之间的差异在每种情况下都是 4, 如果*x 指向43171856 那么*(x+1) 应该指向4317185 而不是43171860sizeof(long long) 也是 8 字节,所以分配的内存如何在 4 字节之间分配 8 字节在 4317185643171860 之间。

【问题讨论】:

    标签: c pointers malloc dynamic-memory-allocation cs50


    【解决方案1】:

    首先,在你的代码中

     printf("%i\t %i\t %i\n",(int)x, (int)(x+1), (int)(x+2));
    

    调用实现定义的行为,因为您试图将指针强制转换为整数。

    如果要打印指针

    • 使用%p 格式说明符
    • 将参数转换为void *

    也就是说,指针算术尊重数据类型。您已将 x 声明为指向 int 的指针,因此任何指针算法都将基于 sizeof(int),无论在您的平台上计算为什么。

    引用C11,第 §6.5.6/P8 章,(强调我的

    当一个整数类型的表达式被添加到指针或从指针中减去时, 结果具有指针操作数的类型。 如果指针操作数指向一个元素 一个数组对象,并且数组足够大,结果指向一个元素的偏移量 原始元素使得结果和原始的下标的差异 数组元素等于整数表达式。换句话说,如果表达式P 指向 数组对象的i-th 元素,表达式(P)+N(等效于N+(P))和 (P)-N(其中N 的值为n)分别指向i+n-th 和i−n-th 元素 数组对象,前提是它们存在。 [....]

    在你的代码中,你写了

     x = malloc(sizeof(long long)*3);
    

    这是错误的。在这种的情况下,你可能更安全,因为sizeof(long long)&gt;= sizeof(int),但对于任何任意类型都不是这样。

    • 最好的情况:你最终会浪费内存。
    • 最坏的情况:您最终会访问超出范围(无效)的内存。

    写这个的更好和首选的方式是

     x = malloc(sizeof*x * 3);   //sizeof is not a function :)
    

    然后,检查malloc() 是否成功。这会分配所需的确切内存量,不多也不少。

    【讨论】:

      【解决方案2】:

      这是 C 中真正令人困惑的位之一:x+1,当x 具有指针类型时,将x 的数值增加sizeof(*x) 增加1 .

      必须如此,因为对于任何指针类型T *xx+1&amp;x[1] 相同。 &amp;x[1]x 指向的伪数组中第二个T 的地址。因此x+1 的数值必须等于x 的数值加上sizeof(T),在你的例子中是4。

      malloc 同时不知道你通过了3*sizeof(long long)。它看到malloc(24),它给你24个字节,(在你的平台上)是六个ints。您只使用前三个,这很好,只是浪费了一点内存。你可能打算写3*sizeof(int)

      【讨论】:

        【解决方案3】:

        您使用的 int* 通常是 32 位,因此是 4 个字节。 尝试使用 long long *x 代替? 正如你所说,分配的 8*3 字节只使用了其中的 4*3 字节。 每个偏移量,即 (x+1) 仅偏移一个 int 的大小。

        【讨论】:

          【解决方案4】:

          C 2011 Online Draft

          6.3.2.1 左值、数组和函数指示符
          ...
          3 除非它是 sizeof 运算符、_Alignof 运算符 或 一元 &amp; 运算符,或者是用于初始化数组的字符串文字,具有 type ''array of type'' 被转换为类型为''pointer to type'' 的表达式,它指向 到数组对象的初始元素并且不是左值。如果数组对象有 注册存储类,行为未定义。

          在大多数情况下,“T 的数组”类型的 表达式 将被转换(“衰减”)为“指向 T 的指针”的表达式,并且表达式将是数组第一个元素的地址。

          AFAIK,_Alignof 运算符子句是在线草案中的错误,已在官方™ 中更正,而不是免费提供的标准,这就是上面引用中删除它的原因。

          6.5.2 数组下标
          ...
          2    后缀表达式后跟方括号中的表达式 <strong>[]</strong> 是下标 指定数组对象的元素。下标运算符[]的定义 是 E1[E2](*((E1)+(E2))) 相同。由于转换规则 适用于二元 + 运算符,如果 E1 是一个数组对象(等效地,指向 数组对象的初始元素)并且 E2 是整数,E1[E2] 指定 E2-th E1 的元素(从零开始计数)。

          给定一个T 类型的数组a 和一个整数i,表达式a[i] 等价于(定义为)*(a + i) - 给定地址a,偏移量i T 类型的>元素不是字节)并取消引用结果。

          如果a 是一个数组或指针表达式,而i 是一个整数表达式,那么a[i]i[a] 将产生相同的结果。

          6.5.6 加法运算符
          ...
          8    将整数类型的表达式添加到指针或从指针中减去时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果和原始数组元素的下标之差等于整数表达式。换句话说,如果表达式 P 指向 数组对象的第 i 个元素,表达式 <strong>(P)+N</strong>(等效于 <strong>N+(P)</strong>)和 <strong>(P)-N</strong>(其中<strong>N</strong> 的值为n)分别指向i+n-th 和i -n-数组对象的第一个元素,前提是它们存在。此外,如果表达式<strong>P</strong> 指向最后一个 数组对象的元素,表达式<strong>(P)+1</strong> 指向数组对象的最后一个元素,如果表达式<strong>Q</strong> 指向数组对象的最后一个元素,则表达式<strong>(Q)-1</strong> 指向最后一个元素数组对象。如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则计算不应产生溢出;否则, 行为未定义。如果结果指向数组对象的最后一个元素,则不应将其用作评估的一元 * 运算符的操作数。

          如果p 是一个指向T 类型对象的指针,那么表达式p + 1 会产生该类型的下一个对象 的地址。如果sizeof (T) 为1,则p + 1 将地址加1。如果sizeof (T) 为4,则p + 14 添加到地址。

          类似地,表达式++pp++ 前进p 以指向T 类型的下一个对象

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-11-06
            • 1970-01-01
            • 2010-10-12
            • 1970-01-01
            相关资源
            最近更新 更多