Я бросаю результат malloc?

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

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

вместо:

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

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

26 ответов


нет, вы не приведите результат, так как:

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

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

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

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

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

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


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

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

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

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


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

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

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

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


вы do cast, потому что:

  • это делает ваш код портативный между C и C++, и, как показывает опыт, многие программисты утверждают, что они пишут на C, когда они действительно пишут на C++ (или C плюс локальные расширения компилятора).
  • не так может скрыть ошибку: обратите внимание на все так примеры путаницы, когда писать type * и type **.
  • идея, что это держит вас от заметив, что вы не смогли #include соответствующий файл заголовка пропускает лес для деревьев. Это то же самое, что сказать: "не беспокойтесь о том, что вы не смогли попросить компилятор пожаловаться на то, что не видите прототипы-этот надоедливый stdlib.h-это действительно важная вещь, которую нужно помнить!"
  • это заставляет дополнительная когнитивная перекрестная проверка. Он помещает (предполагаемый) желаемый тип рядом с арифметикой, которую вы делаете для необработанного размера этой переменной. Держу пари, ты мог бы. сделайте так исследование, которое показывает, что 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++.


С Википедия

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

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

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

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

недостатки литья

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

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

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

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

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


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


вы не бросаете результат malloc, потому что это добавляет бессмысленный беспорядок в ваш код.

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

некоторые комментарии:

  • указатель void может быть преобразуется в / из любого другого типа указателя без явного приведения (C11 6.3.2.3 и 6.5.16.1).

  • C++, однако, не позволит неявное приведение между void* и другой тип указателя. Таким образом, в C++ приведение было бы правильным. Но если вы программируете на C++, вы должны использовать new и не malloc (). И вы никогда не должны компилировать код с помощью компилятора C++.

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

  • если компилятор C не может найти функцию, потому что вы забыли включить заголовок, вы получите ошибку компилятора/компоновщика об этом. Поэтому, если вы забыли включить <stdlib.h> это не важно, вы не сможете построить свою программу.

  • на древних компиляторах, которые следуют версии стандарта, которая больше, чем 25 лет, забыв включить <stdlib.h> приведет к опасному поведению. Потому что в этом древнем стандарте функции без видимого прототипа неявно преобразовывали возвращаемый тип в int. Приведение результата из malloc явно скрыло бы эту ошибку.

    но это действительно не проблема. Вы не используете 25-летний компьютер, так почему бы вам использовать 25-летний компилятор?


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


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

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

ссылки: C FAQ


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


просто добавляя мой опыт, изучая Компьютерную инженерию, я вижу, что два или три профессора, которых я видел, пишущих на C, всегда бросали Мэллок, однако тот, которого я спросил (с огромным резюме и пониманием C), сказал мне, что это абсолютно ненужно, но только раньше было абсолютно конкретным, и чтобы получить студентов в менталитет быть абсолютно конкретным. По сути, кастинг ничего не изменит в том, как он работает, он делает именно то, что говорит, выделяет память и кастинг не влияет на него, вы получаете ту же память, и даже если вы бросаете ее на что-то другое по ошибке (и как-то уклоняетесь от ошибок компилятора), C будет обращаться к ней таким же образом.

Edit: кастинг имеет определенный момент. Когда вы используете нотацию массива, сгенерированный код должен знать, сколько мест памяти он должен продвигаться, чтобы достичь начала следующего элемента, это достигается путем литья. Таким образом, вы знаете, что для двойника вы идете на 8 байт вперед, а для int вы идут 4, и так далее. Таким образом, это не имеет никакого эффекта, если вы используете нотацию указателя, в нотации массива это становится необходимым.


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

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


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


Это ссылка на библиотеку GNU C руководство говорит:

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

да и вообще стандарт ISO C11 (р347)говорит так:--5-->

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


Это зависит от языка программирования и компилятора. Если вы используете malloc В C нет необходимости вводить cast, так как он будет автоматически вводить cast, однако, если вы используете C++, вы должны ввести cast, потому что malloc вернет 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++) {
   ...
}

