Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Why it is important to check what the malloc function returned

286 views
Skip to first unread message

Andrey Karpov

unread,
Feb 1, 2018, 12:10:07 PM2/1/18
to
We'd like to present the series of articles dealing with the recommendations on writing code of high quality using the examples of errors found in the Chromium project. This is the 6th part, which focuses on the malloc function. Or rather, why you should always check the pointer returned by this function. Most likely, you don't have a clue what's the catch with malloc, so we recommend looking through this article. https://www.viva64.com/en/b/0558/

Chris M. Thomasson

unread,
Feb 1, 2018, 2:59:17 PM2/1/18
to
On 2/1/2018 9:09 AM, Andrey Karpov wrote:
> We'd like to present the series of articles dealing with the recommendations on writing code of high quality using the examples of errors found in the Chromium project. This is the 6th part, which focuses on the malloc function. Or rather, why you should always check the pointer returned by this function. Most likely, you don't have a clue what's the catch with malloc, so we recommend looking through this article. https://www.viva64.com/en/b/0558/
>

These examples are pretty fuc%ed up man!
__________________
static SubStr *
SubStr_new_u(unsigned char *s, unsigned int l)
{
SubStr *r = malloc(sizeof(SubStr));
r->str = (char*)s;
r->len = l;
return r;
}
__________________

Sorry buddy, but you are fired!

Alf P. Steinbach

unread,
Feb 1, 2018, 3:55:37 PM2/1/18
to
That's C code (hence the lack of cast of `malloc` result, which is
technically correct and preferable in C, opposite of C++: it wouldn't
compile as C++, hence it must be C).

Andrey evidently posted to the wrong group. He should have posted to
comp.lang.c.

Disclaimer: I haven't looked at the linked-to article.


Cheers!,

- Alf

woodb...@gmail.com

unread,
Feb 1, 2018, 6:22:07 PM2/1/18
to
On Thursday, February 1, 2018 at 1:59:17 PM UTC-6, Chris M. Thomasson wrote:

Please don't $wear here.


Brian
Ebenezer Enterprises
http://webEbenezer.net

Chris M. Thomasson

unread,
Feb 1, 2018, 9:20:24 PM2/1/18
to
On 2/1/2018 3:21 PM, woodb...@gmail.com wrote:
> On Thursday, February 1, 2018 at 1:59:17 PM UTC-6, Chris M. Thomasson wrote:
>
> Please don't $wear here.

I slightly obfuscated the swear word.

Chris M. Thomasson

unread,
Feb 1, 2018, 9:24:14 PM2/1/18
to
Fwiw, C aside for a moment, the massive error is the use of a pointer
returned from malloc without checking it for NULL.

Öö Tiib

unread,
Feb 2, 2018, 4:57:25 AM2/2/18
to
Karpov just wants to promote his tool and so writes such nausea
and does not care nor understand that incorrect usages of strdup
in some C code isn't topical here.

Arrogance and ignorance of author do not make his tool to look better
and so it is most likely unpopular.

David Brown

unread,
Feb 2, 2018, 6:43:07 AM2/2/18
to
I presume your complaint is about using malloc in a C++ program? And
compounding that with a missing cast.

Certainly it is not /necessarily/ a problem that the return from malloc
is not checked. Rules about always checking for error returns are like
pretty much every other "absolute" rule - it's an excuse for not thinking.

Manfred

unread,
Feb 2, 2018, 7:31:38 AM2/2/18
to
Still this is off-topic in a C++ newsgroup.
Constructs like
void *myPointer = foo();
if(myPointer == NULL) {
log("something");
exit(EXIT_FAILURE);
}

are ubiquitous in C code, but this is one major reason for which Bjarne
introduced exceptions in C++.

Rick C. Hodgin

unread,
Feb 2, 2018, 8:17:14 AM2/2/18
to
On 2/2/2018 4:57 AM, Öö Tiib wrote:
> Karpov just wants to promote his tool...

I agree. I know my posts teaching about Jesus Christ bother
people, but Karpov's posts are thinly veiled "Hey, come buy
my software" posts.

I find that kind of for-money solicitation quite offensive.

--
Thank you! | Indianapolis, Indiana | God is love -- 1 John 4:7-9
Rick C. Hodgin | http://www.libsf.org/ | http://tinyurl.com/yaogvqhj
-------------------------------------------------------------------------
Software: LSA, LSC, Debi, RDC/CAlive, ES/1, ES/2, VJr, VFrP, Logician
Hardware: Arxoda Desktop CPU, Arxita Embedded CPU, Arlina Compute FPGA

David Brown

unread,
Feb 2, 2018, 8:52:59 AM2/2/18
to
On 02/02/18 14:16, Rick C. Hodgin wrote:
> On 2/2/2018 4:57 AM, Öö Tiib wrote:
>> Karpov just wants to promote his tool...
>
> I agree. I know my posts teaching about Jesus Christ bother
> people, but Karpov's posts are thinly veiled "Hey, come buy
> my software" posts.
>

His tool is on-topic, being C++ (except when he accidentally talks about
C). It is also available without cost in certain circumstances, and
some of his blog posts can be instructive. And he has been know at
least occasionally to read replies here and enter discussions.

So his posts are spam, but not the worst kind of spam. If he were to
take a more active part in discussions here, it would be positive.
(Compare with Mr. Flibble - he talks a lot about neoGFX, which he plans
to sell for money - but in c.l.c++ he talks mainly about the C++ aspects
and leads to interesting discussions, and does not push the sales
aspect. Karpov could learn from that.)

> I find that kind of for-money solicitation quite offensive.
>

Your religious solicitations are at least as offensive. If there is the
slightest vein of truth in what you write, the prices being asked and
the prizes offered are worth more than any money - and therefore the
spam is worse than mere commercial postings.


James R. Kuyper

unread,
Feb 2, 2018, 9:09:25 AM2/2/18
to
On 02/02/2018 06:42 AM, David Brown wrote:
> On 01/02/18 20:59, Chris M. Thomasson wrote:
>> On 2/1/2018 9:09 AM, Andrey Karpov wrote:
>>> We'd like to present the series of articles dealing with the
>>> recommendations on writing code of high quality using the examples of
>>> errors found in the Chromium project. This is the 6th part, which
>>> focuses on the malloc function. Or rather, why you should always check
>>> the pointer returned by this function. Most likely, you don't have a
>>> clue what's the catch with malloc, so we recommend looking through
>>> this article. https://www.viva64.com/en/b/0558/
>>>
>>
>> These examples are pretty fuc%ed up man!
>> __________________
>> static SubStr *
>> SubStr_new_u(unsigned char *s, unsigned int l)
>> {
>> SubStr *r = malloc(sizeof(SubStr));
>> r->str = (char*)s;
>> r->len = l;
>> return r;
>> }
>> __________________
>>
>> Sorry buddy, but you are fired!
>
> I presume your complaint is about using malloc in a C++ program? And
> compounding that with a missing cast.

Using malloc in a C++ program is a poor choice. The missing cast makes
the program ill-formed in C++. However, the failure to check whether r
is null before dereferencing could result in undefined behavior - that's
the firing offense.

> Certainly it is not /necessarily/ a problem that the return from malloc
> is not checked. ...

Yes, it is necessarily a problem. Any particular return from malloc()
might be the one that will fail, no matter how small it is, and the
potential consequences have no upper limit on how severe they can be.

> ... Rules about always checking for error returns are like
> pretty much every other "absolute" rule - it's an excuse for not thinking.

Rules intended to avoid the execution of code with undefined behavior
(unless portability of the code is intentionally restricted to contexts
where something other than the C++ standard provides its own definition
for the behavior) are entirely appropriate - "not thinking" about the
consequences is the only excuse for ignoring such rules.
Is there anything that your computer could be programmed do, that you
wouldn't want it to do? Think about the worst such thing - that's one of
the permitted consequences of executing code with undefined behavior.
How far would you be willing to go to prevent that worst case from
happening? That's how far you should go to avoid executing code with
undefined behavior.

Öö Tiib

unread,
Feb 2, 2018, 9:17:03 AM2/2/18
to
You mean in situations like that:
* the null pointer access causes guaranteed SIGSEGV on platform
* all checks of failed malloc would do same what SIGSEGV handler does
Or that:
* the platform never fails malloc but instead kills processes with OOM reaper
These feel quite close to most common situations. ;)

David Brown

unread,
Feb 2, 2018, 10:23:42 AM2/2/18
to
That is three statements, all of which are incorrect.

