Live data from Hacker News

Passing nothing is surprisingly difficult

davidben.net

51–60 of 80 posts

Re: Passing nothing is surprisingly difficult

#51

There is no problem with memcpy other than that you can't use a null pointer. You can memcpy zero bytes as long as the pointer is valid. This works in a good many circumstances; just not circumstances where the empty array is represented by not having an address at all. For instance, say we write a function that rotates an array: it moves the low M bytes to the top of the array, and shuffles the remaining M - N bytes…

N2464 [0]: there was lots of implementation divergence on what realloc(ptr, 0) did (especially with BSD, which allegedly doesn't free the memory at all?), so they just declared it UB. [0] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf

I read this as "there were a lot of buggy implementations of the C standard, so we imported the bug into the standard". Crazy. Don't make the language less defined going forward.

When those implementations eventually pick up C23, they surely could fix the bug as well. At best this should have been an errata/defect for the previous standard, so that the previous standards document behavior of implementations of said standards.

Re: Passing nothing is surprisingly difficult

#52
post #33

Earlier quoted context omitted.

Just a reminder to myself how happy I am to leave this academic level of snobbery behind and migrate to languages that help getting things done and save you from nonsense like this. I did C for about ten years. Hope I’ll never have to do a single line of it again.

> this academic level of snobbery ...is actually very uncommon in the C world (at least much less common than in the C++ or Rust world). Specific compilers and stdlibs have specific behaviours which may or may not fully agree with the details written down in the standard. You'll have to pick a subset of compilers you want to support and write your code against that subset of compilers. It's not perfect, but also not…

In theory, it's really not much different than developing some website that has to run on mobile devices and desktops and all manners of browser implementations and versions and OS and screen sizes and be accessible to screen readers as well. Or is not JavaScript a "language that helps get things done"?

In fact, I'll take the stable, well defined compilers that I can actually test against any day.

Re: Passing nothing is surprisingly difficult

#53

It's obviously too late to change this in Rust's case, but I wonder whether being able to differentiate between None and the empty slice is actually a necessary property in general? There are a bunch of languages where empty arrays are "falsy", and in those it's not recommendable to use the two to differentiate valid states. Feels like the same could apply here

The main complaint in the post is basically that Rust's actual bona fide slice type doesn't work the way cobbled together library types for this purpose in C or C++ do.

The C++ type discussed is much newer than Rust (std::span was standardized in C++ 20).

Yes in many cases what C++ APIs mean here isn't a slice of zero Ts at all but instead None, and Rust has an appropriate type for that Option which works as expected, and so in many cases where people have built an API which they think is &[T] and are trying to make it with the unsafe functions mentioned it's actually Option they needed anyway, they don't even have a type correct design.

Re: Passing nothing is surprisingly difficult

#54
It’s so silly to talk about C not allowing null on memcpy. That’s a thing the spec says, I guess?

The solution is clear: just ignore the C spec. It’s total garbage. Of course you can memcpy between any ptr values if the count is zero and those ptr values don’t have to point to anything.

Re: Passing nothing is surprisingly difficult

#55

It’s so silly to talk about C not allowing null on memcpy. That’s a thing the spec says, I guess? The solution is clear: just ignore the C spec. It’s total garbage. Of course you can memcpy between any ptr values if the count is zero and those ptr values don’t have to point to anything.

Better be rolling your own compiler in that case. Or your own memcpy with a different name.

UB to pass memcpy to null means after that call, the pointer is assumed to be non-null. So if(ptr) can constant fold. Maybe faster.

I'm in agreement with you on this but your compiler probably isn't.

Re: Passing nothing is surprisingly difficult

#56
post #28

Earlier quoted context omitted.

I don't think insulting the BSD folks is very nice. They probably had good reasons for their decisions.

Mostly, the BSD people think that POSIX and ISO C are just forks of Unix documentation, that are mainly used for making non-Unix systems look Unix-like. BSD comes from real Unix DNA and so following those is optional; whatever direction BSD takes is Unix by definition, as if it's forever 1983.

Interesting. I wonder if that means BSD's compiler toolchain has ignored the strict-aliasing misfire that makes such a mess of the ISO associated ones.

Re: Passing nothing is surprisingly difficult

#57

Earlier quoted context omitted.

> Programs written to the C99 standard can resize a dynamic vector down to empty with a resize(ptr, 0). C99 has no resize() function. Assuming you mean realloc(), C99 does not guarantee you can use realloc() in this manner. See also: https://news.ycombinator.com/item?id=38850575 https://stackoverflow.com/questions/16759849/using-realloc-x... https://wiki.sei.cmu.edu/confluence/plugins/servlet/mobile?c... https://deve…

> C99 does not guarantee you can use realloc() in this manner Yes it does. It requires support for reallocing down to zero, which results in an object that is like one that comes from malloc(0). (What some people think is that realloc(x, 0) is equivalent to free(x). It isn't. Resizing down to zero isn't freeing. It might be, if malloc(0) doesn't allocate anything and just returns null. Why some people think realloc(x…

[deleted]

Re: Passing nothing is surprisingly difficult

#58

Earlier quoted context omitted.

> Programs written to the C99 standard can resize a dynamic vector down to empty with a resize(ptr, 0). C99 has no resize() function. Assuming you mean realloc(), C99 does not guarantee you can use realloc() in this manner. See also: https://news.ycombinator.com/item?id=38850575 https://stackoverflow.com/questions/16759849/using-realloc-x... https://wiki.sei.cmu.edu/confluence/plugins/servlet/mobile?c... https://deve…

> C99 does not guarantee you can use realloc() in this manner Yes it does. It requires support for reallocing down to zero, which results in an object that is like one that comes from malloc(0). (What some people think is that realloc(x, 0) is equivalent to free(x). It isn't. Resizing down to zero isn't freeing. It might be, if malloc(0) doesn't allocate anything and just returns null. Why some people think realloc(x…

I literally quoted someone from WG14.

HN really needs the ability to block trolls.

Re: Passing nothing is surprisingly difficult

#60

Earlier quoted context omitted.

Indeed, you couldn't reliably free() the old pointer if the realloc(ptr, 0) failed. But xrealloc(ptr, 0) (or equivalent) would still be perfectly consistent, assuming you trust your implementation to support non-null 0-size allocations in the first place. It's very common to just "leak it all and abort" on a critical error like memory exhaustion. There's a reason most non-C languages expose an infallible allocation A…

Yes. If realloc(ptr, 0) returns a null pointer, you don't know whether that's due to a failure (in which case ptr is still valid) or whether it's the happy case (ptr was freed, and the zero-sized request for replacing it produced a null). Thus you don't know whether ptr is still a valid pointer. If it's valid and you treat it as invalid (hands off), that's a leak. If it's invalid and you treat it as valid (free it),…

I'm not talking about implementations that produce a 'successful' null pointer. I'd consider that a quality-of-implementation issue, in that implementations are responsible for returning non-null on 0-size success in the same way they're responsible for not just stubbing out every single malloc() call, so just assuming that a null output indicates failure is appropriate. (Implementations transitioned ages ago toward returning non-null for 0-size requests for good reason!)

Instead, the problem is about a realloc(ptr, size) that returns null to indicate failure. If size > 0, then the data behind ptr remains unmodified and can be later freed. But if size == 0 (and the 0-size allocation fails), then the data behind ptr is unconditionally freed according to many implementations.

This makes it unsafe to access the data behind ptr after a realloc() failure, unless you've checked that size > 0. But I argue that by making the whole thing UB instead of leaving it sufficiently unspecified, the xrealloc(ptr, size) use case that doesn't care about the leak on failure is made more complicated unnecessarily.

Post reply on HN