News for hackers. Stuff that matters.
Re:Function overloading in C by asveikau on Sunday July 26, 2015 @04:15PM (#9952076)
I find the lack of implicit conversion from void * really annoying. Any time I see someone cast the return type of malloc in a .c file, I curse C++ and die a little. This sounds like a trivial thing but it very much changes the feel of the language, namely into one where you need to insert pointless casts to satisfy a whiney compiler.
Re:Function overloading in C by sjolsen on Monday July 27, 2015 @04:56PM (#9957947)
>it very much changes the feel of the language, namely into one where you need to insert pointless casts to satisfy a whiney compiler

You could use the exact same argument against any type-safety feature: inserting "pointless" forward declarations, inserting "pointless" const qualifiers, etc., to "satisfy a whiney compiler."

The thing is, implicit conversion from void* is plainly unsound, type theoretically speaking. It has certain advantages, but is not necessary in order to have those advantages. For example, it makes use of malloc easier, but one could also make the vast majority of uses of malloc easier with something like

    #define ALLOC(TYPE,N) ((TYPE*)malloc(N*sizeof(TYPE)))
Or at least, one could if C had a remotely regular type syntax and/or actual type genericity. In contrast, you actually can write

    template <typename T>
    T* alloc(std::size_t n) {
        return static_cast <T*> (malloc (n * sizeof (T)));
    }
in C++, and it's even straightforward to add additional logic like overflow detection. Mark it inline and stick it in a header, and it's effectively the same as the above ALLOC macro, except that it actually works. Now, contrast

    int* array = malloc(256*sizeof(int));
with

    int* array = alloc<int> (256);
The C++ expression is simpler. Now, if you change the type of the array, but forget to change the allocation expression (for whatever reason; maybe you're using the absurd convention of declaring all your variables before you start initializing and using them):

    char* array = malloc(256*sizeof(int));
    // vs
    char* array = alloc<int> (256);
You get a type error with the C++ version, and the C version silently compiles. Not too much of an issue in this case -- you'll just waste a bunch of memory -- but go from char to int instead of int to char, and now you've got buffer overflows.

As for the argument made elsewhere in this thread that explicitly casting the result of malloc in C can mask the fact that malloc has been implicitly delcared, that's a language bug -- one that was fixed more than fifteen years ago.

Re:Function overloading in C by asveikau on Monday July 27, 2015 @10:14PM (#9959160)
Yes, I'm glad that you can use templates to re-implement operator new [] in a way that is susceptible to integer multiplication overflows.

In all seriousness though, I really mean it when I say I intellectually understand the C++ fan's response to a number of these issues. It took me a long time of writing C but I do understand the template-nerd habitat, and can acknowledge its strengths and use those tricks when working in a .cpp file. What frustrates me is to see the rigidity with which it is followed, and the failure to understand the world in a different way. There is a different world out there that doesn't mind an implicit conversion from void pointers, and they're not "wrong".

Re:Function overloading in C by sjolsen on Wednesday July 29, 2015 @02:53PM (#9970779)
My point isn't about templates or memory allocation (hence the code that doesn't check for overflow or worry about object construction). It's that implicit casts from void* are unnecessary in C++, addressing your complaint that it's annoying not to have them. My point is that the lack of implicit casts from void* should not be frustrating, because there are in what are by far the most common use cases equally convenient ways of doing the job.

>What frustrates me is to see the rigidity with which it is followed, and the failure to understand the world in a different way. There is a different world out there that doesn't mind an implicit conversion from void pointers, and they're not "wrong".

I have seen and lived in the world where casts from void* are common, and where making those casts implicit is a convenience (and that world is not the world of C++). I understand that world. I understand that viewpoint. I just strongly disagree with it.

Re:Function overloading in C by asveikau on Saturday August 01, 2015 @12:06AM (#9985876)
> It's that implicit casts from void* are unnecessary in C++

Very well aware of the C++-head's answer to these problems.

> My point is that the lack of implicit casts from void* should not be frustrating,

This was a discussion about whether or not C++ is a superset of C. I wrote up the difference that frustrates me the most and all other comments were missing.

Re:Function overloading in C by danieltillett on Sunday July 26, 2015 @06:15PM (#9952376)
Why the hate over casting malloc? It really is not a huge issue.

A related question is why do people use malloc if calloc is available?

Re:Function overloading in C by kazinator on Monday July 27, 2015 @12:05PM (#9955980)
Why programmers don't always use calloc: good question.

Firstly, anyone who does mallocs a block, and then immediately does a memset(block, 0, size) on that block almost certainly should be using calloc. The Linux kernel has kzalloc for this purpose as an alternative to kmalloc and it's fairly widely used.

Sometimes it is inefficient to set something to zero, when that is not the true initial value (some or all of the zeros will be overwritten with the true initial value before the object is used). If you malloc some structure and then painstakingly initialize every member, then calloc is wasteful.

If you malloc some structure, and then forget to initialize some of the members, calloc will hide that bug by initializing them to all zero bits. This might not be the correct value. If the uninitialized parts of the structure aren't clobbered from zeros, you can get an accurate diagnostic about that from a tool like Valgrind. On the other hand, if you aren't using that tool, the behavior with zeros is more deterministic. E.g. if a pointer is not initialized, but overwritten with zeros, on most mainstream platforms that will be a null pointer whose dereferencing uses are detected. If you don't have such a tool available, having the uninitialized bits be all zero is arguably better.

Much of this reasoning mirrors reasoning about whether to initialize a local variable to zero, if its true initialization actually happens later.

Re:Function overloading in C by danieltillett on Monday July 27, 2015 @05:51PM (#9958258)
In my experience using malloc over calloc make it much harder to find subtle bugs that result from off by one under set errors (i.e. you malloc 100 bytes, only set 99, and then you later read 100 bytes). When you use malloc the 100th byte is filled with random garbage, while with calloc you get something consistent.

Finding bugs where you write 101 bytes are easy to find with tools like Valgrind, but finding bugs where you malloc and don’t initialise the data structure correctly are a real pain.

Re:Function overloading in C by nitrogen on Tuesday July 28, 2015 @01:02PM (#9962961)
Valgrind will tell you about reading that 100th uninitialized byte.
Re:Function overloading in C by asveikau on Sunday July 26, 2015 @06:44PM (#9952462)
Probably the best reason to hate it is if you didn't include the right headers and malloc is therefore implicitly defined to return int. That creates a bug on most 64-bit compilers where int is 32 bits. The cast will mask the bug.

But more stylistically, let's be honest, something like this is way too verbose:

    struct foo *bar = (struct foo*)malloc(sizeof(struct foo));
I see code like this often, and I think the only explanation I can think of is that people don't know the language well enough to write:

    struct foo *bar = malloc(sizeof(*bar));
> A related question is why do people use malloc if calloc is available?

Seems like a very different question... calloc multiplies, calls malloc, and zeros. I guess if you don't want to multiply, don't want to zero, don't want to bother specifying another parameter, some combination of these, then malloc might be a better choice. Though I do recall a few years back the OpenBSD folks (possibly others too?) focusing on making sure calloc checks for overflow when it multiplies. [See also: reallocarray]

Re:Function overloading in C by kazinator on Tuesday July 28, 2015 @03:05PM (#9963800)
Any decent C compiler has a warning for the situation that an external function is called without a prototype declaration having been seen.

GCC also has diagnostics for when a function definition occurs without a declaration.

From GCC man page:

       -Wmissing-prototypes (C and Objective-C only)
           Warn if a global function is defined without a previous prototype
           declaration.  This warning is issued even if the definition itself
           provides a prototype.  The aim is to detect global functions that
           fail to be declared in header files.

       -Wstrict-prototypes (C and Objective-C only)
           Warn if a function is declared or defined without specifying the
           argument types.  (An old-style function definition is permitted
           without a warning if preceded by a declaration which specifies the
           argument types.)

       -Wimplicit-function-declaration (C and Objective-C only)
           Give a warning whenever a function is used before being declared.
           In C99 mode (-std=c99 or -std=gnu99), this warning is enabled by
           default and it is made into an error by -pedantic-errors. This
           warning is also enabled by -Wall.
Re:Function overloading in C by danieltillett on Sunday July 26, 2015 @07:23PM (#9952588)
If you turn on wall you should be warned that you didn’t include the right headers?

I was under the impression that lots of implementations of malloc actually call calloc under the hood. Unless you are calling malloc in a tight loop I can’t see the extra step of zeroing being a issue. In one of my programs where performance is critical I changed over all my mallocs to calloc and couldn’t measure the time change - I did find I had fewer difficult-to-duplicate bugs though.

Edit. Interestingly CERT recommends casting [1]. Seems to solve a much worse issue than forgetting to include stdlib.h

1. https://www.securecoding.cert.org/confluence/display/c/MEM02...