One of the most powerful but potentially dangerous features of the C language is its use of indirect addressing through pointers. Bugs introduced by misusing pointers can be difficult to detect and isolate because the error often corrupts memory unpredictably.
Be sure you do not make nonportable assumptions when casting pointers to different types.
/* Nonportable coercion */
char c[4];
long *lp;
lp = (long *)c;
*lp = 0x12345678L;
This code is nonportable because using a cast to change an array of char to a pointer of type long assumes a particular byte-ordering scheme. This is discussed in greater detail in “Byte Order in a Word”.
A pointer can be assigned (or cast) to any integer type large enough to hold it, but the size of the integer type depends on the machine and the implementation. (In fact, it can even depend on the memory model.) Therefore, you cannot assume that a pointer is the same size as an integer; that is:
sizeof( char * ) == sizeof( int )
To determine the size of any unmodified data pointer, use
sizeof( void * )
This expression returns the size of a generic data pointer.
Code that assumes that pointer subtraction yields an int value is nonportable. Pointer subtraction yields a result of type ptrdiff_t (defined in STDDEF.H). Portable code must always use variables of type ptrdiff_t for storing the result of pointer subtraction.
In most implementations, NULL is defined as 0. In Microsoft C, it is defined as ((void *)0). Because code pointers and data pointers are often different sizes, using 0 for the null pointer for both can lead to nonportability. The difference in size between code pointers and data pointers will cause problems for functions that expect pointer arguments longer than an int. To avoid these problems, use the null pointer, as defined in the include file STDDEF.H; use prototypes; or explicitly cast NULL to the correct data type. Here is a portable way to use the null pointer:
/* Portable use of the null pointer */
main()
{
func1( (char *)NULL );
func2( (void *(*)())NULL );
}
void func1( char * c )
{
}
void func2( void *(* func)() )
{
}
The invocations of func1 and func2 explicitly cast NULL to the correct size. In the case of func1, NULL is cast to type char *; in the case of func2, it is cast to a pointer to a function that returns type void.
Subtraction of pointers to huge arrays that have more than 32,767 elements may yield a long result. The __huge keyword is implementation-defined by Microsoft C and is not portable. Here is how to subtract pointers to huge arrays:
char __huge *a;
char __huge *b;
long d;
.
.
.
d = (long)( a - b );
In Microsoft C, the memory model selected and the special keywords __near, __far, and __huge can change the size of a pointer. The Microsoft memory models and extended keywords are nonportable, but you should be aware of their effects.
Sizes of generic pointers and default pointer sizes are shown in Tables 12.2 and 12.3, respectively.
Table 12.2 Size of Generic Pointers
Declaration | Name | Size |
void __near * | Generic near pointer | 16 bits |
void __far * | Generic far pointer | 32 bits |
void __huge * | Generic huge pointer | 32 bits |
Table 12.3 Default Pointer Sizes in 16-Bit Programs
Memory Model | Code Pointer Size | Data Pointer Size |
Tiny | 16 bits | 16 bits |
Small | 16 bits | 16 bits |
Medium | 32 bits | 16 bits |
Compact | 16 bits | 32 bits |
Large | 32 bits | 32 bits |
Huge | 32 bits | 32 bits |