type - cpp cast to int




Я использую результат malloc? (18)

В этом вопросе кто-то предложил в comment что я не должен приводить результат malloc , т.е.

int *sieve = malloc(sizeof(int) * length);

скорее, чем:

int *sieve = (int *) malloc(sizeof(int) * length);

Почему это так?


Нет, вы не разыгрываете результат malloc() .

В общем, вы не бросаете в или из void * .

Типичная причина, по которой они этого не делают, заключается в том, что неудача #include <stdlib.h> может остаться незамеченной. Это уже давно не проблема, поскольку C99 сделал неявные объявления функций недопустимыми, поэтому, если ваш компилятор соответствует хотя бы C99, вы получите диагностическое сообщение.

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

В C приведение указателя почти всегда является ошибкой . Это из-за следующего правила ( §6.5 p7 в N1570, последний проект для C11):

Объект должен иметь свое сохраненное значение, доступное только через выражение lvalue, которое имеет один из следующих типов:
- тип, совместимый с действительным типом объекта,
- квалифицированная версия типа, совместимого с действительным типом объекта,
- тип, который является типом со знаком или без знака, соответствующим действующему типу объекта,
- тип, который является типом со знаком или без знака, соответствующим квалифицированной версии действующего типа объекта,
- тип агрегата или объединения, который включает в себя один из вышеупомянутых типов среди его членов (включая, рекурсивно, член субагрегата или автономного объединения), или
- символьный тип.

Это также известно как строгое правило наложения имен . Таким образом, следующий код является неопределенным поведением :

long x = 5;
double *p = (double *)&x;
double y = *p;

И, иногда удивительно, следующее также:

struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;

Иногда вам действительно нужно бросить указатели, но , учитывая строгие правила наложения спектров , вы должны быть очень осторожны с ним. Таким образом, любое вхождение указателя в вашем коде - это место, где вы должны дважды проверить его правильность . Следовательно, вы никогда не пишете ненужный указатель приведения.

ТЛ; др

В двух словах: поскольку в C любое появление приведения указателя должно поднимать красный флаг для кода, требующего особого внимания, вы никогда не должны писать ненужные приведения указателя.

Примечания стороны:

  • Существуют случаи, когда вам действительно требуется приведение к void * , например, если вы хотите напечатать указатель:

    int x = 5;
    printf("%p\n", (void *)&x);

    Приведение здесь необходимо, потому что printf() это функция с переменным числом, поэтому неявные преобразования не работают.

  • В C ++ ситуация иная. Приведение типов указателей является довольно распространенным (и правильным) при работе с объектами производных классов. Следовательно, имеет смысл, что в C ++ преобразование в и из не void * является неявным. C ++ имеет целый набор различных форм кастинга.


  1. Как уже говорилось, это не нужно для C, но для C ++.

  2. Включение преобразования может позволить программе или функции на C компилироваться как C ++.

  3. В C это не нужно, поскольку void * автоматически и безопасно переводится в любой другой тип указателя.

  4. Но если вы разыгрываете тогда, он может скрыть ошибку, если вы забыли включить stdlib.h . Это может вызывать сбои (или, что еще хуже, не приводить к сбою, пока в какой-то совершенно другой части кода).

    Поскольку stdlib.h содержит прототип для malloc найден. В отсутствие прототипа для malloc стандарт требует, чтобы компилятор C предполагал, что malloc возвращает int. Если нет приведения, выдается предупреждение, когда это целое число присваивается указателю; однако, с использованием приведения это предупреждение не выдается, скрывая ошибку.


Концепция указателя void заключается в том, что он может быть приведен к любому типу данных, поэтому malloc возвращает void. Также вы должны знать об автоматической настройке типов. Так что указывать указатель не обязательно, хотя вы должны это сделать. Это помогает содержать код в чистоте и помогает отладке


