There is no situation where the size calculation changes because you are using void pointers. If you insist on using them immediately you essentially ask the compiler for sizeof(void). Using sizeof(type) to say differently isn't an example I will accept, because you have to use a defined type anyway, and therefore you can use a defined pointer type on the left of the malloc call.The second method would imply you need to change the allocation,
Once again I must point out generic programming. A perfect example in which you allocate memory using a void pointer and cast the data to whatever you want in your user program. Yes, you can't dereference the void* but that doesn't make it useless. Why is that so hard to understand?
Another example I can think of would be a generic slab allocator which is something very commonly used in kernels. The slab allocator uses void* to allocate whatever structs are getting used in the client programs faster than through a regular malloc call. The allocator calls a constructor for the struct via a pointer to a function provided by the user program. At that point, the memory allocated by the void* internally gets used via a cast to a specific pointer. However, this happens without the allocator's knowledge. As far as it is concerned EVERYTHING is a void pointer.
Because your thinking about it the wrong way.Once again I must point out generic programming. A perfect example in which you allocate memory using a void pointer and cast the data to whatever you want in your user program. Yes, you can't dereference the void* but that doesn't make it useless. Why is that so hard to understand?
Another example I can think of would be a generic slab allocator which is something very commonly used in kernels. The slab allocator uses void* to allocate whatever structs are getting used in the client programs faster than through a regular malloc call.
With respect to generic programming, you couldn't provide me with a real world example where void * had to be used immediately. The programmer would just use regular boring pointers, even when something wants void * because void * can /point to anything/. If a variable has to be pushed on a function's stack frame as void * then the compiler can handle that without special effort from the programmer.
If you are writing those functions, you are still using a defined type a majority of the time, whether it be char * or something fancier. A use for void * that doesn't go away or appear with an assignment does not exist.
The slab allocator -- well I have never written one, but I speculate -- may also use void * /as a result pointer temporarily/ but that has dick to do with the allocated size.
Yes, the purpose of code readability is to minimise costs. However, it is not just production costs that matter. It is the life cycle costs aggregated over every modification of that software. Reducing production costs is meaningless if it causes user dissatisfaction (i.e. bugs) sufficient to lose customers, or if the cost to fix those bugs significantly exceed the production costs.
I agree with you that no programmer can expect to modify code without a sufficient understanding of it. But what are the practical circumstances in which readability matters? When the code is being modified.
It is one thing to seek to minimise effort to understand what a section of code does. It is quite another to advocate a technique in which a well-intended programmer action can yield code that misleads some future programmer.
It is that cascading effect that concerns me: programmer A changes a declaration, but does not realise that change generates a need for a change in another part of the code. Because that second change has not been made, programmer B is misled so, while believing she is writing correct code, actually introduces faulty code that looks correct. Subsequently this comes back as a bug report from a user. The effort for programmer C to track down and then resolve the cause can actually be quite significant, because the code continues to be misleading - and programmer C is probably trying to do that with some manager or client breathing down his neck. The immediate production costs of that may be small. The life cycle costs will be significant.
So, essentially we have two options. The first is a code construct that reduces likelihood of misleading a development team, but potentially (I'm unconvinced, but you are) requires more programmer effort to add new functionality. The second option is one which reduces effort to understand the code, but potentially misleads the programmer into a false sense of security. You are advocating the latter.
Not at all. I'm arguing that code that can mislead a conscientious programmer is worse than code that a foolish programmer can trip on. The foolish programmer will get in trouble regardless. The conscientious programmer has a chance of avoiding trouble if s/he is not mislead.
You're the one who suggested it is necessary to know the amount of memory allocated by the malloc() call. I provided a way to do that. I don't actually consider the requirement significant. If programmers need to know the exact amount of memory used by a program, they will find a way to compute it reliably.
In any event, the fact you may know what machines you code for is only relevant if that knowledge is in the head of the programmers who eventually have to maintain your code. In the real world, management decisions often mean that some other programmer is required to (say) port your code to a new system, which was not within the range you coded for. Or, since time has past, you have forgotten the details and the documentation has been misplaced (assuming you remembered to write it).
That's specious as, at some point, that void pointer will need to be converted into some other pointer type so it can be used.
Yes, the "a = malloc(n * sizeof *a)" will trigger a compiler error if a is changed from a non-void pointer to a void pointer.
The only difference is that your technique means that the malloc() call misleads some future programmer - who needs to use that allocated memory - into believing that a is a non-void pointer. The programmer then attempts to dereference that pointer, and encounters an error as a result.
Last edited by grumpy; 04-11-2010 at 01:30 AM.
@grumpy
I see what you are saying. It's been a really interesting debate and I guess it all comes down to a matter of preference since both methods are correct. I appreciate the effort you put into stating your case. It was a good read.
@whiteflags
I'm sorry maybe I am just very tired but I don't really seem to follow your point. I don't understand what "using the void* immediately has to do with anything". This debate has been mostly about the contrary, allocating memory for a pointer FAR AWAY from its declaration. e.g. Suppose you have a global pointer declared in a different header file and allocated in another source file.
In any case, it was a good read everyone! Best of luck in your pointer use
Well that's annoying that I can't explain anything...
I refuse to put too much more effort into this, but void * semantically does not mean anything other than "Oh here's an address to something somewhere". Therefore, I always assign void * pointers to some other type, and almost never assign /to/ void *, as it is not necessary. Even when I do assign to void * it has always been valid not to use void *. Casting them to something else is always an option, but by no means an excellent preference, when you can just assign.
It doesn't really buy you anything if you need to find a declaration. That notion you want to convince me of is truly bizarre, since it implies trusting the closest thing you see, which may be a sizeof thing or a cast. The only thing that matters of course is
foo bar;
And in IDEs, bar will show up as foo in a tooltip. Such a nicety.
Last edited by whiteflags; 04-11-2010 at 02:44 AM.