Specific pointer -> generic pointer -> boolean (i.e. implicit compare to 0) seems not to work as expected.
void main() { char* p; char* q; char xdata* xp = NULL; bit fail = FALSE; // This fails. if ( p = xp ) { fail = TRUE; } // This is ok. if ( ( q = xp ) == NULL ) { fail = TRUE; } }
It's only Keil C, and particular it's memory-specific pointers, that make it hard. NULL is not difficult. It is the 8051 architecture that throws a wrench into things. The 8051 is not a vonNeumann architecture and it is not linear. In the 8051, you have:
strcpy (char *d, char *s);
strcpy_ii (char idata *d, char idata *s); strcpy_ix (char idata *d, char xdata *s); strcpy_ic (char idata *d, char code *s); strcpy_xi (char xdata *d, char idata *s); strcpy_xx (char xdata *d, char xdata *s); strcpy_xc (char xdata *d, char code *s);
#include <REG52.H> #include <stdio.h> void main (void) { char idata *ip = NULL; char xdata *xp = NULL; char code *cp = NULL; char *gp = NULL; SCON = 0x50; /* SCON: mode 1, 8-bit UART, enable rcvr */ TMOD |= 0x20; /* TMOD: timer 1, mode 2, 8-bit reload */ TH1 = 221; /* TH1: reload value for 1200 baud @ 16MHz */ TR1 = 1; /* TR1: timer 1 run */ TI = 1; /* TI: set TI to send first char of UART */ if (ip == NULL) printf ("IP is NULL\n"); if (xp == NULL) printf ("XP is NULL\n"); if (cp == NULL) printf ("CP is NULL\n"); if (gp == NULL) printf ("GP is NULL\n"); if ((gp = ip) == NULL) printf ("(GP = IP) is NULL\n"); if ((gp = xp) == NULL) printf ("(GP = XP) is NULL\n"); if ((gp = cp) == NULL) printf ("(GP = CP) is NULL\n"); while (1); }
#define TRUE 1 #define FALSE 0 void main() { char* p; char* q; char xdata* xp = NULL; bit fail = FALSE; // This fails. if ( p = xp ) { fail = TRUE; } // This is ok. if ( ( q = xp ) == NULL ) { fail = TRUE; } }
It's always interesting to discuss whether an implementation of a C compiler conforms to the standard or not. Here is my 2 cents. The ISO C 1999 says: An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. 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. ... Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal. Let's see if C51 conforms to that. Forgive me if I make mistakes here, since I only did C166. It's easy to satisfy the first requirement: a null pointer can't point to an object in C program. Just make sure that the linker doesn't put anything to where the null pointers point. The second requirement:
if ( (void*)0 != (void xdata*)((void*)0) ) printf("Oops! This is not ISO C behaviour.");
My point is that it's probably OK to break standard conformance in some places if it's justified by performance/simplicity/etc considerations. After all, if one wants standard C behaviour, they can limit themselves to using generic pointers only. I guess that this is the nub of the matter. If you write standard C code, you will always be getting generic pointers and, hence, standard C behaviour. Memory specific keywords are Keil extensions and if you choose to use them it is necessary to understand how they work in some detail.
Now the question is which one of these is a pointer to offset 0 (in a particular memory space) and which is NULL? From the point of view of the C standard, I think it's pretty clear: they're all NULL, and should compare equal to any null pointer. Based on the definition of NULL (which is defined as ((void *) 0) ) the only candidate for NULL is a generic pointer to idata address 0x00. You're thinking of (generic) pointers as integers, it seems. But they aren't numbers. They're abstract thingies that behave somewhat, but not quite like numbers. Actually, the C standard does not define NULL as (void *)0, either. #define NULL 0 would be every bit as valid. My point would be that in a comparison of the type
(generic pointer == 0)
(generic pointer == NULL)
(void *)(xdata char *)0 == 0
Jon, Thanks so much for this posting. It really hit home regarding the NULL value problem even after I had read the section on generic pointers and conversion. The issue to me is that during the comparison I am looking at the memory-type, which I do not care about. Here is a suggestion that might work.
char * var; if ((unsigned short) var == (unsigned short) NULL) {}
This will strip out the memory types on both sides and then a valid comparison can be made. What do you think? I'd probably do something like this:
#define ISNULL(x) (((unsigned) (void *) (x)) == 0) void * p; // gerneric pointer . . . if (ISNULL(p)) { // NULL pointer detected }
It would be worth you taking a look here: http://www.keil.com/update/_docs/releasenotes/c51v709.htm as there have been a number of bug fixes throughout version 7.x releases relating to comparisons between generic/far pointers and NULL. Stefan