c 中的内存是如何分配的,为什么两个连续的内存之差总是 4?
How is memory allocated in c , and why diffrence between two contiguous is always 4?
#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
而不是 43171860
? sizeof(long long)
也是 8
字节,因此分配的内存如何在 43171856
和 43171860
之间的 4
字节之间分配 8
字节。
首先,在你的代码中
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,(强调我的)
When an expression that has integer type is added to or subtracted from a pointer, the
result has the type of the pointer operand. If the pointer operand points to an element of
an array object, and the array is large enough, the result points to an element offset from
the original element such that the difference of the subscripts of the resulting and original
array elements equals the integer expression. In other words, if the expression P
points to
the i
-th element of an array object, the expressions (P)+N
(equivalently, N+(P)
) and
(P)-N
(where N
has the value n
) point to, respectively, the i+n
-th and i−n
-th elements of
the array object, provided they exist. [....]
在你的代码中,你写了
x = malloc(sizeof(long long)*3);
这是错误的。在 这种 情况下,您可能更安全,因为 sizeof(long long)
是 >=
sizeof(int)
,但对于任何任意类型都不是这样。
- 最好的情况:你最终会浪费内存。
- 最坏情况:您最终会访问越界(无效)内存。
一个更好的首选写法是
x = malloc(sizeof*x * 3); //sizeof is not a function :)
然后,检查 malloc()
是否成功。这会分配所需的确切内存量,不多也不少。
这是 C 的真正令人困惑的位之一:x+1
,当 x
具有指针类型时,将 x
的数值递增 sizeof(*x)
, 不是 1.
必须这样,因为对于任何指针类型 T *x
,x+1
与 &x[1]
相同。 &x[1]
是x
指向的伪数组中第二个T
的地址。因此 x+1
的数值必须等于 x
的数值加上 sizeof(T)
,在你的例子中是 4.
malloc
同时,不知道你通过了3*sizeof(long long)
。它看到 malloc(24)
并给你 24 个字节,(在你的平台上)是 six int
s。您只使用了前三个,这很好,只是浪费了一点内存。您可能打算写 3*sizeof(int)
.
您使用的是 int*,它通常是 32 位,因此是 4 个字节。
尝试使用 long long *x 代替?
正如你所说,你已经分配了 8*3 个字节,字节只使用了其中的 4*3 个字节。
每个偏移量即 (x+1) 仅偏移一个 int 的大小。
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof
operator, the _Alignof
operator, or the
unary &
operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
在大多数情况下,类型 "array of T
" 的 表达式 将被转换 ("decay") 为 "pointer to T
" 的表达式,并且表达式的值将是数组第一个元素的地址。
AFAIK,_Alignof
运算符子句是在线草案中的一个错误,已在官方™ 中更正,而不是免费提供的标准,这就是为什么它在上面的引用中被删除的原因。
6.5.2 Array subscripting
...
2 A postfix expression followed by an expression in square brackets <strong>[]</strong>
is a subscripted
designation of an element of an array object. The definition of the subscript operator []
is that E1[E2]
is identical to (*((E1)+(E2)))
. Because of the conversion rules that
apply to the binary +
operator, if E1
is an array object (equivalently, a pointer to the
initial element of an array object) and E2
is an integer, E1[E2]
designates the E2
-th
element of E1
(counting from zero).
给定一个 T
类型的数组 a
和一个整数 i
,表达式 a[i]
等价于(定义为)*(a + i)
- 给定地址 a
,偏移量 i
elements 类型 T
(not bytes)从那个地址开始取消引用结果。
如果a
是一个数组或指针表达式,而i
是一个整数表达式,那么a[i]
和i[a]
将产生相同的结果。
6.5.6 Additive Operators
...
8 When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P
points to
the i-th element of an array object, the expressions <strong>(P)+N</strong>
(equivalently, <strong>N+(P)</strong>
) and
<strong>(P)-N</strong>
(where <strong>N</strong>
has the value n) point to, respectively, the i+n-th and i−n-th elements of the array object, provided they exist. Moreover, if the expression <strong>P
points to the last
element of an array object, the expression <strong>(P)+1</strong>
points one past the last element of the array object, and if the expression <strong>Q</strong>
points one past the last element of an array object, the expression <strong>(Q)-1</strong>
points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the
behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary *
operator that is evaluated.
如果 p
是指向类型 T
的对象的指针,则表达式 p + 1
产生 下一个对象的地址 那种类型的。如果 sizeof (T)
为 1,则 p + 1
将地址加 1。如果 sizeof (T)
为 4,则 p + 1
将 4 添加到地址。
类似地,表达式++p
和p++
提前p
指向类型T
的下一个对象。
#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
而不是 43171860
? sizeof(long long)
也是 8
字节,因此分配的内存如何在 43171856
和 43171860
之间的 4
字节之间分配 8
字节。
首先,在你的代码中
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,(强调我的)
When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression
P
points to thei
-th element of an array object, the expressions(P)+N
(equivalently,N+(P)
) and(P)-N
(whereN
has the valuen
) point to, respectively, thei+n
-th andi−n
-th elements of the array object, provided they exist. [....]
在你的代码中,你写了
x = malloc(sizeof(long long)*3);
这是错误的。在 这种 情况下,您可能更安全,因为 sizeof(long long)
是 >=
sizeof(int)
,但对于任何任意类型都不是这样。
- 最好的情况:你最终会浪费内存。
- 最坏情况:您最终会访问越界(无效)内存。
一个更好的首选写法是
x = malloc(sizeof*x * 3); //sizeof is not a function :)
然后,检查 malloc()
是否成功。这会分配所需的确切内存量,不多也不少。
这是 C 的真正令人困惑的位之一:x+1
,当 x
具有指针类型时,将 x
的数值递增 sizeof(*x)
, 不是 1.
必须这样,因为对于任何指针类型 T *x
,x+1
与 &x[1]
相同。 &x[1]
是x
指向的伪数组中第二个T
的地址。因此 x+1
的数值必须等于 x
的数值加上 sizeof(T)
,在你的例子中是 4.
malloc
同时,不知道你通过了3*sizeof(long long)
。它看到 malloc(24)
并给你 24 个字节,(在你的平台上)是 six int
s。您只使用了前三个,这很好,只是浪费了一点内存。您可能打算写 3*sizeof(int)
.
您使用的是 int*,它通常是 32 位,因此是 4 个字节。 尝试使用 long long *x 代替? 正如你所说,你已经分配了 8*3 个字节,字节只使用了其中的 4*3 个字节。 每个偏移量即 (x+1) 仅偏移一个 int 的大小。
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of thesizeof
operator,the, or the unary_Alignof
operator&
operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
在大多数情况下,类型 "array of T
" 的 表达式 将被转换 ("decay") 为 "pointer to T
" 的表达式,并且表达式的值将是数组第一个元素的地址。
AFAIK,_Alignof
运算符子句是在线草案中的一个错误,已在官方™ 中更正,而不是免费提供的标准,这就是为什么它在上面的引用中被删除的原因。
6.5.2 Array subscripting
...
2 A postfix expression followed by an expression in square brackets<strong>[]</strong>
is a subscripted designation of an element of an array object. The definition of the subscript operator[]
is thatE1[E2]
is identical to(*((E1)+(E2)))
. Because of the conversion rules that apply to the binary+
operator, ifE1
is an array object (equivalently, a pointer to the initial element of an array object) andE2
is an integer,E1[E2]
designates theE2
-th element ofE1
(counting from zero).
给定一个 T
类型的数组 a
和一个整数 i
,表达式 a[i]
等价于(定义为)*(a + i)
- 给定地址 a
,偏移量 i
elements 类型 T
(not bytes)从那个地址开始取消引用结果。
如果a
是一个数组或指针表达式,而i
是一个整数表达式,那么a[i]
和i[a]
将产生相同的结果。
6.5.6 Additive Operators
...
8 When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expressionP
points to the i-th element of an array object, the expressions<strong>(P)+N</strong>
(equivalently,<strong>N+(P)</strong>
) and<strong>(P)-N</strong>
(where<strong>N</strong>
has the value n) point to, respectively, the i+n-th and i−n-th elements of the array object, provided they exist. Moreover, if the expression<strong>P
points to the last element of an array object, the expression<strong>(P)+1</strong>
points one past the last element of the array object, and if the expression<strong>Q</strong>
points one past the last element of an array object, the expression<strong>(Q)-1</strong>
points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary*
operator that is evaluated.
如果 p
是指向类型 T
的对象的指针,则表达式 p + 1
产生 下一个对象的地址 那种类型的。如果 sizeof (T)
为 1,则 p + 1
将地址加 1。如果 sizeof (T)
为 4,则 p + 1
将 4 添加到地址。
类似地,表达式++p
和p++
提前p
指向类型T
的下一个对象。