Вывести результат malloc?

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

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

скорее, чем:

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

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

Нет ; вы не произносите результат, так как:

  • Это необязательно, так как void * автоматически и безопасно продвигается к любому другому типу указателя в этом случае.
  • Он добавляет беспорядок в код, отливки не очень легко читать (особенно если тип указателя длинный).
  • Это заставляет вас повторять себя, что обычно плохо.
  • Он может скрыть ошибку, если вы забыли включить . Это может привести к сбоям (или, что еще хуже, не привести к сбою до конца позже в какой-то совершенно другой части кода). Подумайте, что произойдет, если указатели и целые числа по-разному; то вы скрываете предупреждение путем кастинга и можете потерять бит вашего возвращенного адреса. Примечание: с 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 математикой.

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

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

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

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

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

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

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

Как и другие заявленные, он не нужен для 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-битных системах, где long и указатели являются 64-битными, а int – 32-разрядными), эта ошибка может фактически приводить к неопределенному поведению, поскольку неявно объявленный malloc возвращает 32- тогда как фактически определенная функция возвращает 64-битное значение. В зависимости от соглашений вызова и макета памяти это может привести к разбитию стека. В современных компиляторах эта проблема, скорее всего, останется незамеченной, поскольку они равномерно выдают предупреждения о том, что необъявленная функция использовалась, поэтому предупреждение все равно появится. Например, поведение GCC по умолчанию заключается в том, чтобы показать предупреждение, которое гласит «несовместимое неявное объявление встроенной функции» независимо от того, присутствует ли прилив или нет.

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

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

т.е.: если вам нужно скомпилировать 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 ++ с тем же исходным кодом, используйте переключатели компилятора, чтобы отметить различия. Не пытайтесь удовлетворить оба языковых стандарта одним и тем же кодом, потому что они несовместимы.

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

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

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

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

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

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

Ссылка: C FAQ

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

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

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

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

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

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

Добавление ко всей информации здесь; это то, что в справочном руководстве библиотеки GNU C :

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

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

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

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

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

 #include  #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++) { ... } 

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

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

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

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

Аргумент о том, что он не нужен в соответствии с действующими стандартами, вполне допустим. Но этот аргумент опускает практичность реального мира. Мы не кодируем в мире, управляемом исключительно стандартом дня, а практикой того, что мне нравится называть «областью реальности местного управления». И это было согнуто и искажено больше, чем просто пространство. 🙂

YMMV.

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

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

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

Мне жаль, что этого не было (и это не в C ++), и поэтому я бросил. Это мой вкус и моя политика программирования. Я не просто набрасываю указатель, но эффективно, бросаю бюллетень и изгоняю деmonoв глупости . If I can’t actually cast out stupidity , then at least let me express the wish to do so with a gesture of protest.

In fact, a good practice is to wrap malloc (and friends) with functions that return unsigned char * , and basically never to use void * in your code. If you need a generic pointer-to-any-object, use a char * or unsigned char * , and have casts in both directions. The one relaxation that can be indulged, perhaps, is using functions like memset and memcpy without casts.

On the topic of casting and C++ compatibility, if you write your code so that it compiles as both C and C++ (in which case you have to cast the return value of malloc when assigning it to something other than void * ), you can do a very helpful thing for yourself: you can use macros for casting which translate to C++ style casts when compiling as C++, but reduce to a C cast when compiling as C:

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

If you adhere to these macros, then a simple grep search of your code base for these identifiers will show you where all your casts are, so you can review whether any of them are incorrect.

Then, going forward, if you regularly compile the code with C++, it will enforce the use of an appropriate cast. For instance, if you use strip_qual just to remove a const or volatile , but the program changes in such a way that a type conversion is now involved, you will get a diagnostic, and you will have to use a combination of casts to get the desired conversion.

To help you adhere to these macros, the the GNU C++ (not C!) compiler has a beautiful feature: an optional diagnostic which is produced for all occurrences of C style casts.

     -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.

If your C code compiles as C++, you can use this -Wold-style-cast option to find out all occurrences of the (type) casting syntax that may creep into the code, and follow up on these diagnostics by replacing it with an appropriate choice from among the above macros (or a combination, if necessary).

This treatment of conversions is the single largest standalone technical justification for working in a “Clean C”: the combined C and C++ dialect, which in turn technically justifies casting the return value of malloc .

The concept behind void pointer is that it can be casted to any data type that is why malloc returns void. Also you must be aware of automatic typecasting. So it is not mandatory to cast the pointer though you must do it. It helps in keeping the code clean and helps debugging

The best thing to do when programming in C whenever it is possible:

  1. Make your program compile through a C compiler with all warnings turned on -Wall and fix all errors and warnings
  2. Make sure there are no variables declared as auto
  3. Then compile it using a C++ compiler with -Wall and -std=c++11 . Fix all errors and warnings.
  4. Now compile using the C compiler again. Your program should now compile without any warning and contain fewer bugs.

This procedure lets you take advantage of C++ strict type checking, thus reducing the number of bugs. In particular, this procedure forces you to include stdlib.h or you will get

malloc was not declared within this scope

and also forces you to cast the result of malloc or you will get

invalid conversion from void* to T*

or what ever your target type is.

The only benefits from writing in C instead of C++ I can find are

  1. C has a well specified ABI
  2. C++ may generate more code [exceptions, RTTI, templates, runtime polymorphism]

Notice that the second cons should in the ideal case disappear when using the subset common to C together with the static polymorphic feature.

