这里考虑以下代码示例:
int *a = malloc(sizeof(int) * n);
这段代码可以用来定义一个包含 n 个整数的数组吗?
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,有关malloc
调用如何创建可访问数组的说明,请参阅 7.22.3p1。
int *a = malloc(sizeof(int) * n);
假设malloc()
调用成功,您可以使用指针a
,就像使用数组表示法的数组一样(例如a[0] = 5;
)。但a
本身并不是一个数组;它只是一个指向int
的指针(它可能是一个可以存储多个int
的内存块)。
您的评论
但是我可以在我的程序中使用数组 a,否则没有声明
这表明这是您主要询问的内容。
在C语言中,
p[i] == *(p + i) == *(i + p) == i[p]
只要i
或p
中的一个是指针类型(p
数组也可以 - 因为它在任何表达式中都会转换为指针)。因此,您可以像访问数组一样索引a
。但a
实际上是一个指针。
是的。这正是malloc()
所做的。
重要的区别在于
int array[10];
将array
声明为具有足够空间容纳 10 个整数的数组对象。相比之下,以下几点:
int *pointer;
将pointer
声明为单个指针对象。
重要的是要区分其中一个是指针,另一个是实际数组,数组和指针密切相关,但又是不同的东西。但是,说以下内容中没有数组也是不正确的:
pointer = malloc(sizeof (int) * 10);
因为这段代码所做的正是为一个有 10 个整数空间的数组对象分配。指针pointer
包含该数组的第一个元素的地址。(C99草案,第7.20.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 来为此目的使用它。
值得指出的是,第二个示例不检查分配错误,这通常被认为是不好的做法。此外,如果以后未释放分配的存储,则构成潜在的内存泄漏。
在编写标准所描述的语言中(与通过迂腐的字面阅读来描述的语言不同),目的是 malloc(n) 将返回一个指针,如果转换为T*
,则可以被视为指向T[n/sizeof T*]
的第一个元素的指针。 根据 N1570 7.22.3:
这 如果分配成功,则返回指针适当对齐,以便可以将其分配给 指向具有基本对齐要求的任何类型的对象的指针,然后使用 在分配的空间中访问此类对象或此类对象的数组(直到空间 显式解除分配)。
然而,指针加减法的定义并不是指作用于"适当对齐"以允许访问对象数组的指针,而是说指向实际数组对象元素的指针。 如果一个程序访问 20 个int
对象的空间,我认为标准实际上并没有说生成的指针在所有方面都表现得好像它是指向int[20]
元素 [0] 的指针,这与例如指向int[4][5]
的元素 [0][0] 的指针不同。 当然,一个实现必须非常迟钝才能不允许它被用作,但我认为标准实际上并不要求这样的处理。