люди, привыкшие к GCC и Clang, испорчены. Там не все так хорошо.

Я был в ужасе на протяжении многих лет ошеломляюще старыми компиляторами, которые я должен был использовать. Часто компании и менеджеры принимают ультраконсервативный подход к изменению компиляторов и даже не будут тест если новый компилятор (с лучшим соответствием стандартам и оптимизацией кода ) будет работать в их системе. Практическая реальность для работающих разработчиков заключается в том, что когда вы кодируете, вам нужно охватить свои базы, и, к сожалению, кастинг mallocs-хорошая привычка, если вы не можете контролировать, какой компилятор может быть применен к вашему коду.

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

аргумент, что это не обязательно в соответствии с текущими стандартами, вполне обоснован. Но этот аргумент опускает практические аспекты реального мира. Мы не кодируем в мире, управляемом исключительно стандартами дня, но практичностью того, что я люблю называть "полем реальности местного управления". И это искривлено и искривлено больше, чем когда-либо было пространство-время. :-)

YMMV.

Я склонен думать о кастинге Маллока как об оборонительной операции. Не красивая, не идеальная, но в целом безопасно. (Честно говоря, если вы не включили stdlib.h тогда вы путь больше проблем, чем кастинг malloc ! ).


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

double d;
void *p = &d;
int *q = p;

Я бы хотел, чтобы этого не было (и это не в C++), и поэтому я бросил. Она представляет мой вкус и мою программную политику. Я не только бросаю указатель, но и эффективно, бросая бюллетень, и изгнание демонов глупость!--23-->. Если не смогу!--24-->на самом деле изгонять дурость, тогда, по крайней мере, позвольте мне выразить желание сделать это жестом протеста.

на самом деле, хорошая практика-обернуть malloc (и друзей) с функциями, которые возвращают unsigned char *, и, в принципе, никогда не использовать void * в коде. Если вам нужен общий указатель на любой объект, используйте char * или unsigned char *, и бросает в обоих направлениях. Единственное расслабление, которое можно себе позволить, возможно, это использование таких функций, как memset и memcpy без забросов.

по теме кастинга и совместимости c++, если вы пишете свой код так, чтобы он компилировался как C, так и C++ (в этом случае вы обязательно приведите возвращаемое значение malloc при назначении его чему-то другому, чем void *), вы можете сделать очень полезную вещь для себя: вы можете использовать макросы для литья, которые переводят в стиль C++ при компиляции как C++, но уменьшают до c cast при компиляции как C:

/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif

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

затем, продвигаясь вперед, если вы регулярно компилируете код с C++, он будет обеспечивать использование соответствующего приведения. Например, если вы используете strip_qual просто удалить const или volatile, но программа изменяется таким образом, что преобразование типа теперь, вы получите диагностику, и вам придется использовать комбинацию слепков, чтобы получить желаемые преобразования.

чтобы помочь вам придерживаться этих макросов, GNU C++ (не C!) компилятор имеет красивую функцию: необязательную диагностику,которая производится для всех вхождений приведений стиля C.

     -Wold-style-cast (C++ and Objective-C++ only)
         Warn if an old-style (C-style) cast to a non-void type is used
         within a C++ program.  The new-style casts (dynamic_cast,
         static_cast, reinterpret_cast, and const_cast) are less vulnerable
         to unintended effects and much easier to search for.

если ваш код C компилируется как C++, вы можете использовать этот -Wold-style-cast возможность узнать все вхождения (type) синтаксис кастинга, который может проникнуть в код, и следите за этими диагностиками, заменив их соответствующим выбором из вышеперечисленных макросов (или комбинацией, если это необходимо).

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


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


лучшее, что можно сделать при программировании на 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...

нет, вы не бросаете результат malloc().

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

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

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

в C, a наведение указателя почти всегда является ошибкой. Это происходит из-за следующего правила (§Р7 6.5 в 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;

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

tl; dr

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


побочные Примечания:

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

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

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

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


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


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


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

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


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

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

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

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

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


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

кастинг не нужен в C из-за:

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

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