No, malloc will not necessarily have a possibility of failing -
certainly not if you think in terms of /realistic/ possibilities. If
you run your program on your 8 GB Linux desktop, and it asks for 100 KB
from malloc, it will /not/ fail. There is /no/ realistic chance of it
failing. You would be better worrying about a lightening strike
blasting the PC to bits than worrying about the malloc failing.

There is a wide range of programs in which a malloc can reasonably be
expected to fail - but there is also a wide range of circumstances in
which it cannot.

Next, there most certainly /can/ be upper limits on the potential
consequences of a failure. These will be highly dependent on the code
in question, the compiler, the OS, etc. But if we again take the
example code above and assume it is running on a reasonably modern Linux
or Windows system, then the consequences will be clear - you will get a
segmentation fault when trying to write to address 0 (or slightly higher
than 0). In some use-cases, that is absolutely fine - having the
program exit in a controlled manner on a critical failure is often
entirely reasonable.

You can also consider embedded systems with more specialised libraries
in which there is no way for malloc to return 0 because the system
restarts the whole device rather than return a failure. Or perhaps you
know it can never return 0 because you have calculated the total
malloc'ed allocations you need, and are sure that you have enough heap
space (this is, in fact, extremely common in small embedded systems
where the possibility of a malloc failure is not acceptable as part of
the design - and since it is eliminated by the development process, it
does not need to be checked at run-time).

Checking for error results is not free. It takes thought, and lines of
code. It disrupts the flow of the code. It is often difficult or
impossible to test in a sensible manner. Obsessions about "always check
this" lead to poorly considered, poorly documented and poorly tested
error handling that is not necessarily any better than /no/ error
checking. Is an "exit(1)" call /really/ better than a segmentation
fault? Maybe - maybe not. Is passing the null pointer back to the
caller any better? Maybe, maybe not.


In the particular case of malloc, I fully accept that it is /usually/
important to check the result - the costs are low, and the consequences
of missing a null pointer return are usually substantial. This
particular case was (apparently) from Chrome - a program that will grab
so much memory that it is entirely realistic for malloc to fail. My
point is that this is not always the case, and knee-jerk checks on
malloc returns are not an excuse for not thinking things through.


>
>> ... Rules about always checking for error returns are like
>> pretty much every other "absolute" rule - it's an excuse for not
>> thinking.
>
> Rules intended to avoid the execution of code with undefined behavior
> (unless portability of the code is intentionally restricted to contexts
> where something other than the C++ standard provides its own definition
> for the behavior) are entirely appropriate - "not thinking" about the
> consequences is the only excuse for ignoring such rules.

There is a critical difference between avoiding the execution of
undefined behaviour (which is something all code should strive for), and
following rules "intended to avoid the execution of code with undefined
behaviour".

> Is there anything that your computer could be programmed do, that you
> wouldn't want it to do? Think about the worst such thing - that's one of
> the permitted consequences of executing code with undefined behavior.
> How far would you be willing to go to prevent that worst case from
> happening? That's how far you should go to avoid executing code with
> undefined behavior.

I agree with that.

But I do /not/ agree that using malloc without checking the return for 0
is necessarily a risk of executing code with undefined behaviour.


David Brown

unread,
Feb 2, 2018, 10:27:12 AM2/2/18
to
If your code is used in such situations, then yes, that would be valid
reasons for /not/ checking the return of malloc.

(I think it goes without saying that such assumptions need to be
documented, and if possible statically checked.)


James R. Kuyper

unread,
Feb 2, 2018, 11:06:36 AM2/2/18
to
If you can afford to restrict the portability of your programs to
systems that provide particular definitions for behavior that is
undefined by the C standard, it's perfectly fine to rely upon those
definitions - but you should remember that the code is non-portable.

Just because there's no possibility of your 100KB allocation failing on
your Linux system, doesn't mean that the same is true of other systems.
On some systems, the fact that something else running at the same time
has already used up all but 50KB of the available memory will not only
allow, but guarantee, that your 100KB allocation will fail.

The same implementation-dependence applies to writing to memory at
location 0. If your code only needs to be portable to systems that
guarantee that writing through a null pointer will do nothing worse than
abort your program, then you are indeed entitled to rely upon that
guarantee.

Paavo Helde

unread,
Feb 2, 2018, 11:20:09 AM2/2/18
to
On 2.02.2018 17:23, David Brown wrote:
> No, malloc will not necessarily have a possibility of failing -
> certainly not if you think in terms of /realistic/ possibilities. If
> you run your program on your 8 GB Linux desktop, and it asks for 100 KB
> from malloc, it will /not/ fail. There is /no/ realistic chance of it
> failing. You would be better worrying about a lightening strike
> blasting the PC to bits than worrying about the malloc failing.

On Linux this might even hold as the offending memory eaters are killed
by the kernel. On Windows however I have seen it happen several times -
one program (typically mine, but that's besides the point) consumes up
all the memory and then random other processes start crashing as they
have been written with exactly this assumption that a 100 byte malloc
will never fail.



Scott Lurndal

unread,
Feb 2, 2018, 12:11:01 PM2/2/18
to
David Brown <david...@hesbynett.no> writes:
>On 02/02/18 15:09, James R. Kuyper wrote:

>> Yes, it is necessarily a problem. Any particular return from malloc()
>> might be the one that will fail, no matter how small it is, and the
>> potential consequences have no upper limit on how severe they can be.
>
>That is three statements, all of which are incorrect.
>
>No, malloc will not necessarily have a possibility of failing -
>certainly not if you think in terms of /realistic/ possibilities.

> If
>you run your program on your 8 GB Linux desktop, and it asks for 100 KB
>from malloc, it will /not/ fail.

That's entirely a function of the workload on the desktop and
the memory requirements of the set of running applications vice
the available DRAM + swap resources.

I've seen it happen, and recently.

red floyd

unread,
Feb 2, 2018, 12:56:15 PM2/2/18
to
On 2/1/2018 3:21 PM, woodb...@gmail.com wrote:
> Please don't $wear here.

Go fuck yourself and your holier-than-thou attitude.


Rick C. Hodgin

unread,
Feb 2, 2018, 1:12:24 PM2/2/18
to
On 2/2/2018 12:55 PM, red floyd wrote:
> On 2/1/2018 3:21 PM, woodb...@gmail.com wrote:
>> Please don't $wear here.
>
> Go .. yourself and your holier-than-thou attitude.

It's not "holier-than-thou," red floyd. It's decency. It's common
respect for your fellow man. It's meeting people where they are and
placing them above yourself, recognizing that their natural demeanor
and ways may be different than your own.

You are very forceful in your attacks on people. You're not supposed
to be that way. It's not a holier-than-thou attitude, but it is the
one taught to us by God. He teaches us to love one another as we do
ourselves.

Do you not love yourself to treat others that way?

Geoff

unread,
Feb 2, 2018, 1:21:23 PM2/2/18
to
Chris M. Thomasson gets points for actually bothering to read the
blog.

If anyone had even casually read the blog and followed the references
they would know this particular example exists in
/yasm/tools/re2c/substr.h, lines 48-55 of the yasm project code, the
source of which is available on GitHub and http://yasm.tortall.net/
YASM is a re-write of NASM. This function definition does not exist in
NASM.

Yasm is written in C. The Chromium project apparently depends on it
which is why it's cited as a warning in the PVS-Studio output. That
the Chromium project would include and depend on libraries with bugs
like this is somewhat concerning but the yasm code never uses or
refers to this particular function and there is no evidence to support
any belief that Chromium would call it directly.

Karpov's error is to state:

"Yes, in Chromium itself these functions are not used almost anywhere.
In Chromium only containers or operator new are applied. However, once
there are errors in the libraries, then, we can say that they are in
Chromium. Of course, some parts of the libraries may not be used when
running Chromium, but it is difficult and unnecessary to define it. It
is necessary to correct all errors anyway."

This is the job of the yasm maintainers, not of Chromium.

Does this invalidate the argument that use of a tool like PVS-Studio
is invalid?

Chris M. Thomasson

unread,
Feb 2, 2018, 2:54:33 PM2/2/18
to
Argh! I should say even if the returned pointer passes the NULL check,
well, the size used for the malloc better not be zero! Afaict, you
should not store anything in a location pointed to by a non-null pointer
returned from malloc(0).

Chris M. Thomasson

unread,
Feb 2, 2018, 3:00:44 PM2/2/18
to
C++ is a very nice tool to have in the box! :^)

