Яка різниця між тим, що робити:
ptr = (char **) malloc (MAXELEMS * sizeof(char *));
або:
ptr = (char **) calloc (MAXELEMS, sizeof(char*));
Коли корисно використовувати calloc over malloc чи навпаки?
ptr = calloc(MAXELEMS, sizeof(*ptr));
Яка різниця між тим, що робити:
ptr = (char **) malloc (MAXELEMS * sizeof(char *));
або:
ptr = (char **) calloc (MAXELEMS, sizeof(char*));
Коли корисно використовувати calloc over malloc чи навпаки?
ptr = calloc(MAXELEMS, sizeof(*ptr));
Відповіді:
calloc()
дає вам нульовий ініціалізований буфер, при цьому malloc()
залишає пам'ять неініціалізованою.
Для великих виділень більшість calloc
реалізацій під основними операційними системами отримуватимуть відомі нульові сторінки з ОС (наприклад, через POSIX mmap(MAP_ANONYMOUS)
або Windows VirtualAlloc
), тому не потрібно записувати їх у користувальницький простір. Ось як нормально malloc
отримує і більше сторінок з ОС; calloc
просто використовує гарантію ОС.
Це означає, що calloc
пам'ять все ще може бути "чистою" та ліниво виділеною, а копіювати при записі відображати на загальносистемну загальну фізичну сторінку нулів. (Припускаючи систему з віртуальною пам'яттю.)
Деякі компілятори навіть можуть оптимізувати malloc + memset (0) у calloc, але вам слід використовувати явно calloc, якщо ви хочете, щоб пам'ять читалася як 0
.
Якщо ви не збираєтеся ніколи читати пам'ять перед її написанням, використовуйте malloc
так, що вона (потенційно) може дати вам брудну пам'ять із її внутрішнього безкоштовного списку замість отримання нових сторінок з ОС. (Або замість нулю блоку пам'яті у вільному списку для невеликого виділення).
Вбудовані реалізації програми calloc
можуть залишити її calloc
до нульової пам'яті, якщо немає ОС, або це не фантазія багатокористувацької ОС, яка нульовує сторінки, щоб зупинити витік інформації між процесами.
У вбудованому Linux, malloc міг mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS)
, який увімкнено лише для деяких вбудованих ядер, оскільки це небезпечно для багатокористувацької системи.
calloc
це не обов'язково дорожче, оскільки ОС може зробити деякі хитрощі, щоб пришвидшити його. Я знаю, що FreeBSD, коли отримує будь-який час простою процесора, використовує цей процес для запуску простого процесу, який просто обходить і зводить нанівець розміщені блоки пам'яті і маркує блоки таким чином, обробляючи прапор. Тож коли ви робите calloc
це, він спершу намагається знайти один із таких попередньо нульових блоків і просто надіслати його - і, швидше за все, він знайде його.
Менш відома відмінність полягає в тому, що в операційних системах з оптимістичним розподілом пам’яті, як-от Linux, вказівник, який повертається, malloc
не підтримується реальною пам'яттю, поки програма фактично не торкнеться цього.
calloc
дійсно торкається пам'яті (вона записує нулі на ній), і, таким чином, ви будете впевнені, що ОС підтримує розподіл фактичною оперативною пам’яттю (або свопом). Ось чому він повільніше, ніж malloc (він не тільки має нульовий рівень, ОС також повинен знайти відповідну область пам'яті, можливо замінивши інші процеси)
Дивіться, наприклад, це питання SO для подальшого обговорення поведінки malloc
calloc
не потрібно писати нулі. Якщо виділений блок складається здебільшого з нових нульових сторінок, наданих операційною системою, він може залишати ці недоторканими. Звичайно, це потрібно calloc
налаштовувати на операційну систему, а не на загальну функцію бібліотеки malloc
. Або виконавець може змусити calloc
порівнювати кожне слово проти нуля, перш ніж занулювати його. Це не заощадить жодного часу, але уникне забруднення нових сторінок.
dlmalloc
подібні реалізації пропускають, memset
якщо фрагмент був отриманий через mmap
нові анонімні сторінки (або еквівалент). Зазвичай такий вид виділення використовується для більших часток, починаючи з 256k або близько того. Я не знаю жодної реалізації, яка проводить порівняння проти нуля, перш ніж записати нуль осторонь від мого власного.
omalloc
також пропускає memset
; calloc
не потрібно торкатися жодних сторінок, які вже не використовуються програмою (кеш сторінок). Хоча надзвичайно примітивні calloc
реалізації відрізняються.
Однією з часто недооцінених переваг calloc
є те, що (відповідні реалізації) це допоможе захистити вас від цілих вразливих переповнень. Порівняйте:
size_t count = get_int32(file);
struct foo *bar = malloc(count * sizeof *bar);
vs.
size_t count = get_int32(file);
struct foo *bar = calloc(count, sizeof *bar);
Перший може призвести до невеликого розподілу та подальшого переповнення буфера, якщо count
він перевищує SIZE_MAX/sizeof *bar
. Остання автоматично вийде з ладу, оскільки великий об'єкт не може бути створений.
Звичайно, можливо, вам доведеться шукати невідповідні реалізації, які просто ігнорують можливість переповнення ... Якщо це турбує проблеми на платформах, на які ви орієнтуєтесь, вам доведеться все-таки зробити тест вручну на переповнення.
char
є НЕ переповнення , а скоріше визначається реалізацією перетворення при призначенні результат назад у вигляді char
об'єкта.
size_t
64-бітний, тому це не проблема", це хибний спосіб мислення, який призведе до помилок у безпеці. size_t
це абстрактний тип , який представляє розміри, і немає ніяких підстав вважати , довільне твір 32-бітного числа і size_t
(примітка: sizeof *bar
в принципі може бути більше , ніж 2 ^ 32 на реалізацію в 64-бітному C!) уміщається в size_t
.
Документація робить calloc схожим на malloc, який просто робить ініціалізацію пам'яті; це не первинна різниця! Ідея calloc полягає у відміні семантики копіювання на запис для розподілу пам'яті. Якщо ви виділяєте пам'ять з calloc, все це відображається на ту саму фізичну сторінку, яка ініціалізується до нуля. Коли будь-яка зі сторінок виділеної пам'яті записується у фізичну сторінку, виділяється. Це часто використовується для створення ВЕЛИЧЕЗНИХ хеш-таблиць, наприклад, оскільки порожні частини хеша не підтримуються додатковою пам'яттю (сторінки); вони із задоволенням вказують на єдину нульову ініціалізовану сторінку, яку можна розділити навіть між процесами.
Будь-яке записування на віртуальну адресу відображається на сторінку, якщо ця сторінка - нульова сторінка, виділяється інша фізична сторінка, нульова сторінка копіюється туди, а потік управління повертається до клієнтського процесу. Це працює так само, як працюють файли, відображені в пам'яті, віртуальна пам'ять тощо. Він використовує підкачку.
Ось одна історія оптимізації цієї теми: http://blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/
Немає різниці у розмірі виділеного блоку пам'яті. calloc
просто заповнює блок пам’яті фізичним малюнком з усіх нульових біт. На практиці часто передбачається, що об'єкти, розташовані в виділеному з блоку пам'яті, calloc
мають ініціальне значення так, ніби вони були ініціалізовані буквально 0
, тобто цілі числа повинні мати значення 0
змінних з плаваючою комою - значення 0.0
, вказівники - відповідного значення нульового вказівника , і так далі.
З педантичної точки зору, хоча calloc
(як і memset(..., 0, ...)
) гарантується лише належна ініціалізація (з нулями) об'єктів типу unsigned char
. Все інше не гарантується належним чином ініціалізувати і може містити так зване уявлення про пастку , що спричиняє не визначену поведінку. Іншими словами, для будь-якого типу, окрім unsigned char
вищезгаданого patterm all-zero-bits, може бути неправомірне значення, подання в пастку.
Пізніше в одному зі стандартів Технічної корекції до С99 поведінка була визначена для всіх цілих типів (що має сенс). Тобто формально, в поточній мові C ви можете ініціалізувати лише цілі типи з calloc
(і memset(..., 0, ...)
). Використання його для ініціалізації будь-чого іншого в загальному випадку призводить до невизначеної поведінки, з точки зору мови С.
На практиці це calloc
працює, як ми всі знаємо :), але чи бажаєте ви ним користуватися (з огляду на вищесказане). Я особисто вважаю за краще уникати цього, використовувати malloc
замість цього і виконувати власну ініціалізацію.
Нарешті, ще одна важлива деталь є те , що calloc
потрібно , щоб обчислити остаточний розмір блоку всередині , шляхом множення розміру елемента за кількістю елементів. Роблячи це, calloc
необхідно стежити за можливим арифметичним переповненням. Це призведе до невдалого розподілу (нульовий покажчик), якщо запитуваний розмір блоку неможливо правильно розрахувати. Тим часом ваша malloc
версія не намагається стежити за переповненням. Він виділить деякий "непередбачуваний" об'єм пам'яті у випадку, якщо відбудеться переповнення.
memset(p, v, n * sizeof type);
проблему, оскільки n * sizeof type
може переповнитися. Гадаю, мені знадобиться використовувати for(i=0;i<n;i++) p[i]=v;
цикл для надійного коду.
n
елементами існує там, де елемент має розмір sizeof type
, він n*sizeof type
не може переповнюватись, оскільки максимальний розмір будь-якого об'єкта повинен бути меншим за SIZE_MAX
.
SIZE_MAX
, але масивів тут немає . Вказівник, повернутий з, calloc()
може вказувати на виділену пам'ять, ніж перевищує SIZE_MAX
. Багато реалізацій обмежують добуток двох аргументів calloc()
до SIZE_MAX
, але специфіка C не встановлює цього обмеження.
зі статті Бенчмаркінг забави з calloc () і нульових сторінок на блозі Georg Hager в
При розподілі пам’яті за допомогою calloc () запитуваний обсяг пам’яті не виділяється відразу. Натомість всі сторінки, що належать до блоку пам'яті, з'єднуються в одну сторінку, що містить усі нулі деякою магією MMU (посилання нижче). Якщо такі сторінки читаються лише (що було справедливо для масивів b, c і d в оригінальній версії еталону), дані надаються з єдиної нульової сторінки, що, звичайно, вписується в кеш. Стільки для ядер циклу, пов'язаного з пам'яттю. Якщо сторінка записується на (незалежно від того, як), відбувається помилка, відображається "реальна" сторінка, а нульова сторінка копіюється в пам'ять. Це називається відомим підходом до оптимізації (що я навіть кілька разів викладав на своїх C ++ лекціях). Після того,
calloc
зазвичай malloc+memset
до 0
Зазвичай трохи краще використовувати malloc+memset
явно, особливо коли ви робите щось на кшталт:
ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));
Це краще, тому що sizeof(Item)
це відомо компілятору під час компіляції, і компілятор у більшості випадків замінить його найкращими можливими інструкціями щодо нульової пам'яті. З іншого боку, якщо memset
відбувається в calloc
, розмір параметра розподілу не компілюється в calloc
коді і memset
часто називається реальним , який, як правило, містить код для заповнення байта за байтом до довгої межі, ніж цикл для заповнення збільшити об'єм пам'яті sizeof(long)
і, нарешті, байт-байт заповнити залишок місця. Навіть якщо розподільник досить розумний, щоб викликати деякі, aligned_memset
він все одно буде загальним циклом.
Одним з помітних винятків буде те, коли ви робите malloc / calloc дуже великого фрагмента пам'яті (деякий power_of_two кілобайт), у цьому випадку виділення може здійснюватися безпосередньо з ядра. Оскільки ядра ОС зазвичай нулюють всю пам'ять, яку вони віддають з міркувань безпеки, досить розумний calloc може просто повернути її без додаткових нулювань. Знову ж таки - якщо ви просто виділяєте те, що ви знаєте, мало, вам може бути краще з функцією malloc + memset.
calloc()
повільніше, ніж malloc()
: множення на розмір. calloc()
потрібно використовувати загальне множення (якщо size_t
це 64 біт, навіть дуже затратні 64 біти * 64 біти = 64 біти), тоді як у malloc () часто буде константа часу компіляції.
struct foo { char a,b,c; };
. calloc
завжди краще, ніж malloc
+ memset
, якщо ви завжди збираєтесь очистити всю malloc
область редагування. calloc
також ретельно, але ефективно перевіряє наявність int overflow у розмірах * елементів.
Різниця 1:
malloc()
зазвичай виділяє блок пам'яті і це ініціалізований сегмент пам'яті.
calloc()
виділяє блок пам'яті та ініціалізує весь блок пам'яті до 0.
Різниця 2:
Якщо врахувати malloc()
синтаксис, знадобиться лише 1 аргумент. Розглянемо наступний приклад нижче:
data_type ptr = (cast_type *)malloc( sizeof(data_type)*no_of_blocks );
Наприклад: Якщо ви хочете виділити 10 блоків пам'яті для типу int,
int *ptr = (int *) malloc(sizeof(int) * 10 );
Якщо ви розглядаєте calloc()
синтаксис, знадобиться 2 аргументи. Розглянемо наступний приклад нижче:
data_type ptr = (cast_type *)calloc(no_of_blocks, (sizeof(data_type)));
Наприклад: якщо ви хочете виділити 10 блоків пам'яті для типу int та ініціалізувати все це на ZERO,
int *ptr = (int *) calloc(10, (sizeof(int)));
Схожість:
Обидва malloc()
і calloc()
повернуть недійсним * за замовчуванням, якщо вони не будуть типовими.
Є дві відмінності.
По-перше, в кількості аргументів. malloc()
бере один аргумент (потрібна пам'ять у байтах), тоді як calloc()
потрібні два аргументи.
По-друге, malloc()
не ініціалізує виділену пам'ять, в той час як calloc()
ініціалізує виділену пам'ять на ZERO.
calloc()
виділяє область пам'яті, довжина буде добуток її параметрів. calloc
заповнює пам'ять ZERO і повертає вказівник на перший байт. Якщо він не знайде достатньо місця, він повертає NULL
вказівник.Синтаксис: ptr_var=(cast_type *)calloc(no_of_blocks , size_of_each_block);
тобтоptr_var=(type *)calloc(n,s);
malloc()
виділяє єдиний блок пам'яті REQUSTED SIZE і повертає вказівник на перший байт. Якщо не вдасться знайти відновлений об'єм пам'яті, він повертає нульовий покажчик.Синтаксис: функція приймати один аргумент, який є кількістю байт для розподілу, в той час як функція приймає два аргументи, один з яких числа елементів, а інша кількість байт для розподілу для кожного з цих елементів. Крім того, ініціалізує виділений простір до нулів, а не робить.ptr_var=(cast_type *)malloc(Size_in_bytes);
malloc()
calloc()
calloc()
malloc()
calloc()
Функція , яка оголошена в <stdlib.h>
заголовку пропонує кілька переваг по порівнянні з malloc()
функцією.
malloc()
і calloc()
це функції зі стандартної бібліотеки С, які дозволяють динамічно розподіляти пам'ять, тобто вони обидва дозволяють розподілити пам'ять під час виконання.
Їх прототипи такі:
void *malloc( size_t n);
void *calloc( size_t n, size_t t)
В основному існують дві різниці між ними:
Поведінка: malloc()
виділяє блок пам'яті, не ініціалізуючи його, і зчитування вмісту з цього блоку призведе до значень сміття. calloc()
з іншого боку, виділяє блок пам'яті та ініціалізує його на нулі, і очевидно, що читання вмісту цього блоку призведе до нулів.
Синтаксис: malloc()
приймає 1 аргумент (розмір, який повинен бути призначений), і calloc()
бере два аргументи (кількість блоків, які потрібно виділити, і розмір кожного блоку).
Повернене значення з обох є вказівником на виділений блок пам'яті, якщо це вдало. В іншому випадку повернеться NULL , що вказує на збій розподілу пам'яті.
Приклад:
int *arr;
// allocate memory for 10 integers with garbage values
arr = (int *)malloc(10 * sizeof(int));
// allocate memory for 10 integers and sets all of them to 0
arr = (int *)calloc(10, sizeof(int));
Ж функціональні можливості, як calloc()
можна досягти з допомогою malloc()
і memset()
:
// allocate memory for 10 integers with garbage values
arr= (int *)malloc(10 * sizeof(int));
// set all of them to 0
memset(arr, 0, 10 * sizeof(int));
Зверніть увагу, що malloc()
його переважно використовувати над calloc()
тим, що це швидше. Якщо потрібна нульова ініціалізація значень, використовуйте calloc()
замість цього.
Ще не вказана різниця: обмеження розміру
void *malloc(size_t size)
може виділити лише до SIZE_MAX
.
void *calloc(size_t nmemb, size_t size);
може виділити близько SIZE_MAX*SIZE_MAX
.
Ця здатність не часто використовується на багатьох платформах з лінійною адресацією. Такі системи обмежують calloc()
з nmemb * size <= SIZE_MAX
.
Розглянемо тип 512 байт, який називається, disk_sector
і код хоче використовувати безліч секторів. Тут код може використовуватись лише до SIZE_MAX/sizeof disk_sector
секторів.
size_t count = SIZE_MAX/sizeof disk_sector;
disk_sector *p = malloc(count * sizeof *p);
Розглянемо наступне, що дозволяє ще більше розподілити.
size_t count = something_in_the_range(SIZE_MAX/sizeof disk_sector + 1, SIZE_MAX)
disk_sector *p = calloc(count, sizeof *p);
Тепер, якщо така система може забезпечити такий великий розподіл - інша справа. Більшість сьогодні цього не зробить. Тим не менш, це відбувалося протягом багатьох років, коли SIZE_MAX
було 65535. Зважаючи на закон Мура , підозрюйте, що це відбудеться приблизно в 2030 році з певними моделями SIZE_MAX == 4294967295
пам'яті і пулами пам'яті в 100 Гбіт.
size_t
більше 32 біт. Питання лише в тому, чи можна використовувати calloc
значення зі значеннями, продукт яких перевищує, SIZE_MAX
щоб отримати нуль, а не повернути вказівник на менший розмір.
calloc()
виділяти більше, ніж SIZE_MAX
. Це відбувалося в минулому з 16-бітною size_t
версією, і оскільки пам'ять продовжує здешевлюватися, я не бачу причин, що це не може відбутися вперед навіть у тому випадку, якщо це не є загальним явищем .
SIZE_MAX
. Це, звичайно, не вимагає наявності обставин, за яких таке розподіл може бути успішним; Я не впевнений, що якась особлива користь від мандатів на те, що реалізації, які не можуть обробляти такі розподіли, повинні повертатися NULL
(особливо якщо врахувати, що для деяких реалізацій є загальні malloc
покажчики повернення до місця, яке ще не здійснено і може бути недоступним, коли код насправді намагається використовувати це).
size_t
в uint64_t
?
Кількість блоків:
malloc () призначає один блок запитуваної пам'яті,
calloc () призначає кілька блоків запитуваної пам'яті
Ініціалізація:
malloc () - не очищає та ініціалізує виділену пам'ять.
calloc () - ініціалізує виділену пам'ять на нуль.
Швидкість:
malloc () - швидкий.
calloc () повільніше, ніж malloc ().
Аргументи та синтаксис:
malloc () бере 1 аргумент:
байт
calloc () бере 2 аргументи:
довжина
void *malloc(size_t bytes);
void *calloc(size_t length, size_t bytes);
Спосіб розподілу пам'яті:
Функція malloc призначає пам'ять потрібного "розміру" з доступної купи.
Функція calloc призначає пам'ять, розмір якої дорівнює "num * size".
Значення імені:
ім'я malloc означає "розподіл пам'яті".
Назва calloc означає "безперервне виділення".
malloc
сім'ї