Path: csiph.com!eternal-september.org!feeder.eternal-september.org!reader01.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Re: "Why the C Language Will Never Stop You from Making Mistakes" by JeanHeyd Meneide Date: Fri, 28 Aug 2020 08:10:58 -0700 Organization: A noiseless patient Spider Lines: 128 Message-ID: <86eenqajr1.fsf@linuxsc.com> References: <877du33by4.fsf@nosuchdomain.example.com> <87r1sb1fu5.fsf@nosuchdomain.example.com> <87k0y312ua.fsf@nosuchdomain.example.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: reader02.eternal-september.org; posting-host="9035d0fd7127b99c8fa8fe8c15caa128"; logging-data="11521"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+++EgQHVOEVvDytfGnDyoRDMga6l5oL70=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:mnY/vNxyFkhbYkM9/cOdST9XlRA= sha1:MWT4JeUbseMnIipDffSpEc3be+s= Xref: csiph.com comp.lang.c:154103 Richard Damon writes: > On 8/13/20 2:23 AM, Keith Thompson wrote: > >> John Forkosh writes: >> [...] >> >>> I personally do bind the * to the *p_, but would also write it as, >>> typedef struct Meow meow >>> meow *p_cat = (meow *)malloc(sizeof(meow)); >>> which is just how I personally like to read and write it. >>> And if that faq doesn't like it, then it can just go rewrite >>> itself:) >> >> OK, *why* do you like the cast? What is the benefit of it? Why do >> you think having the cast is better than not having it? >> >> You are of course entitled to your opinion, I'm just wondering if >> you have a basis for it. > > The basic argument against the cast is that if you forget the > include/prototype (which in my opinion, that error should be > configured to be a fatal errpr) the cast hides that error. > > The use of the cast detects if the type of the variable is the > wrong type of pointer, maybe not as likely in the case where you > are declaring the pointer, but if you are using an existing > pointer isn't that hard. > > The idiom > > p = malloc(sizeof(*p)); > > also doesn't protect from the 'wrong' type, yes, it will malloc > for the right type for the pointer, but maybe not for the code > that is using it. > > Using a macro like: > > > #define NEW(T) (T *)malloc(sizeof T) > > allows the use of > > p = NEW(Foo) > > and if p isn't a Foo*, you get an error (or at least a constraint > violation), and thus you know the code needs to be checked if it > needs to change based on the new type of *p. I have read through your postings in this thread on this topic. Directly put, I disagree with your position and with your proposal. There are several factors or principles that argue against the position. Some of these are: (1) Casting is bad. Unnecessary casts should be avoided. (2) It violates the factoring principle. The type name T is written in several places rather than just once, on the declaration of the variable assigned the value. (3) As a consequence of (2), it raises maintenance costs. If a change in type is needed, the source needs modifying in several places rather than just one. (4) As a general rule, we would like to avoid situations that require multiple parts of a program to be kept "in sync". This rule is related to (2) but not exactly the same. In some cases being forced to synchronize two distinct parts of a program has some benefit, but I don't see any benefit here relative to the usual idiom `p = malloc( sizeof *p )`. (5) The reasoning offered has an air (at least it does to me) of being disingenuous. The concern expressed is not for assigning to p but later using p to initialize the allocated object. If the real concern is making sure the initialization matches the type of *p then that concern should be addressed in the code that performs the initialization, not the code that does the malloc() call. There are several ways we might effect such initializing so that type correctness is ensured: (1) Have an appropriately typed initializing function: T *p = malloc( sizeof *p ); initialize_T( p ); (2) Have an appropriately typed value-generating function: T *p = malloc( sizeof *p ); *p = some_T_value( x, y, z ); (3) Use a compound literal: T *p = malloc( sizeof *p ); *p = (T){ .foo = 1, .bas = 0, }; In addition to more directly addressing the true concern, these patterns promote more reliable programming practices. Turning to the proposal, here is a different one: #define NEW(p) (p) = malloc( sizeof *(p) ) We can use this pseudo-function as a expression-statement: T *p; ... NEW(p); It also is usable in declarations: T *NEW(p); I'm sure some people will react negatively to the declaration pattern, and that's okay. For those who don't mind it the pattern is there. One final comment: although I disagree with some of your comments I have not set out to persuade or convince anyone otherwise. My purpose is simply to express my disagreement and put forth an alternative proposal, for those who may be interested. I am as always interested to hear whatever reactions you might have but I'm not here to argue about which approach is better.