Подтвердить что ты не робот

Я очень смущен о malloc() и calloc() на C

Я всегда программировал на 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"!

Итак, в порядке:

  • Я правильно со всеми переводами Java-C "?

  • Почему этот код работает?

  • Есть ли разница между использованием malloc(n*sizeof(int)) и calloc(n,sizeof(int))?

Заранее спасибо

4b9b3361

Ответ 1

Вы не можете назначить память массиву. Массив имеет фиксированный размер в течение всего срока его службы. Массив никогда не может быть нулевым. Массив не является указателем.

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};

Попытка объявить массив без размера и без инициализатора является ошибкой.


Код pthread_t tid[MAX_OPS]; объявляет массив с именем tid типа pthread_t и размером MAX_OPS.

Если массив имеет автоматическое хранилище (т.е. объявление находится внутри функции, а не статично, а не глобально), то каждый из элементов массивов имеет неопределенное значение (и это приведет к тому, что поведение undefined пытается прочитать такое значение). К счастью, все, что выполняет вызов функции, состоит в том, что он принимает адрес первого элемента массива в качестве первого параметра и, возможно, инициализирует его (элемент) внутри функции.


Разница 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 d программистом.

Ответ 2

Вам не хватает трех основных и затягивающих (и вводящих в заблуждение!) тем C:

  • разница между массивом и указателями
  • разница между статическим и динамическим распределением
  • отличие от объявления переменных в стеке или в куче

Если вы пишете int array[] = malloc(3*sizeof(int));, вы получите ошибку компиляции (что-то вроде 'identifier: инициализация массива требует фигурных скобок).

Это означает, что объявление массива допускает только статическую инициализацию:

  • 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));, вы на самом деле выполняете три операции:

  • int* array сообщает компилятору зарезервировать указатель на стек (целочисленная переменная, содержащая адрес памяти)
  • malloc(3*sizeof(int)) выделяет кучу 3 смежных целых числа и возвращает адрес первого
  • = назначает копии, возвращающие значение (адрес первого целого числа, которое вы назначили), в вашу переменную указателя

Итак, вернемся к вашему вопросу:

pthread_t tid[MAX_OPS];

- это массив в стеке, поэтому его не нужно выделять (если MAX_OPS есть, скажем, 16, то в стеке будет зарезервировано количество смежных байтов, которое должно соответствовать 16 pthread_t). Содержимое этой памяти будет мусором (переменные стека не инициализируются до нуля), но pthread_create возвращает значение в своем первом параметре (указатель на переменную pthread_t) и игнорирует любой предыдущий контент, поэтому код просто хорошо.

Ответ 3

C предлагает статическое распределение памяти, а также динамическое - вы можете выделять массивы со стека или в исполняемой памяти (управляемой компилятором). Это точно так же, как в Java, вы можете выделить int в стеке или Integer в куче. Массивы в C аналогичны любой другой переменной стека - они выходят за пределы области видимости и т.д. В C99 они также могут иметь переменный размер, хотя они не могут быть изменены.

Основное отличие между {} и malloc/calloc состоит в том, что {} массивы статически распределены (не требуют освобождения) и автоматически инициализируются для вас, тогда как массивы malloc/calloc должны быть явно освобождены, и вы должны их инициализировать явно, Но, конечно, массивы malloc/calloc не выходят за рамки, и вы можете (иногда) realloc() их.

Ответ 4

2 - Объявление этого массива статично:

pthread_t tid[MAX_OPS];

Нам не нужно выделять блок памяти вместо динамического выделения:

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

Не забывайте освобождать память:

free(tid);

3 - Разница между malloc и calloc заключается в том, что calloc выделяет блок памяти для массива и инициализирует все его биты в 0.

Ответ 5

Мне кажется полезным, когда вы программируете на C (в отличие от С++), чтобы явно указать * массив, чтобы помнить, что есть указатель, который можно перемещать. Поэтому я хотел бы начать с перефразирования вашего примера как:

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. Существует адрес базы массива, но вы не можете перемещать его в другом месте.

Тип ptherad_t на самом деле является обложкой для указателя. Итак, tid выше на самом деле представляет собой массив указателей. И все они статически выделены, но они не инициализируются.

pthread_create принимает местоположение в начале массива (&tid[0]), которое является указателем, и выделяет блок памяти для хранения структуры данных pthread. Указатель установлен для указания новой структуры данных и выделена структура данных.

Ваш последний вопрос --- разница между malloc(n*sizeof(int)) и calloc(n,sizeof(int)) заключается в том, что позже инициализируется каждый байт до 0, а первый - нет.