For those that finds C++ strict rules inconvenient, we can use the C++11 feature with inferred type

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

No, you don’t cast the result of malloc() .

In general, you don’t cast to or from void * .

A typical reason given for not doing so is that failure to #include could go unnoticed. This isn’t an issue anymore for a long time now as C99 made implicit function declarations illegal, so if your compiler conforms to at least C99, you will get a diagnostic message.

But there’s a much stronger reason not to introduce unnecessary pointer casts:

In C, a pointer cast is almost always an error . This is because of the following rule ( §6.5 p7 in N1570, the latest draft for C11):

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.

This is also known as the strict aliasing rule . So the following code is undefined behavior :

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

And, sometimes surprisingly, the following is as well:

 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; 

Sometimes, you do need to cast pointers, but given the strict aliasing rule , you have to be very careful with it. So, any occurrence of a pointer cast in your code is a place you have to double-check for its validity . Therefore, you never write an unnecessary pointer cast.

ТЛ; др

In a nutshell: Because in C, any occurrence of a pointer cast should raise a red flag for code requiring special attention, you should never write unnecessary pointer casts.


Side notes:

  • There are cases where you actually need a cast to void * , eg if you want to print a pointer:

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

    The cast is necessary here, because printf() is a variadic function, so implicit conversions don’t work.

  • In C++, the situation is different. Casting pointer types is somewhat common (and correct) when dealing with objects of derived classes. Therefore, it makes sense that in C++, the conversion to and from void * is not implicit. C++ has a whole set of different flavors of casting.

I prefer to do the cast, but not manually. My favorite is using g_new and g_new0 macros from glib. If glib is not used, I would add similar macros. Those macros reduce code duplication without compromising type safety. If you get the type wrong, you would get an implicit cast between non-void pointers, which would cause a warning (error in C++). If you forget to include the header that defines g_new and g_new0 , you would get an error. g_new and g_new0 both take the same arguments, unlike malloc that takes fewer arguments than calloc . Just add 0 to get zero-initialized memory. The code can be compiled with a C++ compiler without changes.

A void pointer is a generic pointer and C supports implicit conversion from a void pointer type to other types, so there is no need of explicitly typecasting it.

However, if you want the same code work perfectly compatible on a C++ platform, which does not support implicit conversion, you need to do the typecasting, so it all depends on usability.

  1. As other stated, it is not needed for C, but for C++.

  2. Including the cast may allow a C program or function to compile as C++.

  3. In C it is unnecessary, as void * is automatically and safely promoted to any other pointer type.

  4. But if you cast then, it can hide an error if you forgot to include stdlib.h . This can cause crashes (or, worse, not cause a crash until way later in some totally different part of the code).

    Because stdlib.h contains the prototype for malloc is found. In the absence of a prototype for malloc, the standard requires that the C compiler assumes malloc returns an int. If there is no cast, a warning is issued when this integer is assigned to the pointer; however, with the cast, this warning is not produced, hiding a bug.

The casting of malloc is unnecessary in C but mandatory in C++.

  • Casting is unnecessary in C because of void * is automatically and safely promoted to any other pointer type in this case.
  • It can hide an error if you forgot to include . This can cause crashes.
  • If pointers and integers are differently sized, then you’re hiding a warning by casting and might lose bits of your returned address.

Please do yourself a favor and more importantly a favor for the next person who will maintain your code, and provide as much information as possible about the data type of a program’s variables.

Thus, cast the returned pointer from malloc . In the following code the compiler can be assured that sieve is in fact being assigned a point to an integer(s).

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

This reduces the chance for a human error when/if the data type for sieve is changed.

I would be interested in knowing if there are any “pure” C compilers that would flag this statement as being in error. If so, let me know, so that I can avoid them as their lack of type checking will increase the overall expense of maintaining software.

  • malloc () против HeapAlloc ()
  • free char *: недопустимый следующий размер (быстрый)
  • Объясните эту реализацию malloc из книги K & R
  • Установка переменной в NULL после
  • Почему это намеренно неправильное использование strcpy не терпит неудачу?
  • динамически распределенная память после завершения программы
  • В чем разница между «новыми» и «malloc» и «calloc» в C ++?
  • Является ли malloc streamобезопасным?
  • Как работают free и malloc в C?
  • выделить матрицу в C
  • Что произойдет, если я попытаюсь получить доступ к памяти за пределами области malloc ()?
  • Interesting Posts

    Как остановить Alt + Shift + 1, закрывая текущее приложение?

    Как я могу установить Linux на жесткий диск другого компьютера (тот, который не загружается с компакт-диска)

    Единичный тест на безопасность резьбы?

    Как принудительно использовать меню переполнения на устройствах с помощью кнопки меню

    Заполнение одного windows выбора на основе выбора в другом поле выбора – JQuery?

    Разница между методами добавления и расширения списка в Python

    Где можно загрузить исходный код JavaFX 2.2?

    Изменение значка меню «Пуск» в Windows 10

    Как показать несколько маркеров в MapFragment в Google Map API v2?

    Не удалось получить LocalDateTime от TemporalAccessor при анализе LocalDateTime (Java 8)

    Скопированный профиль пользователя, теперь получите «невозможно получить доступ к устройствам … соответствующие разрешения» в системных файлах

    Как заставить текст / кнопки заголовка лучше контрастировать с темными цветами в Windows 8?

    Изменение формата Date Java

    Проверка ввода массива с использованием модуля проверки правильности jquery

    CruiseControl против TeamCity для непрерывной интеграции?

    Давайте будем гением компьютера.