Chris M. Thomasson

unread,
Feb 2, 2018, 3:27:48 PM2/2/18
to
On 2/2/2018 10:12 AM, Rick C. Hodgin wrote:
> On 2/2/2018 12:55 PM, red floyd wrote:
>> On 2/1/2018 3:21 PM, woodb...@gmail.com wrote:
>>> Please don't $wear here.
>>
>> Go .. yourself and your holier-than-thou attitude.
>
> It's not "holier-than-thou," red floyd.  It's decency.  It's common
> respect for your fellow man.

Perhaps when I swear on here, it should look like Q-Bert is talking.

Ah ?$%?!

https://youtu.be/0yrhee8W7II?t=8

;^)

David Brown

unread,
Feb 2, 2018, 4:23:59 PM2/2/18
to
Of course it is. My point is not that I think malloc never fails, or it
is a good idea to write code that assumes it can't fail and then let
that same code be used again in a different system. I just want to
point out that sometimes /not/ checking the result of malloc is
perfectly reasonable coding - indeed, it can be a /better/ choice than
testing it. Such cases are rare - of that there is no doubt. But they
do exist.

In my own programming, I rarely use malloc - and I /never/ use it in a
case where it might fail.

David Brown

unread,
Feb 2, 2018, 4:25:08 PM2/2/18
to
On 02/02/18 20:44, Stefan Ram wrote:
> r...@zedat.fu-berlin.de (Stefan Ram) writes:
>> Here we need to check all attempts to obtain a resource
>> and supply reasonable code for both possibilities
>> (success and failure).
>
> And then, there is the elephant in the room:
>
> Whenever one calls a function in either C or C++,
> one never knows whether there is enough memory for
> another function incarnation. And these languages do
> not even provide any means to learn this at runtime.
>

In my embedded systems, I /always/ know there is enough memory for the
function call.

Chris M. Thomasson

unread,
Feb 2, 2018, 4:29:59 PM2/2/18
to
Reminds me of using guaranteed local memory on the "stack", here is some
older region allocator code I wrote, be warned, it is in C:

https://groups.google.com/d/msg/comp.lang.c/7oaJFWKVCTw/sSWYU9BUS_QJ

;^)

red floyd

unread,
Feb 2, 2018, 4:52:56 PM2/2/18
to
On 2/2/2018 12:27 PM, Chris M. Thomasson wrote:
> On 2/2/2018 10:12 AM, Rick C. Hodgin wrote:
>> On 2/2/2018 12:55 PM, red floyd wrote:
>>> On 2/1/2018 3:21 PM, woodb...@gmail.com wrote:
>>>> Please don't $wear here.
>>>
>>> Go .. yourself and your holier-than-thou attitude.
>>
>> It's not "holier-than-thou," red floyd.  It's decency.  It's common
>> respect for your fellow man.
>
> Perhaps when I swear on here, it should look like Q-Bert is talking.
>
> Ah ?$%?!
>
> https://youtu.be/0yrhee8W7II?t=8
>
> ;^)
>
>
>> It's meeting people where they are and
>> placing them above yourself, recognizing that their natural demeanor
>> and ways may be different than your own.

Exactly. Why should we conform to *YOUR* "natural demeanor"? Why won't
you recognize that MY "natural demeanor and ways" are different than
your own?

Richard Damon

unread,
Feb 3, 2018, 12:19:46 PM2/3/18
to
On 2/2/18 10:23 AM, David Brown wrote:
> No, malloc will not necessarily have a possibility of failing -
> certainly not if you think in terms of/realistic/ possibilities. If
> you run your program on your 8 GB Linux desktop, and it asks for 100 KB
> from malloc, it will/not/ fail. There is/no/ realistic chance of it
> failing. You would be better worrying about a lightening strike
> blasting the PC to bits than worrying about the malloc failing.

Even on your 8 GB Linux desktop, your program might be running as a
process which has been assigned to limited ram, and thus malloc may fail.

If you really intend that your function can not be run in an environment
like this, there really should be a GREAT BIG COMMENT that this function
assumes unlimited available heap, and that comment needs to be copied to
every function that calls such a function, all the way to the main, and
into the documentation for the program.

Manfred

unread,
Feb 3, 2018, 4:11:38 PM2/3/18
to
And in more general terms malloc() is a system function that is
specified to be subject to failure, with no further guarantees.

Any causes for malloc() to fail are simply out of the control of the
programmer, hence the requirement for serious code to be prepared for
its failure.
The only option for the programmer to make use of its reasonable
unlikelihood, and ease handling of this situation, is to adopt a simple
solution, so that "being prepared" often just means "abort with no
further action".

Chris M. Thomasson

unread,
Feb 3, 2018, 5:24:43 PM2/3/18
to
Fwiw, I remember trying to send a signal for a malloc failure in a
server that told the system that %^^$ hit the fan, and it should start
freeing cache and even start purging connections that have the lowest
response times along with trying to give the purged users an overloaded
error condition. This actually worked. I could see the malloc failure
signals start to artificially drop load in the server during testing
phases. Fwiw, malloc was only used to handle conditions in the server
when the current load needed more memory that was pre-allocated and
guaranteed in system startup. So, it just allocated more blocks using
malloc, that can fail.

BTW, the signals sent to the systems event loop after malloc failure did
not require a subsequent malloc to create. I remember checking errno for
ENOMEM after malloc failure wrt returning NULL as well.

Christian Gollwitzer

unread,
Feb 3, 2018, 6:33:53 PM2/3/18
to
Am 03.02.18 um 22:11 schrieb Manfred:
> And in more general terms malloc() is a system function that is
> specified to be subject to failure, with no further guarantees.
>
> Any causes for malloc() to fail are simply out of the control of the
> programmer, hence the requirement for serious code to be prepared for
> its failure.

IOW, if you use malloc() you are a masochist, because you have to guard
every single little allocation with if() { printf("OOM"); exit(-1);} or
similar.

> The only option for the programmer to make use of its reasonable
> unlikelihood, and ease handling of this situation, is to adopt a simple
> solution, so that "being prepared" often just means "abort with no
> further action".

The IMHO best solution is to stay away from C and use C++ new, which
handles this properly by throwing an exception. You only need to deal
with the out-of-memory case if you can really handle it, i.e. if you
catch it or ask for the nothrowing version; in most cases you simply
want the program to abort cleanly, which the exception handles for you.

Christian

asetof...@gmail.com

unread,
Feb 4, 2018, 2:15:51 AM2/4/18
to
malloc() can fail: In that case return 0.

(if not is this amd someone add exceptions rises to that function other than write out of memory: It is all insecure and the person make that change better change its work)

David Brown

unread,
Feb 4, 2018, 11:44:12 AM2/4/18
to
Throwing an exception is just another way of handling errors - with its
disadvantages and advantages compared to checking error returns and
handling it at the time. If you call a function that can fail in some
way - be it malloc, new, or anything else - you have a problem to
handle. You have to have some way of turning that failure into a
success, or punting the problem upstream to the calling program, the
user, etc.

The /best/ solution is not to have a system where these sorts of errors
can occur. Clearly, that is not practical in many cases - for most PC
software, you can't place limits on what other uses are made of the
shared resources of the system. But where you /can/ do it, such as by
pre-allocating all the resources you are going to need and not starting
the program proper until you have them, then that is better than any
sort of exception or error handling mechanism.


Juha Nieminen

unread,
Feb 5, 2018, 1:51:08 AM2/5/18
to
Alf P. Steinbach <alf.p.stein...@gmail.com> wrote:
>> These examples are pretty fuc%ed up man!
>> __________________
>> static SubStr *
>> SubStr_new_u(unsigned char *s, unsigned int l)
>> {
>>     SubStr *r = malloc(sizeof(SubStr));
>>     r->str = (char*)s;
>>     r->len = l;
>>     return r;
>> }
>> __________________
>>
>> Sorry buddy, but you are fired!
>
> That's C code

Is it C code from like the times of K&R C (when you couldn't even
return a struct from a function by value)?

Ian Collins

unread,
Feb 5, 2018, 1:54:59 AM2/5/18
to
Why would you want to return something that had been dynamically
allocated by value?

--
Ian

Öö Tiib

unread,
Feb 5, 2018, 2:45:28 AM2/5/18
to
It seems he meant that the very allocation is likely pointless.
Allocating that SubStr seems like allocating new std::string and
then returning std::string* instead of std::string by value.
Theoretically there may be reason but it is hard to find sane code
base dong that.

