Я очень смущен о 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))
?
Заранее спасибо
Ответы
Ответ 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
, а первый - нет.