5

This issue bothered me for a while. I never saw a different definition of NULL, it's always

#define NULL  ((void *) 0)

is there any architecture where NULL is defined diferently, and if so, why the compiler don't declare this for us ?

stdcall
  • 27,613
  • 18
  • 81
  • 125
  • You can use `0` instead of `NULL`. – pmg Apr 20 '13 at 19:41
  • NULL is defined in `stddef.h`, you don't have to define it yourself at all. – Mat Apr 20 '13 at 19:42
  • @pmg 0 is not portable if the answer to my first question is true – stdcall Apr 20 '13 at 19:47
  • I remember years ago my CS professor explained this to us. Something about in very low-level systems programming possibly on very low-level devices, writing to address 0 is actually valid.... I like WhozCraig's answer in the comment even better. – selbie Apr 20 '13 at 19:47
  • Pretty good link: http://stackoverflow.com/questions/2597142/when-was-the-null-macro-not-0 – Valeri Atamaniouk Apr 20 '13 at 19:50
  • 7
    Even if an architecture has a null pointer that isn't all bits zero, replacing `NULL` with `0` must provide a null pointer through some compiler magic. **`0` and `NULL` are interchangeable in C**. See [6.3.2.3/1 in C99 Standard](http://port70.net/~nsz/c/c99/n1256.html#6.3.2.3). – pmg Apr 20 '13 at 19:52
  • C++ version of this question: http://stackoverflow.com/questions/924664/why-is-null-undeclared – patrickmdnet Apr 20 '13 at 19:55
  • some other answers here: http://c-faq.com/null/ – patrickmdnet Apr 20 '13 at 19:57
  • @pmg. Not if NULL is (void *)0 – fizzer Apr 20 '13 at 20:23
  • 2
    What is the advantage of having the compiler do *anything* that can be done in a single line of library code? – Bo Persson Apr 20 '13 at 20:52
  • @fizzer: are you perhaps thinking about C++? I believe C and C++ differ in the way they deal with `void*`. – pmg Apr 20 '13 at 21:41
  • The question seems to assume that the internal representation of a null pointer is 0, which is not true. So I'm not sure what's being asked. From the C compiler's point of view, a 0 in a pointer context is always a null pointer, even if the null pointer is represented by some crazy non-zero bit pattern. So what's really being asked? – Adrian McCarthy Apr 20 '13 at 21:52
  • 5
    @pmg. 'interchangeable' overstates the case. printf("%p", NULL) is valid if NULL is (void *)0, but a 0 here is undefined. – fizzer Apr 20 '13 at 22:04
  • @fizzer: nice counter-example. I agree I was overstating the case. Thank you. – pmg Apr 20 '13 at 22:20

4 Answers4

4

WhozCraig wrote these comments to a now-deleted answer, but it could be promoted to a full answer (and that's what I've done here). He notes:

Interesting note: AS/400 is a very unique platform where any non-valid pointer is considered equivalent to NULL. The mechanics which they employ to do this are simply amazing. "Valid" in this sense is any 128-bit pointer (the platform uses a 128bit linear address space for everything) containing a "value" obtained by a known-trusted instruction set. Hard as it is to believe, int *p = (int *)1; if (p) { printf("foo"); } will not print "foo" on that platform. The value assigned to p is not source-trusted, and thus, considered "invalid" and thereby equivalent to NULL.

It's frankly startling how it works. Each 16-byte paragraph in the mapped virtual address space of a process has a corresponding "bit" in a process-wide bitmap. All pointers must reside on one of these paragraph boundaries. If the bit is "lit", the corresponding pointer was stored from a trusted source, otherwise it is invalid and equivalent to NULL. Calls to malloc, pointer math, etc, are all scrutinized in determining whether that bit gets lit or not. And as you can imagine, putting pointers in structures brings a whole new world of hurt on the idea of structure packing.


This is marked community-wiki (it's not my answer — I shouldn't get the credit) but it can be deleted if WhozCraig writes his own answer.

What this shows is that there are real platforms with interesting pointer properties.

There have been platforms where #define NULL ((void *)0) is not the usual definition; on some platforms it can be just 0, on others, 0L or 0ULL or other appropriate values as long as the compiler understands it. C++ does not like ((void *)0) as a definition; systems where the headers interwork with C++ may well not use the void pointer version.

I learned C on a machine where the representation for the char * address for a given memory location was different from the int * address for the same memory location. This was in the days before void *, but it meant that you had to have malloc() properly declared (char *malloc(); — no prototypes either), and you had to explicitly cast the return value to the correct type or you got core dumps. Be grateful for the C standard (though the machine in question, an ICL Perq — badged hardware from Three Rivers — was largely superseded by the time the standard was defined).

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 2
    I've no problem with this staying as is. Its been nine years since I wrote code for OS/400, and for all i know it has since changed since then, but it was such an incredibly unique perspective on how they accomplished this I hopefully committed enough of it to memory to still be accurate enough for prime-time. Only a "DLL" function invoke was near-as-entertaining, as it is literally scheduled as a system job. That platform is just *amazing*. – WhozCraig Apr 20 '13 at 20:20
4

C 2011 Standard, online draft

6.3.2.3 Pointers
...
3 An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.66) If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
66) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.19.

The macro NULL is always defined as a zero-valued constant expression; it can be a naked 0, or 0 cast to void *, or some other integral expression that evaluates to 0. As far as your source code is concerned, NULL will always evaluate to 0.

Once the code has been translated, any occurrence of the null pointer constant (0, NULL, etc.) will be replaced with whatever the underlying architecture uses for a null pointer, which may or may not be 0-valued.

John Bode
  • 119,563
  • 19
  • 122
  • 198
1

In the dark ages before ANSI-C the old K&R C had many different implementations on hardware that would be considered bizarre today. This was before the days of VM when machines were very "real". Addresses of zero were not only just fine on these machines, an address of zero could be popular... I think it was CDC that sometimes stored the system constant of zero at zero (and did strange things happen if this was set non-zero).

 if ( NULL != ptr )     /* like this */
 if ( ptr )             /* never like this */

The trick was finding address you could safely use to indicate "nothing" as storing things at the end of memory was also popular, which ruled out 0xFFFF on some architectures. And these architectures tended to use word addresses rather than byte addresses.

Gilbert
  • 3,740
  • 17
  • 19
0

I don't know the answer to this but I'm making a guess. In C you usually do a lot of mallocs, and consequently many tests for returned pointers. Since malloc returns void *, and especially (void *)0 upon failure, NULL is a natrual thing to define in order to test malloc success. Since this is so essential, other library functions use NULL (or (void *)0) too, like fopen. Actually, everything that returns a pointer.

Hence here is no reason the define this at the language level - it's just a special pointer value that can be returned by so many functions.

Israel Unterman
  • 13,158
  • 4
  • 28
  • 35