43

I vaguely remember reading about this a couple of years ago, but I can't find any reference on the net.

Can you give me an example where the NULL macro didn't expand to 0?

Edit for clarity: Today it expands to either ((void *)0), (0), or (0L). However, there were architectures long forgotten where this wasn't true, and NULL expanded to a different address. Something like

#ifdef UNIVAC
     #define NULL (0xffff)
#endif

I'm looking for an example of such a machine.

Update to address the issues:

I didn't mean this question in the context of current standards, or to upset people with my incorrect terminology. However, my assumptions were confirmed by the accepted answer:

Later models used [blah], evidently as a sop to all the extant poorly-written C code which made incorrect assumptions.

For a discussion about null pointers in the current standard, see this question.

Community
  • 1
  • 1
György Andrasek
  • 8,187
  • 4
  • 48
  • 76
  • 1
    `NULL` doesn't expand to an address. Are you truly asking when the `NULL` macro was defined to something else, or do you mean to ask when was the underlying representation of the null pointer constant not all-bits-zero? – jamesdlin Apr 08 '10 at 02:48
  • Last I saw, it could expand to either ((void *)(3L - 2L - 1L)), (((((0) * 0x55)))), or ('\0'). (That's in C. Some of these would not be allowed in C++.) – Windows programmer Apr 08 '10 at 06:21
  • 2
    The answer you accepted does not answer your question, as asked. Either correct the question (if that's not what you wanted to ask), or re-accept some other answer. The details are in the comments. – AnT stands with Russia Apr 08 '10 at 14:03

7 Answers7

39

The C FAQ has some examples of historical machines with non-0 NULL representations.

From The C FAQ List, question 5.17:

Q: Seriously, have any actual machines really used nonzero null pointers, or different representations for pointers to different types?

A: The Prime 50 series used segment 07777, offset 0 for the null pointer, at least for PL/I. Later models used segment 0, offset 0 for null pointers in C, necessitating new instructions such as TCNP (Test C Null Pointer), evidently as a sop to [footnote] all the extant poorly-written C code which made incorrect assumptions. Older, word-addressed Prime machines were also notorious for requiring larger byte pointers (char *'s) than word pointers (int *'s).

The Eclipse MV series from Data General has three architecturally supported pointer formats (word, byte, and bit pointers), two of which are used by C compilers: byte pointers for char * and void *, and word pointers for everything else. For historical reasons during the evolution of the 32-bit MV line from the 16-bit Nova line, word pointers and byte pointers had the offset, indirection, and ring protection bits in different places in the word. Passing a mismatched pointer format to a function resulted in protection faults. Eventually, the MV C compiler added many compatibility options to try to deal with code that had pointer type mismatch errors.

Some Honeywell-Bull mainframes use the bit pattern 06000 for (internal) null pointers.

The CDC Cyber 180 Series has 48-bit pointers consisting of a ring, segment, and offset. Most users (in ring 11) have null pointers of 0xB00000000000. It was common on old CDC ones-complement machines to use an all-one-bits word as a special flag for all kinds of data, including invalid addresses.

The old HP 3000 series uses a different addressing scheme for byte addresses than for word addresses; like several of the machines above it therefore uses different representations for char * and void * pointers than for other pointers.

The Symbolics Lisp Machine, a tagged architecture, does not even have conventional numeric pointers; it uses the pair <NIL, 0> (basically a nonexistent <object, offset> handle) as a C null pointer.

Depending on the "memory model" in use, 8086-family processors (PC compatibles) may use 16-bit data pointers and 32-bit function pointers, or vice versa.

Some 64-bit Cray machines represent int * in the lower 48 bits of a word; char * additionally uses some of the upper 16 bits to indicate a byte address within a word.

Jonathan Callen
  • 11,301
  • 2
  • 23
  • 44
janks
  • 2,120
  • 16
  • 13
  • +1 for finding the solid examples. FWIW, I think ISO C now mandates that 0 in the source code means null pointer no matter the underlying representation used. But that's from memory and I don't have a copy of C1x floating around at the moment. – paxdiablo Apr 08 '10 at 02:40
  • 6
    In C source code the NULL macro would still be either an integer constant expression that evaluates to 0 or a (void *) cast of the same. The resulting pointer value might have a representation that isn't 0 but that doesn't mean the source code can assume it will be 0xffff or anything like that. – Windows programmer Apr 08 '10 at 06:23
  • 1
    @Windows Programmer: true now that the C standard mandates that NULL is a null pointer constant (as opposed to just any constant equal to a null pointer). All of those machines predate the standard, though, so it's certainly possible that one of them did `#define NULL ((void*)-1)`, or `#define NULL __nullpointer`, where __nullpointer is a compiler built-in, or whatever. They're the right places to be looking for such a thing. – Steve Jessop Apr 08 '10 at 08:11
  • @SteveJessop: Can you point any text in the standard that rules out `#define NULL __builtin_compiler_magic_nil_pointer`? – janks Apr 08 '10 at 09:25
  • See my other comment. I'm not sure, though, I might take back part of what I said. `__builtin_compiler_magic_nil_pointer` might be all right, provided that the implementation defines (as an extension) that `__builtin_compiler_magic_nil_pointer` is an integral constant expression with value 0, or that it behaves like one in every possible way. g++ in fact does #define NULL to `__null`, but gcc (in C mode) doesn't. – Steve Jessop Apr 08 '10 at 10:01
  • 2
    Sorry, but the answer is completely bogus. The examples given in C FAQ are the examples of machines with non-zero *null-pointer values*. The question was about non-zero `NULL`, i.e. about a non-zero *null-pointer constant*. Null-pointer value and null-pointer constant are two completely different things. The answer mistakes one for another, which is a rather widespread mistake (a newbie mistake, should I add). – AnT stands with Russia Apr 08 '10 at 13:53
  • 2
    The question was about what `NULL` expands to. This answer has absolutely nothing to do with what `NULL` expands to. Yet, the autor of the question says "Exactly what I was looking for" (???). Apparently the author is confused (misunderstands the difference between NPC and NPV, as I said above). – AnT stands with Russia Apr 08 '10 at 13:57
  • Steve Jessop, read the first edition of K&R or read some older technical reports from Bell Labs. Go back far enough and there's no void or void*, but still an integral constant 0 in source code when converted to a pointer at compile time would have to convert to a null pointer no matter what its bit representation was. #define NULL ((char*)-1) would not have been acceptable. – Windows programmer Apr 09 '10 at 00:15
  • 1
    @Windows programmer: Are you sure K&R had the concept of type-specific null-pointer value, as opposed to simply converting integral `0` to pointer to address `0x0`? – AnT stands with Russia Jul 01 '13 at 04:13
  • What the FAQ says about the Cyber-180 is true. The internal representation of null pointers was not all 0 bits. But the C compiler I worked on certainly ensured that `NULL==0` was true. I don't recall exactly how the `NULL` macro was defined in the `.h` files, but it would have been either `0` or `((char*)0)`. (This was just before ANSI-C, so there was no void* at the time.) – Theodore Norvell Apr 23 '15 at 19:41
6

There was a time long ago when it was typed as ((void*)0) or some other machine-specific manner, where that machine didn't use the all-zero bit pattern.

Some platforms (certain CDC or Honeywell machines) had a different bit pattern for NULL (ie, not all zeros) although ISO/ANSI fixed that before C90 was ratified, by specifying that 0 was the correct NULL pointer in the source code, regardless of the underlying bit pattern. From C11 6.3.2.3 Pointers /4 (though, as mentioned, this wording goes all the way back to C90):

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
4

In C compilers, it can expand to '((void *)0)' (but does not have to do so). This does not work for C++ compilers.

See also the C FAQ which has a whole chapter on null pointers.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
4

In the GNU libio.h file:

#ifndef NULL
# if defined __GNUG__ && \
(__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))
#  define NULL (__null)
# else
#  if !defined(__cplusplus)
#   define NULL ((void*)0)
#  else
#   define NULL (0)
#  endif
# endif
#endif

Note the conditional compilation on __cplusplus. C++ can't use ((void*) 0) because of its stricter rules about pointer casting; the standard requires NULL to be 0. C allows other definitions of NULL.

dan04
  • 87,747
  • 23
  • 163
  • 198
3

C compilers usually use ((void *)0). The reason is passing NULL to functions with variable arguments (or now rare but still legal functions without prototype). When pointers are larger than int, 0 will only be promoted to int and will thus not read correctly as pointer.

C++ compilers can't use that definition because C++ does not permit implicit cast from void * (casting 0 to any pointer is special-cased). However C++11 introduced new keyword nullptr that is a null pointer constant of special nullptr_t type implicitly convertible to any pointer type, but not number. This solves both the variadic argument problem and the implicit cast and additionally more severe problems with overload selection (0 for obvious reason selects int overload over pointer one). It is legal to define these yourself for older compilers and some C++ compilers tried that in the past.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
1

NULL macro in C expands to implementation defined null-pointer constant. It can be anything (since it is implementation-defined), but in pointer context the effect is always the same as if it expanded to constant 0.

There has never been a time in standard C history when NULL expanded to something specifically not 0, unless you consider (void *) 0 as "not 0". But (void *) 0 for NULL is widely used to this day.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 4
    There has never been a time in _ISO_ C history ... early compilers had different bit patterns long before the 0 was codified as NULL regardless of the underlying bits. Although, given my advancing years, there's no way I could remember which ones they were :-) – paxdiablo Apr 08 '10 at 02:26
  • Chapter and verse please? The ISO C standard explicitly says that NULL can expand to whatever the implementation wants it to expand to. `4.1.5. Common Definitions [...] The macros are NULL which expands to an implementation-defined null pointer constant; and [...] `. This should not be confused with other text that says that the expression 0 converted to pointer type is always a valid way to get a null pointer constant. Which has nothing to do what what NULL expands to. – janks Apr 08 '10 at 02:46
  • @janks, I think @paxdiablo is saying that in ISO C, `0` in pointer contexts is the null pointer constant, but in pre-ISO (pre-ANSI) C, this wasn't necessarily true. Presumably in those variants one would write `NULL`, or whatever magic number the null pointer happened to be. – Alok Singhal Apr 08 '10 at 03:18
  • 1
    @Alok, yeah, that's exactly what his first paragraph said. But then he contradicted it by saying that "there has never been a time in C history when NULL expanded to something specifically not 0", which is rubbish. The standard doesn't require it to, and he would have to have used every implementation of C ever, without exceptions, to prove the negative. We once believed there were no black swans because we'd only seen white ones. – janks Apr 08 '10 at 03:36
  • @janks: elsewhere in the C standard, a "null pointer constant" is defined to be either an ICE with value 0, or else one of those cast to `(void*)`. That's not "whatever the compiler wants", and I think AndreyT is right to say that any such thing is "specifically 0". The standard forbids `#define NULL ((void*)0xFFFF)`, even if 0xFFFF converts to a null pointer on that implementation. I think it's probably wrong to ignore pre-standard C, since the question is clearly about ages past, but the standard does forbid black swans. – Steve Jessop Apr 08 '10 at 08:18
  • @SteveJessop: Can you cite the text from the standard that you feel forbids `#define NULL ((void*)0xFFFF)`? The "as-if" rule says that an implementation can do whatever it pleases so long as the observable side-effects of your program match those of the abstract machine. While it would be pretty stupid, I can't see any reason why the implementation cannot define NULL in a strange way, and then "fix-up" any context where NULL is used such that it respects the semantics of the abstract machine. – janks Apr 08 '10 at 09:40
  • @janks: In n1256, the text defining a null pointer constant is 6.3.2.3/3. The text which says NULL is a #define to a null pointer constant is 7.17/3. The program which would fail the as-if rule if NULL were #defined to `((void*)0xFFFF)` is one which stringifies NULL and prints it out, for example `#define SSTRING(A) #A #define STRING(A) SSTRING(A) int main() {const char *ptr = STRING(NULL); printf("%s\n", ptr); }`. Plus some linebreaks. – Steve Jessop Apr 08 '10 at 09:53
  • Yes, the stringize argument is clever, it's the only thing that I can see that could arguably forbid a strange definition of NULL. Unless perhaps the implementation could detect when you're trying to stringize NULL, and substitute some magic. But I personally don't believe that 4.1.5 combined with 3.2.2.3 are sufficient to rule out a strange definition of NULL. I read 4.1.5 as saying "ICE 0 and ICE 0 cast to void* must be legal forms of the null pointer constant", not that they are the only legal forms of the null pointer constant. I didn't write the standard though, so I could be wrong. – janks Apr 08 '10 at 10:08
  • I think when the standard says, " such-and-such is called *foo* ", it means to define "foo", not just to give examples of "foo". – Steve Jessop Apr 08 '10 at 10:22
  • 1
    What I meant to say is that since the moment `NULL` appeared as a specified part of the language, `0` was a valid way to define `NULL`. I.e. there never was a time `NULL` was *requred* to be defined as something non-zero. It could be defined to something platform specific (anything), but `0` was always required to work just as well. (Again: since the moment `NULL` was introduced. CRM C, for example, makes no mention of `NULL`.) – AnT stands with Russia Apr 08 '10 at 14:00
1

In modern C, void *pointer = 0; is meant to initialize "pointer" to not point at anything. It is platform-specific as to whether that is accomplished by setting the bits of "pointer" to all-zero.

In the past, this formal meaning of "0" in a pointer context was not established. It was necessary to set the pointer to the actual value that the platform treated as "doesn't point anywhere". As an example, a platform might choose some fixed address that never gets a page mapped to it. In this case, in an old compiler, the platform might have defined NULL as:

#define NULL ((void*)0xFFFFF000)

Of course, today, there's no reason not to define it as ((void*)0).

John Saunders
  • 160,644
  • 26
  • 247
  • 397
  • 'In the past, this formal meaning of "0" in a pointer context was not established.' -- right, where 'In the past' means 'Before the invention of the C language'. – Windows programmer Apr 09 '10 at 00:17
  • @Windows: no, that's not the case. It was not established as of K&R C, first edition. `void *ptr=0;` did not have a defined meaning. – John Saunders Apr 09 '10 at 01:03
  • 1
    To the best of my recollection that is correct, there was no "void" or "void *" as of K&R C, first edition. However, the formal meaning of "0" in a pointer context was established at the beginning of C in 1970. – Windows programmer Apr 09 '10 at 07:25
  • 1
    [K&R1](https://ia801303.us.archive.org/1/items/TheCProgrammingLanguageFirstEdition/The%20C%20Programming%20Language%20First%20Edition%20[UA-07].pdf) page 97: "C guarantees that no pointer that validly points at data will contain zero, so a return value of zero can be used to signal an abnormal event, in this case, no space. We write `NULL` instead of zero, however, to indicate more clearly that this is a special value for a pointer. In general, integers cannot meaningfully be assigned to pointers; zero is a special case." See also chapter 5.6 "Pointers are not Integers." – Quuxplusone Dec 04 '16 at 09:02