根据标准(C17草案,7.22.3.2),calloc
void *calloc(size_t nmemb, size_t size);
“为nmemb
对象的数组分配空间,每个对象的大小为size
“(并将所有位都设置为零)。
对于T
的calloc
艾德数组,我只见过这样的代码:
T *p = calloc(nmemb, sizeof(T));
T *p;
p = calloc(nmemb, sizeof(T));
但是考虑到calloc
为一个 array 分配空间,下面的代码也应该没问题:
T (*arrp)[nmemb] = calloc(nmemb, sizeof(T));
T (*arrp)[nmemb];
arrp = calloc(nmemb, sizeof(T));
(Here,从技术上讲,每对的最高版本是一个 * 初始化 *,而不是一个 * 赋值 *。
calloc
的结果可以赋给什么类型,一个指向数组的指针(类型T (*)[]
),一个指向数组中包含的类型的指针(类型T *
),或者两者之一?
下面的代码
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int (*iarrp)[5];
int *ip;
float (*farrp)[1];
float *fp;
int i;
iarrp = calloc(5, sizeof(int));
for (i = 0; i < 5; ++i)
(*iarrp)[i] = -i;
ip = calloc(5, sizeof(int));
for (i = 0; i < 5; ++i)
ip[i] = i + 100;
for (i = 0; i < 5; ++i)
printf("%d: %d, %d\n", i, (*iarrp)[i], ip[i]);
farrp = calloc(1, sizeof(float));
(*farrp)[0] = 5.5;
fp = calloc(1, sizeof(float));
*fp = 6.6;
printf("%.2f, %.2f\n", (*farrp)[0], *fp);
free(iarrp);
free(ip);
free(farrp);
free(fp);
return 0;
}
对于我来说,使用GCC(gcc -std=c17 -pedantic -Wall -Wextra
)和MSVC(cl /std:c17 /Wall
)编译得很好,输出与预期的一样:
0: 0, 100
1: -1, 101
2: -2, 102
3: -3, 103
4: -4, 104
5.50, 6.60
提出这个问题的背景是这样的:对于T[]
类型的数组arr
,以下三个表达式中的
arr
;类型:T[]
(衰减前),T *
(衰减后)&arr[0]
;类型:T *
- 这就是
arr
衰减到 &arr
;类型:T (*)[]
&
防止衰变
前两个产生相同的值。理论上,第三个表达式的值可以不同于前两个表达式,尽管我知道这是不常见的。标准只保证(void *)&arr == (void *)&arr[0]
为真。
2条答案
按热度按时间ajsxfq5m1#
calloc
返回的动态分配内存没有 * 有效类型 *,根据C 2018 6.5 6。在C语义中,它只是一个可以用于任何对象类型的内存区域。它可以通过使用非字符类型向其中存储值来获取有效类型。(并且这个有效类型可以通过将来的分配进行更改-内存可以重新用于其他类型。
C标准不清楚具有聚合的有效类型的精确形式语义。数组的元素和结构的成员可以单独存储在存储器中,而不存储整个聚合。尽管如此,很明显,我们可以使用动态分配的内存来存储聚合,或者一次全部(对于结构),或者与单个元素或成员一起存储。
是否应该使用
T *
或T (*)[]
类型的指针访问内存的问题没有任何后果。给定声明T *p0
,一个值被存储到*p0 = value;
的内存中。在这个赋值中,*
被应用到存储在p0
中的地址,以形成分配内存中T
元素的左值。给定声明T (*p1)[nmemb]
,一个值被存储到(*p1)[i] = value;
的内存中。在这个赋值中,*
被应用于存储在p1
中的地址,以形成数组的左值,这个左值被转换为指向数组第一个元素的指针,然后下标运算符被应用于形成数组元素i
的左值。因此,在任何一种情况下,用于将值实际存储到内存中的左值都是元素的左值。T *
或T (*)[]
的选择只影响左值的中间计算,而不是用于存储的最终左值。这个问题对结构更有意思。给定
typedef struct { int i, j; } T; T *p = calloc(1, sizeof *p);
,我们可以用*p = (T) { 7, 13 };
来给整个结构赋值,或者用p->i = 7;
来给一个成员赋值。如果我们做后者,我们是否将存储器的有效类型设置为T
?或者我们只将部分内存的有效类型设置为int
?C标准没有充分说明这一点。9udxz4iz2#
你问:
calloc的结果可以赋给什么类型
答案可以在标准中找到(例如,C17 7.22.3):
如果分配成功,则返回的指针适当对齐,以便它可以分配给指向任何类型对象的指针
就这样。任何指针类型。
然后你说:
以下内容也应该不错:
T (*arrp)[nmemb] = calloc(nmemb, sizeof(T));
嗯...是的,只要你访问像
(*arrp)[i]
或arrp[0][i]
(和0 <= i < nmemb
)这样的内存,它就能工作。但这并不是
calloc
的真正用途。代码告诉calloc
每个元素的大小为sizeof(T)
。但是,您将返回值存储到指向大小为nmemb * sizeof(T)
的元素的指针中。这是因为arrp
是一个指向nmemb
Ts数组的指针。对于您的代码,预期的格式将是:
更好的写法是:
这就导致了这个代码的“正常”使用。它用于分配2维数组(又名“数组的数组”)。就像这样:
这段代码给你一个动态分配,对应于静态/自动分配:
顺便说一句:
这可能是一个有趣的阅读:
Why does calloc require two parameters and malloc just one?