[vox-tech] C - passing chars and pointer to chars

Peter Jay Salzman p at dirac.org
Fri Jun 2 08:31:43 PDT 2006


I've been learning Java's JNI API, and came across something about C that I
never knew.

There are 3 types of char:

   char
   signed char
   unsigned char

My understanding of the standard says that char can either be of type
"signed char" or "unsigned char"; it's implementation specific.  By
assigning "c = 255" I found that on my own platform (GNU/x86) a char is
implemented as a "signed char".  I think I remember reading that on Apple
platforms, it's implemented as an "unsigned char".

According to the gcc info page:

     Ideally, a portable program should always use `signed char' or
     `unsigned char' when it depends on the signedness of an object.
     But many programs have been written to use plain `char' and expect
     it to be signed, or expect it to be unsigned, depending on the
     machines they were written for.  This option, and its inverse, let
     you make such a program work with the opposite default.

*    The type `char' is always a distinct type from each of `signed
*    char' or `unsigned char', even though its behavior is always just
*    like one of those two.

char is a *distinct type* from "signed char" or "unsigned char".  That
surprised me.  So I did some experimentation and here's what I found.

Apparently, there's no problem assigning the different chars to each other.
The compiler does the automatic conversion:

   char          a = 0;
   signed char   b = 0;
   unsigned char c = 0;

   a = b; a = c;    // fine.
   b = a; b = c;    // fine.
   c = a; c = b;    // fine.

You can even pass the different types of char to functions that take
other types of char:

   void takesAChar( char x, signed char y, unsigned char z );

   takesAChar(a, b, c); takesAChar(a, c, b);  // fine.
   takesAChar(b, a, c); takesAChar(b, c, a);  // fine.
   takesAChar(c, b, a); takesAChar(c, a, b);  // fine.

What the compiler complains about is passing *pointers* to different
types of char:

   void takesACharPtr( char *x, signed char *y, unsigned char *z );

   takesACharPtr(&a, &b, &c); takesACharPtr(&a, &c, &b);  // warnings.
   takesACharPtr(&b, &a, &c); takesACharPtr(&b, &c, &a);  // warnings.
   takesACharPtr(&c, &a, &b); takesACharPtr(&c, &b, &a);  // warnings.

The warning is:

   pointer targets in passing argument foo of bar differ in signedness.

I'm trying to understand this.  I'm fairly sure the standard says that all 3
types of char must have the same width.  For pointer operations like:

   char s[] = "hello";
   unsigned char *cptr = s;
   ++cptr;
   putc( *cptr, stdout );

will correctly print "e" because "char" and "unsigned char" have the same
width, and when we add one to cptr, it points to the correct location in
memory.

What I'm getting at is this.  Because all the chars have the same width, it
doesn't matter WHAT kind of pointer you pass in to a function: char, signed
char, or unsigned char.  Pointer arithmetic just works, and it works because
they all have the same width.

On the other hand, the data is what gets mangled if you don't use the
correct type:

   char c = 255;
   printf("%d", c);

prints, as expected, -1.  Not 255.

So it seems to me that if the compiler complains about anything, it should
complain about passing a different type of char, not a different type of
char *.

Why does gcc 4 complain about passing different "char *" and not "char"?

And is this because of the standard or is it gcc specific?

Thanks!
Pete


More information about the vox-tech mailing list