Лучшее, что можно сделать при программировании на C, когда это возможно:

  1. Сделайте так, чтобы ваша программа компилировалась через компилятор C со всеми предупреждениями -Wall и исправляла все ошибки и предупреждения
  2. Убедитесь, что нет переменных, объявленных как auto
  3. Затем скомпилируйте его, используя компилятор C ++ с -Wall и -std=c++11 . Исправьте все ошибки и предупреждения.
  4. Теперь снова скомпилируйте с использованием компилятора C. Ваша программа должна теперь скомпилироваться без предупреждения и содержать меньше ошибок.

Эта процедура позволяет вам использовать строгую проверку типов в C ++, тем самым уменьшая количество ошибок. В частности, эта процедура заставляет вас включить stdlib.h или вы получите

malloc не был объявлен в этой области

а также заставляет вас разыграть результат malloc или вы получите

неверное преобразование из void* в T*

или каков ваш целевой тип.

Единственная выгода от написания на C вместо C ++, которую я могу найти:

  1. C имеет хорошо определенный ABI
  2. C ++ может генерировать больше кода [исключения, RTTI, шаблоны, полиморфизм времени выполнения ]

Обратите внимание, что вторые минусы в идеальном случае должны исчезнуть при использовании общего для C подмножества вместе со статической полиморфной функцией.

Для тех, кто считает строгие правила C ++ неудобными, мы можем использовать функцию C ++ 11 с выведенным типом

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...

Я предпочитаю сниматься, но не вручную. Мой любимый использует g_new и g_new0 макросы из glib. Если glib не используется, я бы добавил похожие макросы. Эти макросы уменьшают дублирование кода без ущерба для безопасности типов. Если вы ошиблись в типе, вы бы получили неявное приведение между не пустыми указателями, что вызвало бы предупреждение (ошибка в C ++). Если вы забудете включить заголовок, который определяет g_new и g_new0 , вы получите ошибку. g_new и g_new0 оба принимают одинаковые аргументы, в отличие от malloc этого требует меньше аргументов, чем calloc . Просто добавьте, 0 чтобы получить нулевую инициализированную память. Код может быть скомпилирован с помощью компилятора C ++ без изменений.


В C вам не нужно приводить возвращаемое значение malloc . Указатель на void, возвращаемый malloc , автоматически преобразуется в правильный тип. Однако, если вы хотите, чтобы ваш код компилировался с помощью компилятора C ++, необходимо преобразование. Предпочтительной альтернативой среди сообщества является использование следующего:

int *sieve = malloc(sizeof *sieve * length);

что дополнительно освобождает вас от необходимости беспокоиться об изменении правой части выражения, если вы когда-либо измените тип sieve .

Слепки плохие, как указали люди. Специально указатель отливок.


В C вы получаете неявное преобразование из void* в любой другой указатель (данных).


В языке C указатель void может быть назначен любому указателю, поэтому не следует использовать приведение типа. Если вы хотите «безопасное распределение типов», я могу порекомендовать следующие макро-функции, которые я всегда использую в своих проектах на C:

#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)

С этим на месте вы можете просто сказать

NEW_ARRAY(sieve, length);

Для нединамических массивов третий обязательный макрос функции

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])

что делает циклы массива более безопасными и удобными:

int i, a[100];

for (i = 0; i < LEN(a); i++) {
   ...
}

Вот что говорит Справочное руководство по библиотеке GNU C :

Вы можете сохранить результат malloc в любой переменной указателя без приведения, потому что ISO C автоматически преобразует тип void * в указатель другого типа при необходимости. Но приведение необходимо в контекстах, отличных от операторов присваивания или если вы хотите, чтобы ваш код выполнялся в традиционном C.

И действительно, стандарт ISO C11 (p347) гласит:

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