James R. Kuyper

unread,
Feb 5, 2018, 8:25:26 AM2/5/18
to
You wouldn't. You'd write something like this instead:

static SubStr
SubStr_new_u(unsigned char *s, unsigned int l)
{
SubStr r;
r.str = (char*)s;
r.len = l;
return r;
}

Jorgen Grahn

unread,
Feb 6, 2018, 8:14:01 AM2/6/18
to
On Fri, 2018-02-02, Paavo Helde wrote:
> On 2.02.2018 17:23, David Brown wrote:
>> No, malloc will not necessarily have a possibility of failing -
>> certainly not if you think in terms of /realistic/ possibilities. If
>> you run your program on your 8 GB Linux desktop, and it asks for 100 KB
>> from malloc, it will /not/ fail. There is /no/ realistic chance of it
>> failing. You would be better worrying about a lightening strike
>> blasting the PC to bits than worrying about the malloc failing.
>
> On Linux this might even hold as the offending memory eaters are killed
> by the kernel.

Maybe: IIRC the "OOM killer" in the kernel has some fancy heuristics
for which process to kill. It's not guaranteed to be the one you see
as the offender.

Then, I think you can also disable the OOM killer (although few seem
to do that), and get the behavior below:

> On Windows however I have seen it happen several times -
> one program (typically mine, but that's besides the point) consumes up
> all the memory and then random other processes start crashing as they
> have been written with exactly this assumption that a 100 byte malloc
> will never fail.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Jorgen Grahn

unread,
Feb 6, 2018, 8:48:41 AM2/6/18
to
On Fri, 2018-02-02, Stefan Ram wrote:
> "James R. Kuyper" <james...@verizon.net> writes:
>>Using malloc in a C++ program is a poor choice. The missing cast makes
>>the program ill-formed in C++. However, the failure to check whether r
>>is null before dereferencing could result in undefined behavior - that's
>>the firing offense.
>
> I make a difference between (at least) /two/ requirement types:
>
> 1. "sunshine code"
>
> This is supposed to work as long as the weather is fine.
...
>
> 2. "library-grade code"
>
> This is code that will or might end up in a library
> where it is called under unknown circumstances. It needs
> to fulfill the contract from its documentation.

It's important IMHO to realize that there are different requirement
types, but you picked the extremes.

There's plenty of non-library code which musn't fail uncontrolledly.
The "called under unknown circumstances" part isn't there, but you
still need to deal with the program's environment.

BTW, I'd like to mention one more type, close to the sunshine code:
where the program may fail to do its job, but must tell the user and
give her the tools to troubleshoot.

Jorgen Grahn

unread,
Feb 6, 2018, 12:59:46 PM2/6/18
to
On Mon, 2018-02-05, Juha Nieminen wrote:
> Alf P. Steinbach <alf.p.stein...@gmail.com> wrote:
...
>> That's C code
>
> Is it C code from like the times of K&R C (when you couldn't even
> return a struct from a function by value)?

Actually, I believe late K&R C supported it too, but the users assumed
it was hideously expensive and chose to forget.

Thiago Adams

unread,
Feb 7, 2018, 10:40:06 AM2/7/18
to
On Saturday, February 3, 2018 at 9:33:53 PM UTC-2, Christian Gollwitzer wrote:
> Am 03.02.18 um 22:11 schrieb Manfred:
> > And in more general terms malloc() is a system function that is
> > specified to be subject to failure, with no further guarantees.
> >
> > Any causes for malloc() to fail are simply out of the control of the
> > programmer, hence the requirement for serious code to be prepared for
> > its failure.
>
> IOW, if you use malloc() you are a masochist, because you have to guard
> every single little allocation with if() { printf("OOM"); exit(-1);} or
> similar.

..or you can create and reuse a function to do that.

Rick C. Hodgin

unread,
Feb 7, 2018, 10:48:35 AM2/7/18
to
My CAlive language defines registered callback functions which allow
interception of failed malloc() calls without requiring the return
value be validated at every use. This allows cached resources,
temporary resources, or other resources allocated to be able to be
freed with a retry attempt possible. If it ultimately fails, then
it goes into a special run mode which allows for polite error
recovery.

This ability exists in CAlive because the ability to unwind exists
natively in the new "LiveCode ABI" design, and through the concept
I've added called an inquiry, which is a type of program state which
suspends to the debugger when possible, allowing the code to be
fixed with edit-and-continue (LiveCode) and re-applied to the running
ABI, or to a wrapper function at runtime which can politely capture
information and exit the program.

I also allow registered callbacks for malloc() when memory is reaching
thresholds, to allow a process to be stopped before the actual malloc()
call fails.

CAlive is a re-thinking of how C/C++ code should be. It's still in
development, but it has goals ultimately to replace nearly all of C
programs, and to greatly simplify a great many C++ applications.

Juha Nieminen

unread,
Feb 8, 2018, 12:23:30 AM2/8/18
to
Jorgen Grahn <grahn...@snipabacken.se> wrote:
> On Mon, 2018-02-05, Juha Nieminen wrote:
>> Alf P. Steinbach <alf.p.stein...@gmail.com> wrote:
> ...
>>> That's C code
>>
>> Is it C code from like the times of K&R C (when you couldn't even
>> return a struct from a function by value)?
>
> Actually, I believe late K&R C supported it too, but the users assumed
> it was hideously expensive and chose to forget.

The irony is that it's malloc() that's actually hideously expensive...

Paavo Helde

unread,
Feb 8, 2018, 2:02:57 AM2/8/18
to
It wasn't in the past. Malloc() used to by an O(1) adjustment of a
couple of pointers in the allocator data structures. Nowadays it
involves (at least some) synchronization of N CPU cores and losing of
memory locality which imposes an unpredictable amount of cache misses.

Neither multithreading nor CPU caches were a thing to worry about back
then.


Jorgen Grahn

unread,
Feb 8, 2018, 6:50:22 AM2/8/18
to
To be fair, the normal alternative was to pass a pointer, and let the
function fill in the struct piece by piece. But in this example, yes.

David Brown

unread,
Feb 8, 2018, 6:59:59 AM2/8/18
to
That is effectively what happens when a function returns a large struct
in C today - it's just that this pointer is hidden behind the scenes.
The space for the struct is allocated on the stack of the caller
function. Smaller structs are usually returned in registers. The
details of when a struct is returned via a hidden pointer or in a
register will depend on the target ABI, and possibly the implementation
and maybe even compiler flags.


Josef Moellers

unread,
Feb 8, 2018, 11:04:25 AM2/8/18
to
On 01.02.2018 21:55, Alf P. Steinbach wrote:
> On 2/1/2018 8:59 PM, Chris M. Thomasson wrote:
>> On 2/1/2018 9:09 AM, Andrey Karpov wrote:
>>> We'd like to present the series of articles dealing with the
>>> recommendations on writing code of high quality using the examples of
>>> errors found in the Chromium project. This is the 6th part, which
>>> focuses on the malloc function. Or rather, why you should always
>>> check the pointer returned by this function. Most likely, you don't
>>> have a clue what's the catch with malloc, so we recommend looking
>>> through this article. https://www.viva64.com/en/b/0558/
>>>
>>
>> These examples are pretty fuc%ed up man!
>> __________________
>> static SubStr *
>> SubStr_new_u(unsigned char *s, unsigned int l)
>> {
>>      SubStr *r = malloc(sizeof(SubStr));
>>      r->str = (char*)s;
>>      r->len = l;
>>      return r;
>> }
>> __________________
>>
>> Sorry buddy, but you are fired!
>
> That's C code (hence the lack of cast of `malloc` result, which is
> technically correct and preferable in C, opposite of C++: it wouldn't
> compile as C++, hence it must be C).

You shouldn't ever cast malloc()'s result! If it is a pointer, den C is
satisfied. If, however, it was not declared, then C assumes it returns
an "int".
This is usually not a problem, especially with people who only know the
x86 architectures, but eg the M68k (and, probably others too) has two
sets of registers: address and data registers and int results are
returned in d0 while pointer results in a0!

Josef

PS I know, it does not belong here.

James R. Kuyper

unread,
Feb 8, 2018, 11:25:04 AM2/8/18
to
Implicit int was dropped in C99. This is comp.lang.c++, and implicit int
was never supported in C++. Casting the value returned by malloc() was a
bad idea in C90, a poor idea in later versions of C, and absolutely
required in C++.
Using malloc() in C++ is permitted, but it is also a bad idea.

