В этом вопросе кто-то предложил в комментарии, что я не должен приводить результат malloc
, т.е.
int *sieve = malloc(sizeof(int) * length);
скорее, чем:
int *sieve = (int *) malloc(sizeof(int) * length);
Почему это так?
В этом вопросе кто-то предложил в комментарии, что я не должен приводить результат malloc
, т.е.
int *sieve = malloc(sizeof(int) * length);
скорее, чем:
int *sieve = (int *) malloc(sizeof(int) * length);
Почему это так?
NULL
. (возможно, именно поэтому C ++ и представилnullptr
: C ++ не допускает никаких неявных приведений указателей)Ответы:
Нет ; Вы не разыгрываете результат, так как:
void *
в этом случае он автоматически и безопасно переводится на любой другой тип указателя.<stdlib.h>
. Это может вызывать сбои (или, что еще хуже, не приводить к сбою, пока в какой-то совершенно другой части кода). Подумайте, что произойдет, если указатели и целые числа имеют разные размеры; тогда вы скрываете предупреждение путем приведения и можете потерять биты вашего возвращенного адреса. Примечание: начиная с C99 неявные функции ушли из C, и эта точка больше не актуальна, так как нет автоматического предположения о возврате необъявленных функцийint
.В качестве пояснения обратите внимание, что я сказал: «Вы не читаете», а не «вам не нужно читать». На мой взгляд, это не удалось включить актерский состав, даже если вы правильно поняли. В этом просто нет никаких преимуществ, но куча потенциальных рисков, и в том числе актерский состав, указывает на то, что вы не знаете о рисках.
Также отметьте, как отмечают комментаторы, что вышесказанное говорит о прямом C, а не C ++. Я очень твердо верю в C и C ++ как отдельные языки.
Чтобы добавить дальше, ваш код без необходимости повторяет информацию о типе (
int
), которая может вызвать ошибки. Лучше отменить ссылку на указатель, используемый для хранения возвращаемого значения, чтобы «заблокировать» их вместе:Это также перемещает
length
вперед для увеличения видимости и удаляет лишние скобки сsizeof
; они нужны только тогда, когда аргумент является именем типа. Многие люди, кажется, не знают (или игнорируют) это, что делает их код более многословным. Помните:sizeof
это не функция! :)Хотя перемещение
length
в переднюю часть может улучшить видимость в некоторых редких случаях, следует также обратить внимание, что в общем случае лучше написать выражение в виде:Так как сохранение
sizeof
первого, в этом случае гарантирует, что умножение выполняется по крайней мере сsize_t
математикой.Для сравнения: по
malloc(sizeof *sieve * length * width)
сравнению сmalloc(length * width * sizeof *sieve)
второй может переполнить ,length * width
когдаwidth
иlength
более мелкие виды , чемsize_t
.источник
int x = (int) 12;
чтобы прояснить ситуацию?(int)12
это не сравнимо.12
являетсяint
, бросок не делает просто ничего. Возвращает значениеmalloc()
isvoid *
, а не тип указателя. (Если это не такvoid *
. Поэтому аналогия(int)12
будет тем,(void*)malloc(…)
что никто не обсуждает.)В C вам не нужно приводить возвращаемое значение
malloc
. Указатель на void, возвращаемыйmalloc
функцией, автоматически преобразуется в правильный тип. Однако, если вы хотите, чтобы ваш код компилировался с помощью компилятора C ++, необходимо преобразование. Предпочтительной альтернативой среди сообщества является использование следующего:что дополнительно освобождает вас от необходимости беспокоиться об изменении правой части выражения, если вы когда-либо измените тип
sieve
.Слепки плохие, как указали люди. Особенно указатель бросает.
источник
malloc(length * sizeof *sieve)
это выглядит какsizeof
переменная, так что я думаю, чтоmalloc(length * sizeof(*sieve))
она более читабельна.malloc(length * (sizeof *sieve))
еще более читабельным. ИМХО.()
кроме вопроса, обратите внимание, что предложенный вами стиль изменил порядок. Рассмотрим, когда количество элементов вычисляется примерно такlength*width
, сохраняяsizeof
первое в этом случае, обеспечим, чтобы умножение было выполнено хотя бы сsize_t
математикой. Сравнитеmalloc(sizeof( *ptr) * length * width)
сmalloc(length * width * sizeof (*ptr))
- 2-й может переполнить,length*width
когдаwidth,length
меньшие типы, чтоsize_t
.malloc(sizeof *sieve * length)
static_cast>()
(илиreinterpret_cast<>()
) не совместим ни с одним диалектом C.Вы делаете бросок, потому что:
type *
противtype **
.#include
найти подходящий заголовочный файл, пропускает лес для деревьев . Это то же самое, что сказать: «Не беспокойтесь о том, что вы не попросили компилятор жаловаться на то, что не видите прототипы - этот pesky stdlib.h - ДЕЙСТВИТЕЛЬНО важная вещь, которую нужно помнить!»malloc()
ошибки обнаруживаются намного быстрее, когда есть бросок. Как и с утверждениями, аннотации, раскрывающие намерения, уменьшают количество ошибок.источник
.c
/.cpp
file для компиляции, так как оба не очень полезны, но одним из случаев является добавлениеthrow
поддержки C ++ при компиляции с помощью компилятора C ++ (ноreturn -1;
при компиляции с помощью компилятора C или любого другого).malloc
вызове:char **foo = malloc(3*sizeof(*foo));
если она полностью доказана: 3 указателя на указатели на символы. затем цикл и делайfoo[i] = calloc(101, sizeof(*(foo[i])));
. Выделите массив из 101 символа, аккуратно инициализированный нулями. Не нужен актерский состав. в этом случае измените объявлениеunsigned char
или любой другой тип, и вы все еще в порядкеstruct Zebra *p; ... p=malloc(sizeof struct Zebra);
, malloc не может избежать дублирования информации о типе p, но ни компилятор, ни проверка локального кода не обнаружат никаких проблем, если один тип изменился, а другой - нет. Измените код на,p=(struct Zebra*)malloc(sizeof struct Zebra);
и компилятор закричит, если тип приведения не совпадаетp
, и локальная проверка покажет ...Как говорили другие, это не нужно для C, но необходимо для C ++. Если вы думаете, что собираетесь скомпилировать свой код C с помощью компилятора C ++, по каким-либо причинам вы можете вместо этого использовать макрос, например:
Таким образом, вы все еще можете написать это очень компактно:
и он скомпилируется для C и C ++.
источник
new
в определении C ++?new
вы должны использовать,delete
и если вы используетеmalloc()
вы должныfree()
. Никогда не смешивайте их.NEW
, вероятно, является плохой идеей, поскольку ресурс никогда не возвращается с использованиемdelete
(илиDELETE
), поэтому вы смешиваете свой словарный запас. Вместо этого, назвав егоMALLOC
, или, скорее,CALLOC
в этом случае, имело бы больше смысла.Из Википедии :
Хотя malloc без преобразования является предпочтительным методом, и большинство опытных программистов выбирают его , вы должны использовать все, что захотите, зная о проблемах.
То есть: если вам нужно скомпилировать программу на C как C ++ (хотя это отдельный язык), вы должны привести результат использования
malloc
.источник
malloc()
вызова »? Не могли бы вы привести пример?p = malloc(sizeof(*p) * count)
идиома автоматически регистрирует изменения в типе, поэтому вам не нужно получать предупреждения и что-либо менять. Так что это не реальное преимущество по сравнению с лучшей альтернативой для не-каста.В C вы можете неявно преобразовывать
void
указатель в любой другой тип указателя, поэтому приведение не требуется. Использование одного может подсказать случайному наблюдателю, что есть какая-то причина, по которой он нужен, что может ввести в заблуждение.источник
Вы не приводите результат malloc, потому что это добавляет бесполезный беспорядок в ваш код.
Наиболее распространенная причина, по которой люди приводят результат malloc, заключается в том, что они не уверены в том, как работает язык Си. Это предупреждающий знак: если вы не знаете, как работает тот или иной языковой механизм, не стоит угадывать. Посмотрите это или спросите о переполнении стека.
Некоторые комментарии:
Пустой указатель может быть преобразован в / из любого другого типа указателя без явного преобразования (C11 6.3.2.3 и 6.5.16.1).
Однако C ++ не допускает неявное приведение между
void*
другим типом указателя. Так что в C ++ приведение было бы правильным. Но если вы программируете на C ++, вы должны использовать,new
а не malloc (). И вы никогда не должны компилировать код C, используя компилятор C ++.Если вам нужно поддерживать C и C ++ с одним и тем же исходным кодом, используйте переключатели компилятора, чтобы отметить различия. Не пытайтесь насытить оба языковых стандарта одним и тем же кодом, потому что они несовместимы.
Если компилятор C не может найти функцию, потому что вы забыли включить заголовок, вы получите сообщение об ошибке компилятора / компоновщика. Так что, если вы забыли включить
<stdlib.h>
это не важно, вы не сможете создать свою программу.На древних компиляторах, которые следуют версии стандарта, которой более 25 лет, забывание о включении
<stdlib.h>
может привести к опасному поведению. Потому что в этом древнем стандарте функции без видимого прототипа неявно преобразовывали возвращаемый тип вint
. Явное приведение результата из malloc могло бы скрыть эту ошибку.Но это действительно не проблема. Вы не используете компьютер 25 лет, так зачем вам использовать компилятор 25 лет?
источник
В C вы получаете неявное преобразование из
void *
любого другого указателя (данных).источник
Приведение значения, возвращаемого
malloc()
теперь не является необходимым, но я хотел бы добавить один момент, который, кажется, никто не указал:В древние времена, т. Е. До того, как ANSI C предоставил
void *
общий тип указателей,char *
это тип для такого использования. В этом случае приведение может отключить предупреждения компилятора.Ссылка: C FAQ
источник
Просто добавив мой опыт, изучая компьютерную инженерию, я вижу, что два или три профессора, которых я видел, пишу на C, всегда бросают malloc, однако тот, кого я спросил (с огромным резюме и пониманием C), сказал мне, что это абсолютно ненужно, но Раньше он был абсолютно конкретным и привел студентов к менталитету быть абсолютно конкретным. По сути, приведение ничего не изменит в том, как оно работает, оно делает именно то, что говорит, выделяет память, и приведение не влияет на это, вы получаете ту же память, и даже если вы приведете ее к чему-то другому по ошибке (и каким-то образом уклонитесь от компилятора ошибки) C будет обращаться к нему таким же образом.
Изменить: кастинг имеет определенную точку. Когда вы используете нотацию массива, сгенерированный код должен знать, сколько мест в памяти нужно продвинуть, чтобы достичь начала следующего элемента, это достигается с помощью приведения. Таким образом, вы знаете, что для двойника вы идете на 8 байтов вперед, а для целого - 4 и так далее. Таким образом, это не имеет никакого эффекта, если вы используете нотацию указателя, в нотации массива это становится необходимым.
источник
p = malloc(sizeof *p * n);
все так просто и лучше.Не обязательно приводить результаты
malloc
, так как он возвращаетvoid*
, и avoid*
может указывать на любой тип данных.источник
Указатель void является указателем общего объекта, и C поддерживает неявное преобразование из типа указателя void в другие типы, поэтому нет необходимости явно указывать его тип.
Однако если вы хотите, чтобы один и тот же код работал идеально совместимым на платформе C ++, которая не поддерживает неявное преобразование, вам необходимо выполнить приведение типов, так что все зависит от удобства использования.
источник
malloc
and friends в C ++ является хорошим предупреждением о том, что оно заслуживает особого внимания (или переписывания в C).void *
, чтоvoid *
недостаточно для правильного хранения указателя на функцию.Вот что говорит Справочное руководство по библиотеке GNU C :
И действительно, стандарт ISO C11 (p347) гласит:
источник
Возвращаемый тип void *, который может быть приведен к нужному типу указателя данных, чтобы иметь возможность обращения к нему.
источник
void*
может быть приведен к нужному типу, но в этом нет необходимости, поскольку он будет автоматически преобразован. Таким образом, актерский состав не является необходимым и фактически нежелательным по причинам, указанным в ответах с высокой оценкой.В языке C указатель void может быть назначен любому указателю, поэтому не следует использовать приведение типа. Если вы хотите «безопасное распределение типов», я могу порекомендовать следующие макро-функции, которые я всегда использую в своих проектах на C:
С этим на месте вы можете просто сказать
Для нединамических массивов третий обязательный макрос функции
что делает циклы массива более безопасными и удобными:
источник
malloc()
единственная.void*
указателю функции функции to / from может привести к потере информации, поэтому «указатель void может быть назначен любому указателю» - проблема в этих случаях. Присвоениеvoid*
, frommalloc()
любому указателю объекта не является проблемой.do
Петля комментарии , связанные с макросами , включающих цикл еще , что интересно , от заголовка вопроса. Удаление этого комментария. Снесу этот позже тоже.Это зависит от языка программирования и компилятора. Если вы используете
malloc
в C, нет необходимости вводить cast, так как он автоматически набирает cast. Однако, если вы используете C ++, вы должны набрать cast, потомуmalloc
что вернетvoid*
тип.источник
Люди привыкли к GCC и Clang испорчены. Там не все так хорошо.
Я был довольно напуган за эти годы потрясающе старыми компиляторами, которые я должен был использовать. Часто компании и менеджеры применяют ультраконсервативный подход к изменению компиляторов и даже не тестируют будет ли работать новый компилятор (с лучшим соответствием стандартам и оптимизацией кода) в их системе. Практическая реальность для работающих разработчиков заключается в том, что когда вы пишете код, вам нужно охватить свои базы, и, к сожалению, приведение malloc - хорошая привычка, если вы не можете контролировать, какой компилятор может применяться к вашему коду.
Я также хотел бы предложить, чтобы многие организации применяли собственный стандарт кодирования и что должно быть методом, которым люди следуют, если он определен. В отсутствие четких указаний я склоняюсь скорее к компиляции везде, чем к рабской приверженности стандарту.
Аргумент, что в нынешних стандартах это не нужно, вполне обоснован. Но этот аргумент опускает практические аспекты реального мира. Мы пишем код не в мире, управляемом исключительно стандартами дня, а практиками того, что я люблю называть «полем реальности местного управления». И это изгибается и искривляется больше, чем когда-либо было пространство-время. :-)
YMMV.
Я склонен думать о том, чтобы использовать malloc как оборонительную операцию. Не красиво, не идеально, но в целом безопасно. (Честно говоря, если вы не включены stdlib.h , то вы имеете пути больше проблем , чем литье таНоса!).
источник
Я вставил приведение просто, чтобы показать неодобрение уродливой дыры в системе типов, которая позволяет скомпилировать код, такой как следующий фрагмент, без диагностики, даже если никакие преобразования не используются для плохого преобразования:
Я хотел бы, чтобы этого не было (и это не существует в C ++), и поэтому я разыграл. Это представляет мой вкус и мою политику программирования. Я не только разыгрываю указатель, но и эффективно, разыгрываю бюллетени и изгоняю бесов глупости . Если я на самом деле не могу изгнать глупость , то, по крайней мере, позвольте мне выразить желание сделать это с жестом протеста.
На самом деле, хорошей практикой является использование
malloc
(и друзей) функций, которые возвращаютсяunsigned char *
и в основном никогда не используютсяvoid *
в вашем коде. Если вам нужен общий указатель на любой объект, используйтеchar *
илиunsigned char *
, и приведите в обоих направлениях. Единственное расслабление, которое можно потворствовать, возможно, это использование таких функций, какmemset
иmemcpy
без приведения.Что касается приведения типов и совместимости с C ++, если вы пишете свой код так, чтобы он компилировался как на C, так и на C ++ (в этом случае вы должны приводить возвращаемое значение
malloc
при назначении ему чего-то другого, чемvoid *
), вы можете сделать очень полезную вещь для себя: вы можете использовать макросы для приведения, которые преобразуются в приведения в стиле C ++ при компиляции в C ++, но уменьшаются до приведения в C при компиляции в C:Если вы придерживаетесь этих макросов, то простой
grep
поиск в вашей кодовой базе по этим идентификаторам покажет вам, где находятся все ваши приведения, и вы сможете проверить, являются ли какие-либо из них неправильными.Затем, в дальнейшем, если вы регулярно компилируете код с C ++, он принудительно использует соответствующее приведение. Например, если вы используете
strip_qual
только для удаленияconst
илиvolatile
, но программа изменяется таким образом, что теперь происходит преобразование типов, вы получите диагностику и вам придется использовать комбинацию приведений, чтобы получить желаемое преобразование.Чтобы помочь вам придерживаться этих макросов, у компилятора GNU C ++ (не C!) Есть прекрасная функция: дополнительная диагностика, которая создается для всех случаев приведения типов в стиле C.
Если ваш код на C компилируется как C ++, вы можете использовать эту
-Wold-style-cast
опцию, чтобы выяснить все вхождения(type)
синтаксиса приведения, которые могут проникнуть в код, и проследить за этими диагностиками, заменив его подходящим выбором из перечисленных выше макросов (или комбинация, если необходимо).Такая обработка преобразований является единственным крупнейшим техническим обоснованием для работы в «Чистом С»: комбинированный диалект С и С ++, который, в свою очередь, технически оправдывает приведение возвращаемого значения
malloc
.источник
Лучшее, что можно сделать при программировании на C, когда это возможно:
-Wall
и исправляла все ошибки и предупрежденияauto
-Wall
и-std=c++11
. Исправьте все ошибки и предупреждения.Эта процедура позволяет вам использовать строгую проверку типов в C ++, тем самым уменьшая количество ошибок. В частности, эта процедура заставляет вас включить
stdlib.h
или вы получитеа также заставляет вас разыграть результат
malloc
или вы получитеили каков ваш целевой тип.
Единственная выгода от написания на C вместо C ++, которую я могу найти:
Обратите внимание, что вторые минусы в идеальном случае должны исчезнуть при использовании общего для C подмножества вместе со статической полиморфной функцией.
Для тех, кто считает строгие правила C ++ неудобными, мы можем использовать функцию C ++ 11 с выведенным типом
источник
gcc -c c_code.c
), код C ++ как C ++ (напримерg++ -c cpp_code.cpp
), а затем связать их вместе (например,gcc c_code.o cpp_code.o
или наоборот, в зависимости от зависимости проекта). Теперь не должно быть причин лишать себя каких-либо приятных черт любого языка ...p = malloc(sizeof(*p));
, что не требует изменения в первую очередь, еслиp
меняется имя другого типа. Предлагаемое «преимущество» приведения заключается в том, что вы получаете ошибку компиляции, еслиp
неправильный тип, но даже лучше, если он просто работает.Нет, вы не разыгрываете результат
malloc()
.В общем, вы не бросаете в или из
void *
.Типичная причина, по которой они этого не делают, заключается в том, что неудача
#include <stdlib.h>
может остаться незамеченной. Это уже давно не проблема, поскольку C99 сделал неявные объявления функций недопустимыми, поэтому, если ваш компилятор соответствует хотя бы C99, вы получите диагностическое сообщение.Но есть гораздо более веская причина не вводить ненужные приведения указателей:
В C приведение указателя почти всегда является ошибкой . Это из-за следующего правила ( §6.5 p7 в N1570, последний проект для C11):
Это также известно как строгое правило наложения имен . Таким образом, следующий код является неопределенным поведением :
И, иногда удивительно, следующее также:
Иногда вам действительно нужно литые указатели, но , учитывая строгое правило сглаживания , вы должны быть очень осторожны с ним. Таким образом, любое вхождение указателя в вашем коде - это место, где вы должны дважды проверить его правильность . Следовательно, вы никогда не пишете ненужный указатель приведения.
ТЛ; др
В двух словах: поскольку в C любое появление приведения указателя должно поднимать красный флаг для кода, требующего особого внимания, вы никогда не должны писать ненужные приведения указателя.
Примечания стороны:
Существуют случаи, когда вам действительно требуется приведение к
void *
, например, если вы хотите напечатать указатель:Приведение здесь необходимо, потому что
printf()
это функция с переменным числом, поэтому неявные преобразования не работают.В C ++ ситуация иная. Приведение типов указателей является довольно распространенным (и правильным) при работе с объектами производных классов. Следовательно, имеет смысл, что в C ++ преобразование в и из не
void *
является неявным. C ++ имеет целый набор различных форм кастинга.источник
Я предпочитаю сниматься, но не вручную. Мой любимый использует
g_new
иg_new0
макросы из glib. Если glib не используется, я бы добавил похожие макросы. Эти макросы уменьшают дублирование кода без ущерба для безопасности типов. Если вы ошиблись в типе, вы бы получили неявное приведение между не пустыми указателями, что вызвало бы предупреждение (ошибка в C ++). Если вы забудете включить заголовок, который определяетg_new
иg_new0
, вы получите ошибку.g_new
иg_new0
оба принимают одинаковые аргументы, в отличие отmalloc
этого требует меньше аргументов, чемcalloc
. Просто добавьте,0
чтобы получить нулевую инициализированную память. Код может быть скомпилирован с помощью компилятора C ++ без изменений.источник
Кастинг только для C ++, а не для C. В случае, если вы используете компилятор C ++, лучше поменяйте его на компилятор C.
источник
Концепция за указателем void заключается в том, что он может быть приведен к любому типу данных, поэтому malloc возвращает void. Также вы должны знать об автоматической настройке типов. Так что указывать указатель не обязательно, хотя вы должны это сделать. Это помогает содержать код в чистоте и помогает отладке
источник
Указатель void является универсальным указателем, и C поддерживает неявное преобразование из типа указателя void в другие типы, поэтому нет необходимости явно указывать его типизацию.
Однако если вы хотите, чтобы один и тот же код работал идеально совместимым на платформе C ++, которая не поддерживает неявное преобразование, вам необходимо выполнить приведение типов, так что все зависит от удобства использования.
источник
Как уже говорилось, это не нужно для C, но для C ++.
Включение преобразования может позволить программе или функции на C компилироваться как C ++.
В C это не нужно, поскольку void * автоматически и безопасно переводится в любой другой тип указателя.
Но если вы приведете его, он может скрыть ошибку, если вы забыли включить stdlib.h . Это может вызывать сбои (или, что еще хуже, не приводить к сбою, пока в какой-то совершенно другой части кода).
Поскольку stdlib.h содержит прототип для malloc найден. В отсутствие прототипа для malloc стандарт требует, чтобы компилятор C предполагал, что malloc возвращает int. Если нет приведения, выдается предупреждение, когда это целое число присваивается указателю; однако, с использованием приведения это предупреждение не выдается, скрывая ошибку.
источник
Приведение malloc не нужно в C, но обязательно в C ++.
Приведение не нужно в C из-за:
void *
автоматически и безопасно повышается до любого другого типа указателя в случае C.<stdlib.h>
. Это может вызвать сбои.malloc
вызывается и приводится.С другой стороны, приведение может увеличить переносимость вашей программы. то есть, он позволяет программе или функции на C компилироваться как C ++.
источник
Для меня возвращение домой и вывод здесь - это то, что приведение
malloc
в C совершенно НЕ обязательно, но если вы все же произнесете, это не повлияет, такmalloc
какmalloc
все равно выделит вам запрошенное вами благословенное пространство памяти. Другой способ - это причина или одна из причин, по которой люди выполняют кастинг, и это позволяет им компилировать одну и ту же программу на C или C ++.Могут быть и другие причины, но другие причины, почти наверняка, рано или поздно приведут к серьезным неприятностям.
источник
Вы можете, но не обязательно приводить в C. Вы должны приводить, если этот код скомпилирован как C ++.
источник