Most active commenters
  • bobmcnamara(6)
  • o11c(3)

←back to thread

48 points ingve | 27 comments | | HN request time: 0.861s | source | bottom
Show context
bobmcnamara ◴[] No.44389188[source]
Ages ago I worked with a system where malloc(0) incremented a counter and returned -1.

free(-1) decremented the counter.

This way you could check for leaks :p

replies(3): >>44389317 #>>44389346 #>>44389977 #
1. o11c ◴[] No.44389317[source]
Noncompliant, since `malloc(0)` is specified to return a unique pointer if it's not `NULL`.

On most platforms an implementation could just return adjacent addresses from the top half of the address space. On 32-bit platforms it doesn't take long to run out of such address space however, and you don't want to waste the space for a bitmap allocator. I suppose you could just use a counter for each 64K region or something, so you can reuse it if the right number of elements has been freed ...

replies(3): >>44389517 #>>44389553 #>>44395128 #
2. LPisGood ◴[] No.44389517[source]
Noncompliant, but what could this reasonably impact?
replies(2): >>44389559 #>>44389605 #
3. bobmcnamara ◴[] No.44389553[source]
> Noncompliant, since `malloc(0)` is specified to return a unique pointer if it's not `NULL`.

I know I've seen that somewhere, but may I ask what standard you're referring to?

If I recall correctly, this was an archaic stackless microcontroller. The heap support was mostly a marketing claim.

replies(3): >>44389646 #>>44389679 #>>44390133 #
4. bobmcnamara ◴[] No.44389559[source]
> Noncompliant, since `malloc(0)` is specified to return a unique pointer if it's not `NULL`.

I know I've seen that somewhere, but may I ask what standard you're referring to?

replies(1): >>44389694 #
5. o11c ◴[] No.44389605[source]
Pointers are frequently used as keys for map-like data structures. This introduces collisions that the programmer can't check for, whereas NULL is very often special-cased.
6. jmgao ◴[] No.44389646[source]
C89: https://port70.net/%7Ensz/c/c89/c89-draft.html

If the size of the space requested is zero, the behavior is implementation-defined; the value returned shall be either a null pointer or a unique pointer.

replies(1): >>44389900 #
7. fredoralive ◴[] No.44389679[source]
Presumably the ANSI C standard or one of the later editions? They also cover the standard library as well as the language. (Presumably the bit about "Each such allocation shall yield a pointer to an object disjoint from any other object." if the random C99 draft I found via google is accurate to the final standard - I suppose you might question if this special use is technically an allocation of course).

Of course, microcontrollers and the like can have somewhat eccentric implementations of languages of thing and perhaps aren't strictly compliant, and frankly even standard compliant stuff like "int can be 16 bits" might surprise some code that doesn't expect it.

8. masfuerte ◴[] No.44389694{3}[source]
It's POSIX.

> Each [...] allocation shall yield a pointer to an object disjoint from any other object. The pointer returned points to the start (lowest byte address) of the allocated space. If the space cannot be allocated, a null pointer shall be returned. If the size of the space requested is 0, the behavior is implementation-defined: either a null pointer shall be returned, or the behavior shall be as if the size were some non-zero value, except that the behavior is undefined if the returned pointer is used to access an object.

https://pubs.opengroup.org/onlinepubs/9799919799/functions/m...

replies(1): >>44390087 #
9. f1shy ◴[] No.44389900{3}[source]
Isn’t -1 basically 0xffff which is a constant pointer? What am I missinterpreting?
replies(1): >>44389943 #
10. comex ◴[] No.44389943{4}[source]
If you call malloc(0) multiple times (without freeing in between) and get -1 each time, then the pointer is not unique.
replies(3): >>44390238 #>>44392262 #>>44396934 #
11. MaxBarraclough ◴[] No.44390087{4}[source]
Not just POSIX, also the ISO C standard itself. https://en.cppreference.com/w/c/memory/malloc
replies(2): >>44390145 #>>44390234 #
12. o11c ◴[] No.44390133[source]
(you duped your comment under the other subthread)

From C89, §7.10.3 "Memory management functions":

> If the size of the space requested is > zero, the behavior is implementation-defined; the value returned shall be either a null pointer or a > unique pointer.

The wording is different for C99 and POSIX, but I went back as far as possible (despite the poor source material; unlike later standards C89 is only accessible in scans and bad OCR, and also has catastrophic numbering differences). K&R C specifies nothing (it's often quite useless; people didn't actually write against K&R C but against the common subset of extensions of platforms they cared about), but its example implementation adds a block header without checking for 0 so it ends up doing the "unique non-NULL pointer" thing.