Vir Campestris

unread,
Feb 8, 2018, 4:04:04 PM2/8/18
to
On 08/02/2018 16:04, Josef Moellers wrote:
> This is usually not a problem, especially with people who only know the
> x86 architectures, but eg the M68k (and, probably others too) has two
> sets of registers: address and data registers and int results are
> returned in d0 while pointer results in a0!

x86 architectures.

Like the 8086, where a pointer could be 16 or 20 bits, depending on the
memory model? Or the 80286, 16 or (IIRC)24?

Andy
--
I definitely remember the '286 memory manager giving me headaches...

Rick C. Hodgin

unread,
Feb 8, 2018, 5:11:20 PM2/8/18
to
On 2/8/2018 4:03 PM, Vir Campestris wrote:
> On 08/02/2018 16:04, Josef Moellers wrote:
>> This is usually not a problem, especially with people who only know the
>> x86 architectures, but eg the M68k (and, probably others too) has two
>> sets of registers: address and data registers and int results are
>> returned in d0 while pointer results in a0!
>
> x86 architectures.
>
> Like the 8086, where a pointer could be 16 or 20 bits, depending on the
> memory model? Or the 80286, 16 or (IIRC)24?


The 80286's 24-bits referred to the physical memory the machine could
address, but each single process was limited to 8086 code and could only
address 20-bits max itself.

You could have multiple 20-bit processes running simultaneously.

The 80386 was the first CPU to allow more than 20-bits of memory per
process. And the Pentium Pro introduced an initially undocumented
mode (now called "Appendex H") that allowed the 32-bit CPU to actually
address 36-bits of physical memory, making it a 64 GB CPU.

The modern 32-bit Linux and Windows Server kernels support this mode.
Linus Torvalds was quoted as saying it was one reason he liked the
AMD64 design, was because in 32-bit mode they had to "jump through
hoops" to access more than 4 GB of RAM.

Robert Wessel

unread,
Feb 8, 2018, 5:17:06 PM2/8/18
to
On Thu, 8 Feb 2018 21:03:55 +0000, Vir Campestris
<vir.cam...@invalid.invalid> wrote:

>On 08/02/2018 16:04, Josef Moellers wrote:
>> This is usually not a problem, especially with people who only know the
>> x86 architectures, but eg the M68k (and, probably others too) has two
>> sets of registers: address and data registers and int results are
>> returned in d0 while pointer results in a0!
>
>x86 architectures.
>
>Like the 8086, where a pointer could be 16 or 20 bits, depending on the
>memory model? Or the 80286, 16 or (IIRC)24?


Pointers on 16-bit x86 (both real and protected) were either 16 or 32
bits. In real mode those collapsed into only 20 bit worth of linear
address space, in protected more it's about 30. But the address
handled by the program is still 32 bits, a 16-bit segment (or
selector) value, and a 16-bit offset.

Now you *could* compress those addresses, especially in real mode to
20 bits, but that would have only been good for storing addresses, the
processor could not use them for accessing memory.

Jorgen Grahn

unread,
Feb 9, 2018, 1:38:38 AM2/9/18
to
On Thu, 2018-02-08, Josef Moellers wrote:
...
> You shouldn't ever cast malloc()'s result! If it is a pointer, den C is
> satisfied. If, however, it was not declared, then C assumes it returns
> an "int".
> This is usually not a problem, especially with people who only know the
> x86 architectures, but eg the M68k (and, probably others too) has two
> sets of registers: address and data registers and int results are
> returned in d0 while pointer results in a0!

I'm pretty sure the Commodore-Amiga (MC68000) ABI said everything
fitting in a register was returned in d0. Supporting pre-prototype C
might have been the reason. There may be/have been other examples,
but a MC68k-based computer doesn't /have to/ do it like you describe.

Of course, use C or C++ properly and you don't have to care.

David Brown

unread,
Feb 9, 2018, 3:24:58 AM2/9/18
to
This is a totally specious argument. Your compiler should warn you
about using a function without a declaration. If it does not, you
either have a useless compiler, or you don't know how to use your tools.
Either way, you have a vastly bigger problem than a mere incorrect
function call.

Similarly, arguments about "you might get inconsistent types if the code
is changed" are only valid if you have a completely broken development
process.

The only reason /not/ to cast the result of malloc() in C is that
casting it will cause some old C programmers to rant about it being a
C++'ism and how K&R never cast their mallocs, so neither should you.

Ben Bacarisse

unread,
Feb 9, 2018, 5:55:10 AM2/9/18
to
David Brown <david...@hesbynett.no> writes:
<snip>
> The only reason /not/ to cast the result of malloc() in C is that
> casting it will cause some old C programmers to rant about it being a
> C++'ism and how K&R never cast their mallocs, so neither should you.

K&R always do so the old programmers would be lying!

The main reason not to cast the result of malloc is just that it's
extraneous code. Some people like extraneous code, but if you don't,
that's probably reason enough.

--
Ben.

David Brown

unread,
Feb 9, 2018, 6:43:21 AM2/9/18
to
On 09/02/18 11:54, Ben Bacarisse wrote:
> David Brown <david...@hesbynett.no> writes:
> <snip>
>> The only reason /not/ to cast the result of malloc() in C is that
>> casting it will cause some old C programmers to rant about it being a
>> C++'ism and how K&R never cast their mallocs, so neither should you.
>
> K&R always do so the old programmers would be lying!
>

Ah, you know your K&R better than I do :-)

> The main reason not to cast the result of malloc is just that it's
> extraneous code. Some people like extraneous code, but if you don't,
> that's probably reason enough.
>

People don't always agree on what is "extraneous". Casting the result
of malloc is certainly extraneous from the viewpoint of C. But if you
think it improves readability, reduces the risk of errors, or improves
portability to C++ (most programmers agree that malloc should not be
found often in C++ code, but it turned up in this example), then it is
no longer extraneous. I am no fan of adding code that serves no
purpose, but I see purpose in code beyond the mere mechanics of the
language needed for the compiler.

My main point is that if you think casting the result of malloc (or any
other void* pointer) increases the risk of errors in your C code, then
you have a problem with the way you write, compiler or otherwise
automatically check your code.

Beyond that, it is a matter of choice or coding style.

Josef Moellers

unread,
Feb 9, 2018, 8:07:03 AM2/9/18
to
On 09.02.2018 11:54, Ben Bacarisse wrote:
> David Brown <david...@hesbynett.no> writes:
> <snip>
>> The only reason /not/ to cast the result of malloc() in C is that
>> casting it will cause some old C programmers to rant about it being a
>> C++'ism and how K&R never cast their mallocs, so neither should you.
>
> K&R always do so the old programmers would be lying!

I stand corrected, they indeed do: "should (1st ed)/must (2nd ed) be
cast into the apropriate type".

Josef

James Kuyper

unread,
Feb 9, 2018, 11:06:03 AM2/9/18
to
On 02/09/2018 03:24 AM, David Brown wrote:
> On 08/02/18 17:04, Josef Moellers wrote:
...
>> You shouldn't ever cast malloc()'s result! If it is a pointer, den C is
>> satisfied. If, however, it was not declared, then C assumes it returns
>> an "int".
>> This is usually not a problem, especially with people who only know the
>> x86 architectures, but eg the M68k (and, probably others too) has two
>> sets of registers: address and data registers and int results are
>> returned in d0 while pointer results in a0!
>
> This is a totally specious argument. Your compiler should warn you
> about using a function without a declaration. If it does not, you
> either have a useless compiler, or you don't know how to use your tools.

In C90, the behavior when calling a function without a declaration in
scope could be well-defined: the standard argument promotions were
performed on all arguments and the return type was implicitly assumed to
be int. If the number of arguments was the same as the number of
parameters in the function definition, and if the the promoted types of
the arguments were compatible with the corresponding parameters in the
function definition, and if the return type was actually int, there was
no problem with writing such code. Lots of code took advantage of this
fact, and a compiler that warned about such code by default would have
been very annoying to a lot of people (personally, I would have been
overjoyed to use such a compiler, or even one where it was merely an
option that could be turned on).

Implicit int was a bad idea, which is why it was removed in C99. But an
awful lot of code has been written (and some is still being written)
which either targeted C90 or was at least required to be compatible with
it. In the context of C90, it's a valid argument and excellent advice.

> Either way, you have a vastly bigger problem than a mere incorrect
> function call.

