malloc()能否用于定义数组的大小?

3

以下是一段代码示例:

int *a = malloc(sizeof(int) * n);

这段代码能够用来定义一个包含 n 个整数的数组吗?


9
它可以用来创建一个指针,指向一个能够容纳 n 个整数的内存。就是这样。在这段代码中没有数组的概念。 - Eugene Sh.
1
不确定您的问题。您在这里所做的是为类型为“int”的n个值分配足够的内存,并将a指向分配的缓冲区的开头。因为a的类型为int,并且因为您可以通过指针进行索引(例如,a[1]将第二个sizeof(int)字节解释为int),因此您可以像描述的数组一样处理它;但它不“知道”它的元素数量,a不是常量,因此您可以移动它,它将不再指向数组的基础,如果您将char指向同一缓冲区,则可以将数据解释为char - Mark Adelsberger
3
代码可用于为n个int分配空间。这并没有定义或声明一个数组。 - chux - Reinstate Monica
4
如果n>0,则malloc调用成功会创建一个匿名数组对象(假设是这样的)。否则,表达式a[0]a+0将无效。 - Keith Thompson
4
@EugeneSh,这个标准将malloc分配的空间描述为一个数组。 - M.M
显示剩余14条评论
5个回答

7
int *a = malloc(sizeof(int) * n);

这段代码能用于定义一个包含n个整数的数组吗?
这要取决于你对“定义数组”的理解。
例如以下声明语句:
int arr[10];

定义了一个命名的数组对象。而你的指针声明和初始化并没有。

然而,malloc 调用(如果成功,并返回非 null 结果,且 n > 0)将在运行时创建一个匿名的数组对象。

但它并没有 "定义一个数组 a"。 a 是一个指针对象的名称。鉴于 malloc 调用成功,a 将指向一个数组对象的初始元素,但它本身不是一个数组。

请注意,由于该数组对象是匿名的,因此无法应用 sizeof,也无法从指针中检索数组对象的大小。如果您需要知道数组的大小,您需要自己跟踪。

(一些评论认为 malloc 调用分配了可以容纳 n 个整数对象的内存,但并没有分配数组。如果是这样,那么你将无法访问创建的数组对象的元素。请参见 N1570 6.5.6p8 中指针加法的定义,以及 7.22.3p1 中关于 malloc 如何创建可访问数组的描述。)


对于这些运算符,指向不是数组元素的对象的指针与具有该对象类型作为其元素类型的长度为一的数组的第一个元素的指针行为相同。这允许在 a 不是数组的情况下出现 i == 0 的情况;如果 a 指向不是数组的对象,则 a[0] 是有效的。 - davmac
@davmac:更新了标准对malloc返回的指针如何用于访问数组的描述的引用。 - Keith Thompson
你的回答仍然说“索引表达式a[i],其中(i >= 0 && i < n),只有在指针a指向数组对象的元素时才有效”,但正如我所指出的那样,这是不正确的。 - davmac
@davmac:已更新。 - Keith Thompson

5
int *a = malloc(sizeof(int) * n);

假设malloc()调用成功,你可以使用指针a来像数组一样使用数组符号(例如a[0] = 5;)。但是a本身不是数组;它只是指向一个int的指针(并且它可能是一个能够存储多个int的内存块)。
你的评论

但是我可以在我的程序中使用一个没有声明的数组a

表明这可能是你主要关心的问题。
在C语言中,
p[i] == *(p + i) == *(i + p) == i[p]

只要 ip 中有一个是指针类型(p 也可以是数组,因为它在任何表达式中都会被转换为指针),因此,您就可以像访问数组一样对 a 进行索引。但是,a 实际上是一个指针。

即使是动态分配的对象——与指向它的指针不同——也不是数组。 - John Bollinger

4
是的,这正是malloc()所做的事情。 重要的区别在于:
int array[10];

array声明为一个数组对象,具有足够的空间存储10个整数。相比之下,以下方式:

int *pointer;

pointer声明为单个指针对象。

重要的是要区分它们中的一个是指针,另一个是实际数组,并且数组和指针密切相关但是不同的东西。然而,说以下没有数组也是不正确的:

pointer = malloc(sizeof (int) * 10);

这段代码的作用是分配一个能容纳10个整数的数组对象。指针pointer包含该数组第一个元素的地址。(C99草案,第7.20.3节“内存管理函数”)


3

如果我对您的问题字面上进行解释,答案是否定的:定义数组意味着具有相当特定的含义; 数组定义看起来像这样:

int a[10];

您所发布的是一个内存分配。它分配了一个适合容纳10个int值的空间,并将指针存储在该空间的第一个元素中,但它并没有定义一个数组;它只是分配了一个数组。
话虽如此,在任何情况下您都可以使用数组元素访问运算符[]。例如以下代码片段是合法的:
int a[10];
for (int i = 0; i < 10; i++) a[i] = 0;

并且

int *a = malloc(sizeof(int) * n);
for (int i = 0; i < n; i++) a[i] = 0;

然而,它们之间存在微妙的区别。第一个定义了一个数组,并将其所有元素设置为0。第二个分配存储空间,可以容纳等效类型的数组值,并通过初始化每个元素为0来用于此目的。

值得指出的是,第二个示例没有检查分配错误,这通常被认为是不好的实践。此外,如果未稍后释放分配的存储空间,则构成潜在的内存泄漏。


那么我需要在给定的内存分配中定义一个数组以避免分段错误吗? - kartikeykant18
@kartikeykant18 你的意思是“在给定的内存位置定义一个数组”,但我认为不行。在标准C中,没有办法在特定的内存位置上定义一个数组(据我所知)。但是,正如我所展示的那样,你可以分配空间来使用数组。请注意,“定义”在这里有特殊的含义。 - davmac
1
@kartikeykant18,你不能在动态内存分配的地址处定义数组。但这与分段错误无关。只要你在其范围内,就可以很好地存储动态分配的空间中的数据。 - John Bollinger

0

在标准描述的语言中(与对其进行学究般文字解释的语言不同),malloc(n)的意图是返回一个指针,如果将其强制转换为T*,则可以将其视为指向T[n/sizeof T*]的第一个元素的指针。根据N1570 7.22.3的规定:

如果分配成功,则返回的指针适当地对齐,以便可以将其分配给具有基本对齐要求的任何类型对象的指针,然后使用该指针来访问分配的空间中的此类对象或此类对象的数组(直到显式释放空间)。

指针加法和减法的定义并没有讨论对于“适当对齐”的指针进行操作,以允许访问对象数组,而是讨论指向实际数组对象元素的指针。如果程序访问了20个int对象的空间,我认为标准并没有确切地表示结果指针在所有方面都将表现得像指向int[20]元素[0]的指针,而不是例如指向int[4][5]元素[0][0]的指针。当然,一个实现必须非常愚蠢才不允许它被用作其中任何一种,但我认为标准实际上并没有要求这样处理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接