Яка різниця між тим, що робити:
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_t64-бітний, тому це не проблема", це хибний спосіб мислення, який призведе до помилок у безпеці. 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сім'ї