13. masfuerte ◴[] No.44390145{5}[source]
That doesn't say the pointer has to be unique.
replies(2): >>44390258 #>>44390287 #
14. ◴[] No.44390234{5}[source]
15. ◴[] No.44390238{5}[source]
16. jcranmer ◴[] No.44390258{6}[source]
cppreference isn't the standard, and while the text they write looks like it's the same verbiage that would be authoritative, it's not. (And there's some criticism of it from standards committee members in that regard).

The current C standard text says:

> The order and contiguity of storage allocated by successive calls to the aligned_alloc, calloc, malloc, and realloc functions is unspecified. The pointer returned if the allocation succeeds is suitably aligned so that it can be assigned to a pointer to any type of object with a fundamental alignment requirement and size less than or equal to the size requested. It can then be used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated). The lifetime of an allocated object extends from the allocation until the deallocation. Each such allocation shall yield a pointer to an object disjoint from any other object. The pointer returned points to the start (lowest byte address) of the allocated space. If the space cannot be allocated, a null pointer is returned. If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned to indicate an error, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.

So yeah, the allocations are required to be unique (at least until it's free'd).

replies(1): >>44392253 #
17. bobmcnamara ◴[] No.44390287{6}[source]
It is in ANSI 89, under memory management functions.
18. bobmcnamara ◴[] No.44392253{7}[source]
> Each such allocation shall yield a pointer to an object disjoint from any other object.

Phrasing could be slightly more clear to prevent someone from making the argument that -1 is disjoint from all objects as it does not point to an object

replies(1): >>44395792 #
19. bobmcnamara ◴[] No.44392262{5}[source]
But do we need a unique pointer or merely a pointer that is disjoint from all objects?
replies(1): >>44394909 #
20. david-gpu ◴[] No.44394909{6}[source]
As per the specification, it has to be a unique pointer.

Being tasked to implement a specification typically means having to pass extensive conformance tests and having to answer for instances of noncompliance. You soon learn to follow the spec to the letter, to the best of your abilities, unless you can make a strong case to your management for each specific deviation.

replies(2): >>44396834 #>>44396893 #
21. j1elo ◴[] No.44395128[source]
Oh but no worries with compliance, it always returned a newly created -1, never repeating the same one!
22. Joker_vD ◴[] No.44395792{8}[source]
And if you use 0 as the value of NULL pointer, then -1 can't ever point to an object (because adding 1 to it should generate a non-NULL pointer, so that pointer comparisons are not UB).

So yeah, C implementations have to reserve at least two addresses, not just one. By the way, the standard to this day allows NULL, when cast to a pointer type, to be something else than all-bits-zero pattern (and some implementations indeed took this opportunity).

replies(1): >>44396796 #
23. ynik ◴[] No.44396796{9}[source]
But adding 1 to a pointer will add sizeof(T) to the underlying value, so you actually need to reserve more than two addresses if you want to distinguish the "past-the-end" pointer for every object from NULL.

--

While it's rare to find a platform nowadays that uses something other than a zero bit pattern for NULL as normal pointer type; it's extremely common in C++ for pointer-to-member types: 0 is the first field at the start of a struct (offset 0); and NULL is instead represented with -1.

replies(1): >>44397276 #
24. magicalhippo ◴[] No.44396834{7}[source]
But the letter is non-specific. It doesn't clarify if unique refers to unique when compared to non-zero allocations, or unique when called multiple times.

The C99 standard[1] seems to have worded it more precisely:

If the size of the space requested is zero, the behavior is implementation- defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.

[1]: https://rgambord.github.io/c99-doc/sections/7/20/3/index.htm...

25. minetest2048 ◴[] No.44396893{7}[source]
This is embedded C where standard abuse is a thing: https://thephd.dev/conformance-should-mean-something-fputc-a...
26. mystified5016 ◴[] No.44396934{5}[source]
Null is not a unique pointer, it's a contant like -1

It returns multiple types of null pointer

27. Joker_vD ◴[] No.44397276{10}[source]
> so you actually need to reserve more than two addresses if you want to distinguish the "past-the-end" pointer for every object from NULL.

Well, yes and no. A 4-byte int can not reside at -4, but a char could be; but no object can reside at -1. So implementations need to take care that one-past-the-end addresses never equal to whatever happens to serve as nullptr but this requirement only makes address -1 completely unavailable for the C-native objects.