Страницы

Поиск по вопросам

Показаны сообщения с ярлыком calloc. Показать все сообщения
Показаны сообщения с ярлыком calloc. Показать все сообщения

воскресенье, 8 марта 2020 г.

Почему без ошибок выполняется этот код?

#c #указатели #calloc #строковый_литерал


char * buf = (char*)calloc(0,0);

    buf = "vygbubinon";

    printf("%s\n", buf);


Выводится "vygbubinon". Хотя по идее памяти выделено недостаточно
    


Ответы

Ответ 1



Давайте последовательно рассмотрим предложения указанного вами фрагмента кода. В первом предложении char * buf = (char*)calloc(0,0); выделяется память нулевого размера. Согласно стандарту языка C (7.22.3 Memory management functions) If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object. То есть вы можете запрашивать выделение памяти нулевого размера, но результат будет зависеть от платформы, где компилируется и выполняется программа. Либо будет возвращено значение NULL, либо некоторый действительный адрес памяти, по которому однако вы не можете обращаться к объекту. Так что данное предложение корректно. Далее в следующем предложении независимо от того, каков был результат предыдущего предложения (либо NULL, либо некоторый действительный адрес) указатель buf переприсваивается, и получает адрес первого символа строкового литерала "vygbubinon". buf = "vygbubinon"; Строковые литералы имеют статическую память. Следовательно, указатель получает адрес первого символа данного строкового литерала, расположенного в статической области памяти. Память для строковых литералов резервируется компилятором на этапе компиляции. (Компиляторы обычно собирают все литералы, присутствующее в программе, в некоторый пул литералов.) Ничего некорректного в этом предложении нет. Единственная проблема, связанная с этим предложением состоит в том, что если в предыдущем предложении была выделена память, то вы уже не сможете ее удалить, так как адрес этой памяти за счет переприсвоения указателя был утерян. Произойдет утечка памяти, но никакой ошибки времени компиляции или времени выполнения не будет. Ну, и, наконец, в этом предложении выводится на консоль данный литерал. printf("%s\n", buf); Так что фрагмент кода совершенно корректный за исключением возможной утечки памяти, что представляет собой лишь логическую ошибку.

Ответ 2



памяти выделено недостаточно Нет, достаточно. Вот этот код: buf = "vygbubinon"; не копирует строку в память, на которую указывает buf. Он всего лишь присваивает указателю buf адрес строки "vygbubinon", лежащей где-то в сегменте данных. Если будет понятней, то на уровень ниже это может выглядеть как-то так: .data ; ... _some_static_string db "vygbubinon", 0 buf dd ? ; ... .code ; ... mov buf, offset _some_static_string Если же вам нужно скопировать строку в область памяти, адрес которой содержится в указателе, нужно использовать функции семейств strcpy(), memcpy() и другие.

Ответ 3



char *buf - всего-лишь переменная-указатель. По сути она является просто адресом некоторой ячейки памяти. В случае со строками - это адрес самого первого символа. Поэтому в первой строке вы присваиваете переменной buf адрес выделенной памяти, но никак не саму память. А в следующей строке присваиваете buf значение из области данных программы, т.е. ваше "vygbubinon". При этом следует учитывать, что если вы не освобождаете кусок памяти, выделенный в куче вызовом malloc (если вы выделяли не 0) и перезаписываете указатель на нее, то происходит утечка памяти

среда, 18 декабря 2019 г.

Различия между функциями calloc и malloc

#c #функции #память #malloc #calloc


В чем заключается отличие функции malloc от calloc?
Есть ли случаи, когда подходит лишь одна из этих функций?
    


Ответы

Ответ 1



Во-первых, функция calloc, в отличие от malloc, возвращает указатель на инициализированный блок памяти, т.е. изначально содержащий нулевые биты1. Это, однако, совсем не означает, что функция calloc сама занимается обнулением полученного блока памяти в контексте пользовательского процесса. Функция calloc может пользоваться более эффективными средствами обнуления, предоставляемыми подлежащей платформой, т.е., например, выделять память в пуле "обнуленных страниц" ОС или использовать некий механизм "ленивого" обнуления уровня аппаратуры/ОС. То есть если вам нужен обнуленный блок памяти, то в общем случае получение такого блока памяти через calloc может оказаться существенно более эффективной операцией, чем "ручное" обнуление блока памяти, полученного через malloc. Во-вторых, функция calloc, как несложно заметить, сама внутренне занимается вычислением общего размера запрашиваемого блока памяти (буквально - перемножением своих аргументов). При этом функция calloc обязана самостоятельно отследить корректность такого перемножения, т.е. обнаружить ошибку при возникновении переполнения. В случае использования функции malloc такое перемножение (если оно необходимо) выполняет вызывающий код и предотвращение переполнения - обязанность вызывающего кода. 1 С точки зрения языка это не гарантирует правильное формирование нулевых указателей и нулевых значений плавающего типа.

Ответ 2



malloc просто выделяет память, оставляя ее содержимое неопределенным, a calloc ее гарантированно обнуляет. Что не всегда необходимо, зато требует времени...

понедельник, 27 мая 2019 г.

Почему без ошибок выполняется этот код?

