31

我一直用 Java 编程,这可能就是我对此感到如此困惑的原因:

在 Java 中,我声明了一个指针:

int[] array

并初始化它或为其分配一些内存:

int[] array = {0,1,0}
int[] array = new int[3]

现在,在 C 语言中,一切都变得如此混乱。起初我认为这就像声明它一样简单:

int array[]

并对其进行初始化或为其分配一些内存:

int array[] = {0,1,0}
int array[] = malloc(3*sizeof(int))
int array[] = calloc(3,sizeof(int))

除非我错了,否则以上所有内容都是等效的 Java-C,对吗?

然后,今天我遇到了一个代码,其中我发现了以下内容:

pthread_t tid[MAX_OPS];

和下面的一些行,没有任何初始化......

pthread_create(&tid[0],NULL,mou_usuari,(void *) 0);

令人惊讶的是(至少对我来说),代码有效!至少在 Java 中,这会返回一个不错的“NullPointerException”!

所以,按顺序:

  1. 我对所有 Java-C“翻译”都正确吗?

  2. 为什么该代码有效?

  3. malloc(n*sizeof(int))使用和有什么区别calloc(n,sizeof(int))吗?

提前致谢

4

5 回答 5

44

您不能将内存分配给数组。数组在其整个生命周期内具有固定的大小。数组永远不能为空。数组不是指针。

malloc将地址返回到为程序保留的内存块。您不能将其(作为内存块)“分配”给数组,但您可以将此内存块的地址存储在指针中:幸运的是,数组订阅是通过指针定义的 - 因此您可以“使用像数组一样的指针” ,例如

int *ptr = malloc(5 * sizeof *ptr);
ptr[2] = 5; // access the third element "of ptr"
free(ptr); // always free at the end

当你声明一个没有大小的数组(即array[])时,它仅仅意味着数组的大小是从初始化列表中确定的。那是

int array[] = {1, 2, 3, 4, 5}; // is equal to
int array[5] = {1, 2, 3, 4, 5};

试图声明一个没有大小且没有初始化程序的数组是错误的。


该代码声明了一个名为type和 sizepthread_t tid[MAX_OPS];的数组。tidpthread_tMAX_OPS

如果数组具有自动存储(即声明在函数内部,而不是静态的,不是全局的),那么每个数组元素都有不确定的值(并且它会导致试图读取这样的值的未定义行为)。幸运的是,函数调用所做的只是将数组的第一个元素的地址作为第一个参数,并可能在函数内部初始化它(元素)。


calloc和的区别malloc在于calloc返回的内存块被初始化为零。那是;

int *ptr = calloc(5, sizeof *ptr);
// is somewhat equal to
int *ptr = malloc(5 * sizeof *ptr);
memset(ptr, 0, 5 * sizeof *ptr);

和...之间的不同

int *ptr = malloc(5 * sizeof *ptr);
// and
int array[5];

array具有自动存储功能的(存储在堆栈上),并且在超出范围后“释放”。ptr但是,(存储在堆上)是动态分配的,必须free由程序员进行分配。

于 2010-11-21T13:12:01.277 回答
5

您缺少三个非常基本且严格(且具有误导性!)的 C 主题:

  • 数组和指针的区别
  • 静态分配和动态分配的区别
  • 与在堆栈或堆上声明变量的区别

如果你写int array[] = malloc(3*sizeof(int));你会得到一个编译错误(像'标识符':数组初始化需要花括号)。

这意味着声明一个数组只允许静态初始化:

  • int array[] = {1,2,3};在堆栈上保留 3 个连续整数;
  • int array[3] = {1,2,3};与上一个相同;
  • int array[3];仍然在堆栈上保留 3 个连续整数,但不初始化它们(内容将是随机垃圾)
  • int array[4] = {1,2,3};当初始化列表未初始化所有元素时,其余元素设置为 0(C99 §6.7.8/19):在这种情况下,您将获得 1,2,3,0

请注意,在所有这些情况下,您都没有分配新内存,您只是在使用已经提交给堆栈的内存。只有当堆栈已满时,您才会遇到问题(猜猜看,这将是堆栈溢出)。因此,声明int array[];将是错误且毫无意义的。

要使用malloc你必须声明一个指针:int* array.

当你写的时候,int* array = malloc(3*sizeof(int));你实际上是在做三个操作:

  1. int* array告诉编译器在堆栈上保留一个指针(一个包含内存地址的整数变量)
  2. malloc(3*sizeof(int))在堆上分配 3 个连续整数并返回第一个整数的地址
  3. =将返回值(您分配的第一个整数的地址)的副本分配给您的指针变量

所以,回到你的问题:

pthread_t tid[MAX_OPS];

是堆栈上的一个数组,因此不需要分配它(MAX_OPS例如,如果是 16,那么堆栈上将保留适合 16 pthread_t 所需的连续字节数)。该内存的内容将是垃圾(堆栈变量未初始化为零),但pthread_create在其第一个参数(指向变量的指针)中返回一个值pthread_t并忽略任何先前的内容,因此代码很好。

于 2010-11-21T13:42:03.440 回答
1

C 提供静态内存分配以及动态 - 您可以在堆栈外或可执行内存(由编译器管理)中分配数组。这与在 Java 中的方式相同,您可以在堆栈上分配一个 int 或在堆上分配一个 Integer。C 中的数组就像任何其他堆栈变量一样——它们超出范围等。在 C99 中,它们也可以具有可变大小,尽管它们不能调整大小。

{} 和 malloc/calloc 之间的主要区别在于 {} 数组是静态分配的(不需要释放)并为您自动初始化,而 malloc/calloc 数组必须显式释放并且您必须显式初始化它们。但当然,malloc/calloc 数组不会超出范围,您可以(有时) realloc() 它们。

于 2010-11-21T13:15:04.187 回答
0

2 - 这个数组声明是静态的:

pthread_t tid[MAX_OPS];

我们不需要分配内存块,而是动态分配:

pthread_t *tid = (pthread_t *)malloc( MAX_OPS * sizeof(pthread_t) );

不要忘记释放内存:

free(tid);

3 - malloc 和 calloc 之间的区别是 calloc 为数组分配一块内存并将其所有位初始化为 0。

于 2010-11-21T12:59:47.153 回答
-1

我发现在使用 C(而不是 C++)编程时明确指示 *array 很有帮助,记住有一个可以移动的指针。因此,我想首先将您的示例改写为:

int array[] = {0,1,2};
int *array = malloc(3*sizeof(int));
int *array = calloc(3,sizeof(int));

第一个清楚地表明有一个叫做数组的东西指向一个包含 0、1 和 2 的内存块。数组不能移动到其他地方。

您的下一个代码: pthread_t tid[MAX_OPS];

实际上会导致分配 sizeof(pthread_t) * MAX_OPS 的数组。但是它没有分配一个叫做 *tid 的指针。有一个数组基址的地址,但你不能把它移到别处。

pthrad_t 类型实际上是一个指针的覆盖。所以tid上面实际上是一个指针数组。它们都是静态分配的,但没有初始化。

pthread_create获取数组 ( ) 开头的位置,&tid[0]它是一个指针,并分配一块内存来保存 pthread 数据结构。将指针设置为指向新的数据结构并分配数据结构。

malloc(n*sizeof(int))您的最后一个问题 ---和之间的区别在于calloc(n,sizeof(int))后者将每个字节初始化为0,而第一个则没有。

于 2010-11-21T12:57:05.063 回答