Yes, being forced to use C90 is a bigger problem - but not necessarily a
solvable one. In production code for the MODIS instrument, I'm bound by
a standards and guidelines document written in the 1993 that mandates
the use of either Ada, Fortran 77, Fortran 90, or C94 (C90 + two
technical corrigenda). That document has, as far as I know, never been
updated, replaced, or canceled - and I haven't been able to convince
anybody that it needs to be (to be fair, it's been a long time since I
last bothered trying).

> The only reason /not/ to cast the result of malloc() in C is that
> casting it will cause some old C programmers to rant about it being a
> C++'ism and how K&R never cast their mallocs, so neither should you.

As has already been pointed out by others, K&R did make the mistake of
casting their malloc()s. In all fairness to them, that was long before
anybody (the designers of C included) had had enough experience writing
C to realize that it was a bad idea to cast a malloc() call.

David Brown

unread,
Feb 9, 2018, 12:11:11 PM2/9/18
to
On 09/02/18 17:05, James Kuyper wrote:
> On 02/09/2018 03:24 AM, David Brown wrote:
>> On 08/02/18 17:04, Josef Moellers wrote:
> ...
>>> You shouldn't ever cast malloc()'s result! If it is a pointer, den C is
>>> satisfied. If, however, it was not declared, then C assumes it returns
>>> an "int".
>>> This is usually not a problem, especially with people who only know the
>>> x86 architectures, but eg the M68k (and, probably others too) has two
>>> sets of registers: address and data registers and int results are
>>> returned in d0 while pointer results in a0!
>>
>> This is a totally specious argument. Your compiler should warn you
>> about using a function without a declaration. If it does not, you
>> either have a useless compiler, or you don't know how to use your tools.
>
> In C90, the behavior when calling a function without a declaration in
> scope could be well-defined: the standard argument promotions were
> performed on all arguments and the return type was implicitly assumed to
> be int.

Yes, it could be well-defined. But implicit function declarations were
removed from the language in C99, and form of the implicit declarations
of C90 "int foo()" were considered obsolete in C90. Relying on implicit
function declarations has never been considered good practice.

So if you are writing code in a way that uses a poorly considered
language construct that was removed two decades ago, obsolete three
decades ago, and never considered a good idea, then you are not exactly
writing high quality code. And if you are using tools which don't tell
you about such a basic mistake, you are either using hopeless tools, or
you don't know how to use the tools you have.

I have had to deal with code written in this way. Actually, the
compiler /did/ warn this programmer about the implicit function
declarations - he just thought "it's only a warning, not a real
problem". The result was utterly crap software that was often
incomprehensible because a function could be used in one file with a
different set of parameters from the way it was defined.

(I think it is a flaw in gcc's warnings that "-Wimplicit-int" and
"-Wimplicit-function-declaration" are not enabled by default in C90
mode. In C90 mode it has to accept such code, but it could still warn
about it.)

> If the number of arguments was the same as the number of
> parameters in the function definition, and if the the promoted types of
> the arguments were compatible with the corresponding parameters in the
> function definition, and if the return type was actually int, there was
> no problem with writing such code. Lots of code took advantage of this
> fact, and a compiler that warned about such code by default would have
> been very annoying to a lot of people (personally, I would have been
> overjoyed to use such a compiler, or even one where it was merely an
> option that could be turned on).

Old tools were more limited than modern ones. Some people wrote bad
code. That is no excuse for using failing to use modern tools to help
write good software.

I can understand people used to write code in other ways. I can
understand people using a wider set of C constructs than I do. But
there is some stuff that simply has no good justification for writing.

>
> Implicit int was a bad idea, which is why it was removed in C99. But an
> awful lot of code has been written (and some is still being written)
> which either targeted C90 or was at least required to be compatible with
> it. In the context of C90, it's a valid argument and excellent advice.

No, it is not a valid argument and not excellent advice. Sure, some
people write C90 code - in many cases it is because the programmer
doesn't know any better, or has heard that K&R is "the Bible" for C
programming. But in some cases there are sensible reasons for it.

However, that does not matter. It does not /matter/ that you are
allowed to use implicit function declarations in C90, any more than it
matters that gcc will accept such code even in C99 or C11 mode, with
merely a warning. People can write C90 code if they need to or want to
- but they should still be striving to write /good/ C90 code.

Excellent advice here is not "make sure you never cast the result of
malloc so that you can find find this one particular case of missing
header files". Excellent advice is "make sure you have the right header
files for the functions you use", and "make sure you enable warnings in
your toolchain to catch small mistakes early".

>
>> Either way, you have a vastly bigger problem than a mere incorrect
>> function call.
>
> Yes, being forced to use C90 is a bigger problem - but not necessarily a
> solvable one. In production code for the MODIS instrument, I'm bound by
> a standards and guidelines document written in the 1993 that mandates
> the use of either Ada, Fortran 77, Fortran 90, or C94 (C90 + two
> technical corrigenda). That document has, as far as I know, never been
> updated, replaced, or canceled - and I haven't been able to convince
> anybody that it needs to be (to be fair, it's been a long time since I
> last bothered trying).

Being required to write C90 is not the problem. It is writing bad code
without good use of tools that is the problem. Sure, C99 lets you write
better code in an easier way than C90, and I dislike having to go back
to C90 on the few occasions when it is necessary in my job. But you are
certainly not required to use implicit function declarations just
because you are writing C90 - it is, after all, implicitly obsolete
already in that standard.

>
>> The only reason /not/ to cast the result of malloc() in C is that
>> casting it will cause some old C programmers to rant about it being a
>> C++'ism and how K&R never cast their mallocs, so neither should you.
>
> As has already been pointed out by others, K&R did make the mistake of
> casting their malloc()s. In all fairness to them, that was long before
> anybody (the designers of C included) had had enough experience writing
> C to realize that it was a bad idea to cast a malloc() call.
>

I don't see casting malloc()s as a mistake. An unnecessary cast, yes,
technically. It is something you can choose to do or not - if you feel
it makes code clearer, or less susceptible to errors, then use it. (For
example, if there is a distance between the declaration of the pointer
and the call to malloc, then the cast will likely lead to a warning if
you have got the type wrong.) If you feel it disrupts the flow of the
code and its readability, then don't use it. But if you feel the cast
makes it /less/ likely that an error will be caught by your tools, then
you should fix your use of tools.


James Kuyper

unread,
Feb 9, 2018, 12:48:36 PM2/9/18
to
On 02/09/2018 12:10 PM, David Brown wrote:
> On 09/02/18 17:05, James Kuyper wrote:
...
>> In C90, the behavior when calling a function without a declaration in
>> scope could be well-defined: the standard argument promotions were
>> performed on all arguments and the return type was implicitly assumed to
>> be int.
>
> Yes, it could be well-defined. But implicit function declarations were
> removed from the language in C99, and form of the implicit declarations
> of C90 "int foo()" were considered obsolete in C90. Relying on implicit
> function declarations has never been considered good practice.
>
> So if you are writing code in a way that uses a poorly considered
> language construct that was removed two decades ago, obsolete three
> decades ago, and never considered a good idea, then you are not exactly
> writing high quality code. ...

It's not about deliberately using the construct, which was never a good
idea; my C instructor warned us against relying on that feature when I
first learned C in 1987, so it was already common knowledge before the
first standard was even approved. It's about protecting against a danger
that could only come up because that construct was permitted: the
possibility of forgetting to #include the appropriate header, and
getting no warning about the declaration being missing.

> ... And if you are using tools which don't tell
> you about such a basic mistake, ...

Then you were using perfectly ordinary commonplace implementation of
C90. Modern implementation that have a C90 mode often warn about this,
but it's still often optional, and was not as common before C99 came out
as it is now.

...
> Old tools were more limited than modern ones. Some people wrote bad
> code. That is no excuse for using failing to use modern tools to help
> write good software.

True. and if there were any good reason for inserting the cast, your
vitriol against those who object to it would be more understandable. But
I know of none, (and I've heard the faulty arguments of those who think
it makes their code safer a great many times).

...
> Being required to write C90 is not the problem. It is writing bad code
> without good use of tools that is the problem. Sure, C99 lets you write
> better code in an easier way than C90, and I dislike having to go back
> to C90 on the few occasions when it is necessary in my job. But you are
> certainly not required to use implicit function declarations just
> because you are writing C90 - it is, after all, implicitly obsolete
> already in that standard.

As I said, the issue had nothing to do with deliberately using the
construct - it was about protecting yourself from accidentally invoking
the construct, and not getting any warning about the fact.
It's also about not writing code that serves no useful purpose, and that
applies even to more modern C compilers.

