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?