char * buf = (char*)calloc(0,0);
buf = "vygbubinon";
printf("%s
", buf);
Выводится "vygbubinon". Хотя по идее памяти выделено недостаточно


Ответ

Давайте последовательно рассмотрим предложения указанного вами фрагмента кода. В первом предложении
char * buf = (char*)calloc(0,0);
выделяется память нулевого размера. Согласно стандарту языка C (7.22.3 Memory management functions)
If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.
То есть вы можете запрашивать выделение памяти нулевого размера, но результат будет зависеть от платформы, где компилируется и выполняется программа. Либо будет возвращено значение NULL, либо некоторый действительный адрес памяти, по которому однако вы не можете обращаться к объекту. Так что данное предложение корректно.
Далее в следующем предложении независимо от того, каков был результат предыдущего предложения (либо NULL, либо некоторый действительный адрес) указатель buf переприсваивается, и получает адрес первого символа строкового литерала "vygbubinon".
buf = "vygbubinon";
Строковые литералы имеют статическую память. Следовательно, указатель получает адрес первого символа данного строкового литерала, расположенного в статической области памяти. Память для строковых литералов резервируется компилятором на этапе компиляции. (Компиляторы обычно собирают все литералы, присутствующее в программе, в некоторый пул литералов.) Ничего некорректного в этом предложении нет. Единственная проблема, связанная с этим предложением состоит в том, что если в предыдущем предложении была выделена память, то вы уже не сможете ее удалить, так как адрес этой памяти за счет переприсвоения указателя был утерян. Произойдет утечка памяти, но никакой ошибки времени компиляции или времени выполнения не будет.
Ну, и, наконец, в этом предложении выводится на консоль данный литерал.
printf("%s
", buf);
Так что фрагмент кода совершенно корректный за исключением возможной утечки памяти, что представляет собой лишь логическую ошибку.

вторник, 30 октября 2018 г.

Различия между функциями calloc и malloc

В чем заключается отличие функции malloc от calloc? Есть ли случаи, когда подходит лишь одна из этих функций?


Ответ

Во-первых, функция calloc, в отличие от malloc, возвращает указатель на инициализированный блок памяти, т.е. изначально содержащий нулевые биты1. Это, однако, совсем не означает, что функция calloc сама занимается обнулением полученного блока памяти в контексте пользовательского процесса. Функция calloc может пользоваться более эффективными средствами обнуления, предоставляемыми подлежащей платформой, т.е., например, выделять память в пуле "обнуленных страниц" ОС или использовать некий механизм "ленивого" обнуления уровня аппаратуры/ОС. То есть если вам нужен обнуленный блок памяти, то в общем случае получение такого блока памяти через calloc может оказаться существенно более эффективной операцией, чем "ручное" обнуление блока памяти, полученного через malloc
Во-вторых, функция calloc, как несложно заметить, сама внутренне занимается вычислением общего размера запрашиваемого блока памяти (буквально - перемножением своих аргументов). При этом функция calloc обязана самостоятельно отследить корректность такого перемножения, т.е. обнаружить ошибку при возникновении переполнения. В случае использования функции malloc такое перемножение (если оно необходимо) выполняет вызывающий код и предотвращение переполнения - обязанность вызывающего кода.

1 С точки зрения языка это не гарантирует правильное формирование нулевых указателей и нулевых значений плавающего типа.

вторник, 2 октября 2018 г.

Откуда C/C++ знает сколько надо освободить памяти, если не знает размер массива?

В C/C++ для того чтобы обработать массив нужно знать его размер. Соответственно нужно всегда этот размер "помнить" и протаскивать во все функции обработки в качестве аргумента. Например:
void foo(int* arr, size_t n) { for (size_t i = 0; i < n; i++) { arr[i] = i * i; } }
Но при освобождении ресурсов знать размер почему-то необязательно. Можно просто вызвать free(arr) если память выделялась через malloc() или calloc(). Или можно использовать оператор delete[] arr; если память выделялась через оператор new int[n]
Вопрос откуда C/C++ знает сколько надо освободить памяти, если не знает размер массива? Функция free() и оператор delete[] не принимают в качестве аргументов размер массива, а только указатель на массив. А если C/C++ может каким-то образом вычислить размер, то зачем его постоянно "таскать" с собой в отдельной переменной?


Ответ

Все это - детали реализации.
В популярных реализациях malloc обычно записывает размер выделенного блока в начало выделенного блока. Возвращенный вам указатель обычно указывает на память сразу за этим записанным размером. free знает, где искать размер блока, и извлекает его именно оттуда.
По умолчанию new и delete просто делегируют запросы на выделение и освобождение сырой памяти в тот же самый malloc и free или их аналоги, через посредство operator new и operator delete.
При работе с массивами объектов с тривиальными деструкторами new[] и delete[] фактически ведут себя точно так же: вызывают в конечном итоге malloc с правильно вычисленным общим размером массива и вызывают free для освобождения памяти.
При работе с массивами объектов с нетривиальными деструкторами все несколько сложнее: new[] дополнительно записывает в начало блока памяти точное количество элементов создаваемого массива, а delete[] потом извлекает это количество и вызывает правильное количество деструкторов.
Допустим, если у вас есть какой-то класс MyNonTrivialClass размером в 9 байт с нетривиальным деструктором, то выполнение
MyNonTrivialClass *p = new MyNonTrivialClass[17];
приведет к формированию блока памяти со следующей внутренней структурой
+-----+-----+------+------+------ | 176 | 17 | p[0] | p[1] | ... +-----+-----+------+------+------ ^ ^ ^ | | | | | p - полученный вами указатель | | | поле типа `size_t` (8 байт), записано `new[]` | поле типа `size_t` (8 байт), записано `malloc` `new[]` запросил 161 байт = 17 * 9 + 8, размер выровнен до границы 16 байт
Конкретные значения могут отличаться, но общая идея обычно в популярных реализациях именно такая.
См. также https://ru.stackoverflow.com/a/770300/182825