...
>> As has already been pointed out by others, K&R did make the mistake of
>> casting their malloc()s. In all fairness to them, that was long before
>> anybody (the designers of C included) had had enough experience writing
>> C to realize that it was a bad idea to cast a malloc() call.
>>
>
> I don't see casting malloc()s as a mistake.

That's too bad - you really should understand the reasons why it was (in
C90). It's an important example of how not to design a computer language.

> ... An unnecessary cast, yes,
> technically. It is something you can choose to do or not - if you feel
> it makes code clearer, or less susceptible to errors, then use it. (For
> example, if there is a distance between the declaration of the pointer
> and the call to malloc, then the cast will likely lead to a warning if
> you have got the type wrong.)

But you can solve the "wrong type" problem equally well by removing the
cast entirely. The real issue with malloc() is not getting the right
type: that happens automatically when malloc() is used correctly (which
doesn't require a cast - implicit conversions do the right thing without
it). It's getting the right size - and that's something that can be
arranged perfectly by using malloc(count * sizeof *p), without needing
to know or care about what type *p is.

Siri Cruise

unread,
Feb 9, 2018, 2:46:01 PM2/9/18
to
In article <9da1ce32-7a51-4da6...@googlegroups.com>,
Andrey Karpov <karpo...@gmail.com> wrote:

> We'd like to present the series of articles dealing with the recommendations
> on writing code of high quality using the examples of errors found in the
> Chromium project. This is the 6th part, which focuses on the malloc function.
> Or rather, why you should always check the pointer returned by this function.
> Most likely, you don't have a clue what's the catch with malloc, so we
> recommend looking through this article. https://www.viva64.com/en/b/0558/

64-bit MacOSX runs out of disk space before it runs out of heap memory. Malloc
doesn't fail--the process or system crashes.

--
:-<> Siri Seal of Disavowal #000-001. Disavowed. Denied. Deleted. @
'I desire mercy, not sacrifice.' /|\
I'm saving up to buy the Donald a blue stone This post / \
from Metebelis 3. All praise the Great Don! insults Islam. Mohammed

Vir Campestris

unread,
Feb 9, 2018, 3:57:40 PM2/9/18
to
The point is however that there are x86 architectures where
sizeof(void*) != sizeof(int). I suspect Josef has only used 32-bit x86
in flat address mode, and has probably never heard of a selector.

Me, I hope I never see one again!

Andy

Richard

unread,
Feb 9, 2018, 4:03:37 PM2/9/18
to
[Please do not mail me a copy of your followup]

Josef Moellers <josef.m...@invalid.invalid> spake the secret code
<fe3ans...@mid.individual.net> thusly:

>You shouldn't ever cast malloc()'s result! [...]

In C++, std::malloc() returns void *, so it must be cast to some other
type for it to be useful.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Terminals Wiki <http://terminals-wiki.org>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

David Brown

unread,
Feb 11, 2018, 10:16:50 AM2/11/18
to
On 09/02/18 18:48, James Kuyper wrote:
> On 02/09/2018 12:10 PM, David Brown wrote:
>> On 09/02/18 17:05, James Kuyper wrote:
> ...
>>> In C90, the behavior when calling a function without a declaration in
>>> scope could be well-defined: the standard argument promotions were
>>> performed on all arguments and the return type was implicitly assumed to
>>> be int.
>>
>> Yes, it could be well-defined. But implicit function declarations were
>> removed from the language in C99, and form of the implicit declarations
>> of C90 "int foo()" were considered obsolete in C90. Relying on implicit
>> function declarations has never been considered good practice.
>>
>> So if you are writing code in a way that uses a poorly considered
>> language construct that was removed two decades ago, obsolete three
>> decades ago, and never considered a good idea, then you are not exactly
>> writing high quality code. ...
>
> It's not about deliberately using the construct, which was never a good
> idea; my C instructor warned us against relying on that feature when I
> first learned C in 1987, so it was already common knowledge before the
> first standard was even approved. It's about protecting against a danger
> that could only come up because that construct was permitted: the
> possibility of forgetting to #include the appropriate header, and
> getting no warning about the declaration being missing.
>

I realise that. And I can understand the reasoning for worrying about
accidentally using undeclared functions in 1987. I can't understand
worrying about it now, because tools will detect it. Even if you have
to use an older or more limited compiler (and sometimes you do), you can
use additional tools for checking. Many editors will do it.

>> ... And if you are using tools which don't tell
>> you about such a basic mistake, ...
>
> Then you were using perfectly ordinary commonplace implementation of
> C90. Modern implementation that have a C90 mode often warn about this,
> but it's still often optional, and was not as common before C99 came out
> as it is now.

C99 came out /long/ ago. Yes, there are still C90-only compilers in
use. Yes, there are compilers with limited warnings in use. There can
be a number of reasons why you need to compile code with tools that have
little or no ability to spot such errors. I have occasion to use these
sorts of tools myself when making changes to old projects.

But there is a big difference between what you need to use while
/building/ code, and what you can use while /developing/ code. Your
build environment might consist of an ancient C90 compiler, because that
is the one that is tested and qualified for the project. Or it might
consist of a new C90-only compiler, because that is the only one that
exists for the weird processor you are using. Or you might have no idea
about the build environment because there are thousands of people using
the code with all sorts of tools and flags.

However, as the developer, /you/ control the development environment.
/You/ choose to use a smart editor/IDE that checks as you go along, or
run pc-lint, or clang-analyzer, or whatever. /You/ choose to have
standards that you follow, common development patterns, standard headers
that you always use, code reviews with colleagues, etc.

You might, for many reasons, not have a build process that makes it easy
to catch mistakes like missing function declarations. But there are so
many ways to catch it as part of the development process that it is not
something to code around - not for development /today/ at least. Twenty
years ago, maybe even ten years ago, you could argue that omitting the
cast on malloc lead was a code safety point - not now.

>
> ...
>> Old tools were more limited than modern ones. Some people wrote bad
>> code. That is no excuse for using failing to use modern tools to help
>> write good software.
>
> True. and if there were any good reason for inserting the cast, your
> vitriol against those who object to it would be more understandable. But
> I know of none, (and I've heard the faulty arguments of those who think
> it makes their code safer a great many times).

I am only arguing here that the code safety reason for not casting
malloc is a moot point - you can have other good reasons. Personally, I
use malloc so rarely that I can't really say whether I would use a cast
or not. For example, I believe the cast /does/ add to code safety if
the pointer definition was far from the malloc call - but I would also
say it is usually much better to put the pointer definition at the
malloc call (to initialise the pointer), negating that point. The
overriding issue for me is to decide what makes code clearer, and I
don't think a categorical answer can be made. (The exception is if the
code needs to be compiled as C++, in which case there is only one
possible choice - use the cast.)


>
> ...
>> Being required to write C90 is not the problem. It is writing bad code
>> without good use of tools that is the problem. Sure, C99 lets you write
>> better code in an easier way than C90, and I dislike having to go back
>> to C90 on the few occasions when it is necessary in my job. But you are
>> certainly not required to use implicit function declarations just
>> because you are writing C90 - it is, after all, implicitly obsolete
>> already in that standard.
>
> As I said, the issue had nothing to do with deliberately using the
> construct - it was about protecting yourself from accidentally invoking
> the construct, and not getting any warning about the fact.
> It's also about not writing code that serves no useful purpose, and that
> applies even to more modern C compilers.

I agree that you should not write code that serves no useful purpose. I
don't agree that the cast necessarily is without purpose.

>
> ...
>>> As has already been pointed out by others, K&R did make the mistake of
>>> casting their malloc()s. In all fairness to them, that was long before
>>> anybody (the designers of C included) had had enough experience writing
>>> C to realize that it was a bad idea to cast a malloc() call.
>>>
>>
>> I don't see casting malloc()s as a mistake.
>
> That's too bad - you really should understand the reasons why it was (in
> C90). It's an important example of how not to design a computer language.

I am not sure I follow you. I understand why C allows void* to be
converted without a cast. I understand why C++ does not allow that, and
why such implicit conversions are a bad idea (along with many other
"implicit" things in C, such as "implicit int".) And I understand why
it was reasonable to think that omitting a cast on malloc results was a
safety point in C coding long ago. I simply don't agree that it is a
relevant safety point /now/, even for C90 programming.

