Path: csiph.com!news.mixmin.net!eternal-september.org!feeder.eternal-september.org!.POSTED!not-for-mail From: Keith Thompson Newsgroups: comp.lang.c Subject: Re: "Catch-23: The New C Standard,Sets the World on Fire" by Terence Kelly with Special Guest Borer Yekai Pan Date: Wed, 05 Apr 2023 17:40:36 -0700 Organization: None to speak of Lines: 87 Message-ID: <87edoxhmgr.fsf@nosuchdomain.example.com> References: <874jpv84uv.fsf@bsb.me.uk> MIME-Version: 1.0 Content-Type: text/plain Injection-Info: dont-email.me; posting-host="8e05faf3dfe18b7b8f8c1a72052c5d80"; logging-data="105287"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18wFk1hlW35nMVU/4KvrYFN" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux) Cancel-Lock: sha1:UWn0iPTeIrTFM+PS5NO2edm7AHk= sha1:Y2gI/2oIbjKg2bUWX9CeaBdnqwM= Xref: csiph.com comp.lang.c:169846 David Brown writes: > On 05/04/2023 20:55, Dan Cross wrote: >> In article , >> David Brown wrote: >>> On 05/04/2023 15:29, Dan Cross wrote: >>>> In article <874jpv84uv.fsf@bsb.me.uk>, >>>> Ben Bacarisse wrote: >>> >>>>> I got the impression they knew that. However, in C89, realloc(ptr, 0) >>>>> must behave like free(ptr). That changed, I think, in C99 with the >>>>> removal of that guarantee. There is some evidence that they are little >>>>> confused about that guarantee having been lost, thought of course it is >>>>> still permitted. >>>> >>>> Indeed, you are correct. From C89, 7.10.3.3: "if *size* is zero >>>> and *ptr* is not a null pointer, the object it points to is >>>> freed." >>> >>> I don't have a C89 reference handy, but I don't think realloc(ptr, 0) >>> has ever been required to behave like free(ptr). That is one of the >>> allowed behaviours, but it is not the only one. In particular, even if >>> it first acts like free(ptr), it can then allocate a zero-size buffer >>> afterwards. Thus it may not be behaving like free(ptr) alone - it can >>> do more. And if you don't take that into account, your code will leak >>> memory. >> What I quoted above is the actual text from C89. To repeat: >> "If *size* is zero and *ptr* is not a null pointer, >> the object it points to is freed." >> (C89, sec 7.10.3.3.) >> - Dan C. >> > > I am not disagreeing with that. My point is what happens /after/ the > pointer ptr is freed. As far as I can tell (and I could be wrong), > the function can behave in a manner similar to malloc(0) - it can > either do no allocation and return a null pointer (in which case the > whole thing is like free(ptr)), or it could allocate a zero-size > object and return a unique pointer. (Or it could try to do such an > allocation, and fail, possibly affecting errno.) It is this aspect > that is, as far as I can tell, implementation dependent and > non-portable. If realloc(ptr, 0) returns a null pointer, then its behavior is subtly different from that of free(), which doesn't return a value. That's a minor quibble. The standard doesn't say that any of the memory allocation functions set errno, so any of them could set it to any non-zero value regardless of whether they succeed or fail. Under C89/C90 rules, realloc(ptr, 0) definitely frees the object pointed to by ptr (assuming it had been allocated properly). The standard doesn't clearly say what realloc() returns in that case. Common sense suggests that it behaves like malloc(0), so it returns either a null pointer or a unique pointer (and the choice is implementation-defined, so it must be documented). The caller can easily tell whether it returned a null pointer or not, and in either case it can safely pass the result to free(). ptr1 = malloc(42); ptr2 = realloc(ptr1, 0); // At this point, we know that the 42-byte object has been freed free(ptr2); // this is safe and avoids any memory leak In C99 and later, the description of realloc() doesn't specifically address the case of size==0. But since it says that it deallocates the old object and allocates a new object, that's covered by the description in the parent section, which also applies to malloc and friends. (C89/C90 says realloc changes the size of the object, which is IMHO rather sloppy wording.) Again, the caller can tell whether realloc(ptr, 0) returned a null pointer or not, and can safely pass it to free() either way. C17 adds this (which I missed before): If size is zero and memory for the new object is not allocated, it is implementation-defined whether the old object is deallocated. Presumably for an implementation where malloc(0) returns a null pointer, this would not apply. But for an implementation where malloc(0) returns a unique pointer (like malloc(1) except that dereferencing it has undefined behavior), that allocation might fail. -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for XCOM Labs void Void(void) { Void(); } /* The recursive call of the void */