Вы делаете бросок, потому что:

  • Это делает ваш код более переносимым между C и C ++, и, как показывает опыт SO, многие программисты утверждают, что пишут на C, когда они действительно пишут на C ++ (или C плюс локальные расширения компилятора).
  • Несоблюдение этого требования может скрыть ошибку : обратите внимание на все примеры SO, которые вводят в заблуждение, когда писать type * вместо type ** .
  • Идея, что он не заметит, что вы не #include соответствующий заголовочный файл пропускает лес для деревьев . Это то же самое, что сказать: «Не беспокойтесь о том, что вы не попросили компилятор жаловаться на то, что не видите прототипы - этот pesky stdlib.h - ДЕЙСТВИТЕЛЬНО важная вещь, которую нужно помнить!»
  • Это вызывает дополнительную когнитивную перекрестную проверку . Он помещает (предполагаемый) желаемый тип прямо рядом с арифметикой, которую вы делаете для необработанного размера этой переменной. Могу поспорить, что вы могли бы сделать SO исследование, которое показывает, что ошибки malloc() обнаруживаются намного быстрее, когда происходит приведение. Как и с утверждениями, аннотации, раскрывающие намерения, уменьшают количество ошибок.
  • Повторять себя так, как может проверить машина, - это часто хорошая идея. Фактически, это то, что является утверждением, и это использование приведения является утверждением. Утверждения по-прежнему являются наиболее общей техникой, которую мы используем для получения правильного кода, так как Тьюринг придумал эту идею много лет назад.

Из Wikipedia :

Преимущества литья

  • Включение преобразования может позволить программе или функции на C компилироваться как C ++.

  • Приведение допускает версии malloc до 1989 года, которые изначально возвращали char *.

  • Приведение может помочь разработчику выявить несоответствия в определении размера при изменении типа указателя назначения, особенно если указатель объявлен вдали от вызова malloc () (хотя современные компиляторы и статические анализаторы могут предупреждать о таком поведении, не требуя приведения).

Недостатки кастинга

  • В соответствии со стандартом ANSI C приведение является избыточным.

  • Добавление приведения может скрыть неудачу при включении заголовка stdlib.h , в котором находится прототип для malloc. В отсутствие прототипа для malloc стандарт требует, чтобы компилятор C предполагал, что malloc возвращает int. Если нет приведения, выдается предупреждение, когда это целое число присваивается указателю; однако, с использованием приведения это предупреждение не выдается, скрывая ошибку. В некоторых архитектурах и моделях данных (таких как LP64 в 64-разрядных системах, где long и указатели являются 64-разрядными, а int - 32-разрядными), эта ошибка может фактически привести к неопределенному поведению, поскольку неявно объявленный malloc возвращает 32- битовое значение, тогда как фактически определенная функция возвращает 64-битное значение. В зависимости от соглашений о вызовах и расположения памяти это может привести к разрушению стека. Эта проблема с меньшей вероятностью останется незамеченной в современных компиляторах, поскольку они единообразно выдают предупреждения о том, что была использована необъявленная функция, поэтому предупреждение все равно будет появляться. Например, стандартное поведение GCC заключается в отображении предупреждения, которое гласит «несовместимое неявное объявление встроенной функции» независимо от того, присутствует ли приведение или нет.

  • Если тип указателя изменяется при его объявлении, может также потребоваться изменить все строки, где вызывается и преобразуется malloc.

Хотя malloc без приведения является предпочтительным методом, и большинство опытных программистов выбирают его , вы должны использовать все, что захотите, зная о проблемах.

То есть: если вам нужно скомпилировать программу на C как C ++ (хотя это отдельный язык), вы должны использовать malloc с приведением типов.


Как уже говорилось, это не нужно для C, но для C ++. Если вы думаете, что собираетесь скомпилировать свой код C с помощью компилятора C ++, по каким-либо причинам вы можете вместо этого использовать макрос, например:

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

Таким образом, вы все еще можете написать это очень компактно:

int *sieve = NEW(int, 1);

и он скомпилируется для C и C ++.


Не обязательно приводить результаты malloc , так как он возвращает void* , а void* может указывать на любой тип данных.


Приведение значения, возвращаемого malloc() , теперь не нужно, но я хотел бы добавить одну точку, на которую, похоже, никто не указал:

В древние времена, то есть до того, как ANSI C предоставил void * в качестве общего типа указателей, char * является типом для такого использования. В этом случае приведение может отключить предупреждения компилятора.

Ссылка: C FAQ


Указатель void является универсальным указателем, и C поддерживает неявное преобразование из типа указателя void в другие типы, поэтому нет необходимости явно указывать его типизацию.

Однако если вы хотите, чтобы один и тот же код работал идеально совместимым на платформе C ++, которая не поддерживает неявное преобразование, вам необходимо выполнить приведение типов, так что все зависит от удобства использования.


Это зависит от языка программирования и компилятора. Если вы используете malloc в C, нет необходимости вводить cast, поскольку он будет автоматически набирать cast, однако если вы используете C ++, вам следует набрать cast, потому что malloc вернет тип void* .


Приведение malloc не нужно в C, но обязательно в C ++.

Приведение не нужно в C из-за:

  • void * автоматически и безопасно повышается до любого другого типа указателя в случае C.
  • Может скрыть ошибку, если вы забыли включить <stdlib.h> . Это может вызвать сбои.
  • Если указатели и целые числа имеют разный размер, тогда вы скрываете предупреждение путем приведения и можете потерять биты вашего возвращенного адреса.
  • Если тип указателя изменяется при его объявлении, может также потребоваться изменить все строки, где malloc вызывается и приводится.

С другой стороны, приведение может увеличить переносимость вашей программы. то есть, он позволяет программе или функции на C компилироваться как C ++.


Нет ; Вы не разыгрываете результат, так как:

  • В этом нет необходимости, поскольку void * автоматически и безопасно переводится в любой другой тип указателя в этом случае.
  • Это добавляет беспорядок в код, приведения не очень легко читаются (особенно если тип указателя длинный).
  • Это заставляет вас повторяться, что, как правило, плохо.
  • Он может скрыть ошибку, если вы забыли включить <stdlib.h> . Это может вызывать сбои (или, что еще хуже, не приводить к сбою, пока в какой-то совершенно другой части кода). Подумайте, что произойдет, если указатели и целые числа имеют разные размеры; тогда вы скрываете предупреждение путем приведения и можете потерять биты вашего возвращенного адреса. Примечание: начиная с C11 неявные функции ушли из C, и этот момент больше не актуален, поскольку нет автоматического предположения, что необъявленные функции возвращают int .

В качестве пояснения обратите внимание, что я сказал: «Вы не читаете», а не «вам не нужно читать». На мой взгляд, это не удалось включить актерский состав, даже если вы правильно поняли. В этом просто нет никаких преимуществ, но куча потенциальных рисков, и в том числе актерский состав, указывает на то, что вы не знаете о рисках.

Также отметьте, как отмечают комментаторы, что вышесказанное говорит о прямом C, а не C ++. Я очень твердо верю в C и C ++ как отдельные языки.

Чтобы добавить, ваш код без необходимости повторяет информацию о типе ( int ), которая может вызвать ошибки. Лучше разыменовать указатель, используемый для хранения возвращаемого значения, чтобы «заблокировать» их вместе:

int *sieve = malloc(length * sizeof *sieve);

Это также перемещает length вперед для увеличения видимости и удаляет лишние скобки с sizeof ; они нужны только тогда, когда аргумент является именем типа. Многие люди, кажется, не знают (или игнорируют) это, что делает их код более многословным. Помните: sizeof - это не функция! :)

Хотя перемещение length вперед может увеличить видимость в некоторых редких случаях, следует также обратить внимание на то, что в общем случае лучше написать выражение в виде:

int *sieve = malloc(sizeof *sieve * length);

Так как в этом случае сохранение sizeof первую очередь гарантирует, что умножение выполняется по крайней мере с помощью size_t math.

Сравните: malloc(sizeof *sieve * length * width) и malloc(length * width * sizeof *sieve) вторая может переполнить length * width когда width и length являются меньшими типами, чем size_t .





casting