>
>> ... An unnecessary cast, yes,
>> technically. It is something you can choose to do or not - if you feel
>> it makes code clearer, or less susceptible to errors, then use it. (For
>> example, if there is a distance between the declaration of the pointer
>> and the call to malloc, then the cast will likely lead to a warning if
>> you have got the type wrong.)
>
> But you can solve the "wrong type" problem equally well by removing the
> cast entirely. The real issue with malloc() is not getting the right
> type: that happens automatically when malloc() is used correctly (which
> doesn't require a cast - implicit conversions do the right thing without
> it). It's getting the right size - and that's something that can be
> arranged perfectly by using malloc(count * sizeof *p), without needing
> to know or care about what type *p is.
>

I appreciate that idiom, and it can be useful at times - but it is
certainly not the only way to allocate memory. And if the omission of
the cast makes such code clearer, less repetitive and has lower risk of
error, then I entirely agree - omit the cast in such cases. But if you
have a different arrangement for your malloc and the cast makes the code
clearer or safer, then use it.

And if you don't know or care what type *p is, why are you using it in
the first place?

James Kuyper

unread,
Feb 11, 2018, 1:34:04 PM2/11/18
to
On 02/11/2018 10:16 AM, David Brown wrote:
> On 09/02/18 18:48, James Kuyper wrote:
>> On 02/09/2018 12:10 PM, David Brown wrote:
...
> I realise that. And I can understand the reasoning for worrying about
> accidentally using undeclared functions in 1987. I can't understand
> worrying about it now, because tools will detect it.

[Note to others reading this message: since this discussion is occurring
on comp.lang.c++, it might seem odd, but this part of the discussion has
actually been about the behavior of malloc() under C, not about
std::malloc() under C++. Thread drift - sorry. Under C++, a cast is
unambiguously required, and that is not a point under contention.]

It isn't a significant worry now. But because it takes EXTRA effort to
insert the cast, with no associated benefit, the insignificant worry
about the possibility of the code getting compiled as C90 should be more
than sufficient to tip the scale.

>> True. and if there were any good reason for inserting the cast, your
>> vitriol against those who object to it would be more understandable. But
>> I know of none, (and I've heard the faulty arguments of those who think
>> it makes their code safer a great many times).
>
> I am only arguing here that the code safety reason for not casting
> malloc is a moot point - you can have other good reasons. Personally, I
> use malloc so rarely that I can't really say whether I would use a cast
> or not. For example, I believe the cast /does/ add to code safety if
> the pointer definition was far from the malloc call - but I would also

It doesn't. No matter how far away the declaration is from the point of
use, you can be quite certain that if the following line compiles
without a diagnostic, then

p = malloc(count * sizeof *p);

will either result in p containing a null pointer, or pointing at a
block of memory sufficiently large to to contain 'count' objects of
whatever type p points at. Inserting the cast only creates an error if
the type of *p is changed, an error that would not have been an error if
the cast had not been inserted. It's a trivially identified and
corrected error, but that correction is unnecessary extra work created
by insertion of an unnecessary cast.

...
>>> I don't see casting malloc()s as a mistake.
>>
>> That's too bad - you really should understand the reasons why it was (in
>> C90). It's an important example of how not to design a computer language.
>
> I am not sure I follow you. I understand why C allows void* to be
> converted without a cast. I understand why C++ does not allow that, and
> why such implicit conversions are a bad idea (along with many other
> "implicit" things in C, such as "implicit int".) ...

That isn't an "other ... thing". This is one aspect of "implicit int":
undeclared functions were implicitly declared as returning "int", and
that's precisely the feature I was referring to when I said this was "an
important example of how not to design a computer language". It's
important because the problem is subtle: well-chosen default behavior
generally makes things easier to use - but this particular default was
not well-chosen, because it made things more dangerous; understanding
why is something a language designer should learn.

> ... And I understand why
> it was reasonable to think that omitting a cast on malloc results was a
> safety point in C coding long ago. I simply don't agree that it is a
> relevant safety point /now/, even for C90 programming.

People (not many) are still using compilers that either don't warn about
this, or need to have options turned on to warn about it, and many of
those users are unaware of the desirability of doing so. If there's any
danger that my code will compiled by such people (possibly after being
cut-and-pasted out of it's normal context), writing it without a cast
both saves me a small amount of typing, and gives them a benefit. If
inserting the cast had any actual benefit (as it does, for instance, in
C++, where it's mandatory), it would be a different matter.

...
>> But you can solve the "wrong type" problem equally well by removing the
>> cast entirely. The real issue with malloc() is not getting the right
>> type: that happens automatically when malloc() is used correctly (which
>> doesn't require a cast - implicit conversions do the right thing without
>> it). It's getting the right size - and that's something that can be
>> arranged perfectly by using malloc(count * sizeof *p), without needing
>> to know or care about what type *p is.
>>
>
> I appreciate that idiom, and it can be useful at times - but it is
> certainly not the only way to allocate memory. And if the omission of
> the cast makes such code clearer, less repetitive and has lower risk of
> error, then I entirely agree - omit the cast in such cases. But if you
> have a different arrangement for your malloc and the cast makes the code
> clearer or safer, then use it.

I've seen claims that such situations can occur, and examples that were
intended to support those claims - none of which actually did so.

> And if you don't know or care what type *p is, why are you using it in
> the first place?

If you're using code involving pointers to 20+ different types (which,
in my experience, isn't particular uncommon), it's convenient to be
justifiably certain that

p = malloc(count * sizeof *p);

does the correct thing without having to remember or look up which of
those 20+ different types p points at, something that you would be
required to do if it were written with a cast. It's trivial to confirm
that the cast is correct if the malloc() call is the initializer for p,
but in precisely that context, inserting the cast is a particularly
jarring redundancy, not a useful safety measure.

Josef Moellers

unread,
Feb 12, 2018, 2:54:53 AM2/12/18
to
You mean CS, DS, SS, and ES?
I'm glad I never really had to program using FAR pointers ;-) I never
liked it from the start.

I programmed on a CDC Cyber (6000 iirc) machine, DEC PDP-10, PDP-11
during study, then a DG NOVA architecture (16 bit only with overlays),
then 68k, MIPS, Pyramid, and after that only Linux on x86 (32 and now 64
bit).

But FAR pointers are something else than pointer types and scalar vs
pointer types in general. And that's what casting is about. OTOH, I
agree that you'll have a hard time casting an int into a FAR pointer on
a '286 ;-)

Josef

Juha Nieminen

unread,
Feb 12, 2018, 3:00:22 AM2/12/18
to
Paavo Helde <myfir...@osa.pri.ee> wrote:
> It wasn't in the past. Malloc() used to by an O(1) adjustment of a
> couple of pointers in the allocator data structures.

I don't think that's even possible. If malloc() were the only memory
management operation ever made, then sure, it could be done like that.
But free() is also used, and the memory blocks freed that way ought to
be reused, if possible, by future malloc() calls (else you would end
up pretty much effectively with a de-facto memory leak, where the
program will consume more and more memory, never reusing memory that
it itself is freeing.)

Whenever you call malloc(), the memory allocation implemention needs
to find a suitably-sized free memory block. When the allocated memory
is highly fragmented from thousands of mallocs and frees, this can be
quite a complicated task.

And the memory fragmentation itself causes a slowdown, at least in
processors that use caches (which has been so since at least the
80486, I think. Perhaps even earlier.) Techniques used in some
other languages, like memory compaction, can't be used in C/C++.

Melzzzzz

unread,
Feb 12, 2018, 3:30:49 AM2/12/18
to
On 2018-02-12, Juha Nieminen <nos...@thanks.invalid> wrote:
>
> And the memory fragmentation itself causes a slowdown, at least in
> processors that use caches (which has been so since at least the
> 80486, I think. Perhaps even earlier.) Techniques used in some
> other languages, like memory compaction, can't be used in C/C++.

They can be used on pointers that are either registered or have
signature. Pretty possible. But compacting memory burns lot of cycles
and you have to update all pointers after that...


--
press any key to continue or any other to quit...

James R. Kuyper

unread,
Feb 12, 2018, 8:47:54 AM2/12/18
to
On 02/08/2018 02:02 AM, Paavo Helde wrote:
> On 8.02.2018 7:23, Juha Nieminen wrote:
...
>> The irony is that it's malloc() that's actually hideously expensive...
>
> It wasn't in the past. Malloc() used to by an O(1) adjustment of a
> couple of pointers in the allocator data structures.

Are you by any chance confusing malloc() with alloc()?
0 new messages