Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a char variable accept Pointer(NULL) as its value?

I understand that a char variable can accept a null character(1 byte) i.e; \0 as its value but, I don't understand how a char variable in my application below accepts a pointer(4 bytes) as its value and still works properly?

#include<stdio.h>
int main()
{
    char p[10]="Its C";
    printf("%s\n",p);
    p[3]='\0';                // assigning null character 
    printf("%s\n",p);
    p[2]=NULL;               //  assigning null pointer to a char variable
    printf("%s\n",p); 
    p[1]=(void *)0;          //  assigning null pointer to a char variable
    printf("%s\n",p);
    return 0;
}

Note: GCC Compiler (32 Bit Linux Platform).

like image 961
Neeraj Avatar asked Apr 11 '13 16:04

Neeraj


2 Answers

The NULL macro is required to expand to "an implementation-defined null pointer constant".

A null pointer constant is defined as "An integer constant expression with the value 0, or such an expression cast to type void *". Counterintuitively, this definition does not require the expansion of NULL to be an expression of pointer type. A common implementation is:

#define NULL 0

A null pointer constant, when used in a context that requires a pointer, may be implicitly converted to a pointer value; the result is a null pointer. It may also be explicitly converted using a cast, such as (int*)NULL.

But there's no requirement that an expression that qualifies as a null pointer constant may only be used in such a context. Which means that if the implementation chooses to define NULL as above, then this:

char c = NULL; // legal but ugly

is legal and initializes c to the null character.

Such an initialization is non-portable (since NULL may also expand to ((void*)0) and misleading, so it should be avoided, but a compiler is likely to let it through without warning; NULL is expanded to 0 by the preprocessing phase of the compiler, and later phases see it as char c = 0;, which is legal and innocuous -- though personally I'd prefer char c = '\0';.

I just tried your example on my own 32-bit Ubuntu system, with gcc 4.7. With no options specified, the compiler warned about both p[2]=NULL; and p[1]=(void *)0;:

c.c:8:9: warning: assignment makes integer from pointer without a cast [enabled by default]
c.c:10:9: warning: assignment makes integer from pointer without a cast [enabled by default]

The second warning is to be expected from any C compiler; the first indicates that NULL is actually defined as ((void*)0) (running the code through gcc -E confirms this).

The compiler didn't simply "accept" these assignments; it warned you about them. The C language standard merely requires a "diagnostic" for any violation of the language rules, even a syntax error; that diagnostic may legally be a non-fatal warning message. You can make gcc behave more strictly with -std=c89 -pedantic-errors; replace c89 by c99 or c11 to enforce rules from later versions of the standard. (EDIT: I see from comments that you're using a web interface to the compiler that hides warnings; see my comment on your question for a workaround. Warnings are important.)

If you post C code that produces compiler warnings please show us the warnings and pay close attention to them yourself. They often indicate serious problems, even illegalities, in your program.

A language-lawyer quibble: it's not even clear that this:

char c = (void*)0;

specifies a conversion from void* to char. My own view is that, since it violates a constraint, it has no defined semantics. Most compilers that don't reject it will treat it as if it were a void*-to-char conversion, and it's also been argued that this is the required behavior. But you can avoid such questions if you simply pay attention to compiler warnings and/or don't write code like that in the first place.

(The rules are a bit different for C++, but you're asking about C so I won't get into that.)

like image 89
Keith Thompson Avatar answered Oct 04 '22 00:10

Keith Thompson


NULL is a macro and for almost platform is defined in this way

#ifndef __cplusplus
#define NULL ((void *)0)
#else   /* C++ */
#define NULL 0
#endif  /* C++ */

(from stddef.h from my Ubuntu)

and when you write

p[2]=NULL;

It's the same

p[2]=(void *)0; //for c
p[2]=0; //for c++

It's the same

p[2] = 0; // the 0 is casted to char 0 for C --> '\0'
like image 22
MOHAMED Avatar answered Oct 04 '22 01:10

MOHAMED