'char **' <-> 'unsigned char **' and aliasing/punning in C / GNU C

Pedro Alves palves@redhat.com
Fri Mar 8 20:28:00 GMT 2013


On 03/08/2013 07:36 PM, Ian Lance Taylor wrote:
> On 3/8/13, Pedro Alves <palves@redhat.com> wrote:
>> I'm wondering if the following is valid C, and/or valid GNU C.
>> Something like:
>>
>> /* Returns a new malloced buffer in *OUTP.  */
>> void target_read_memory (unsigned long addr, int len,
>>                          unsigned char **outp);
>>
>> void bar ()
>> {
>>   char *str;
>>
>>   ...
>>   target_read_memory (addr, len, (unsigned char **) &str);
>>                                  ^^^^^^^^^^^^^^^^^^^^^^^
>>
>>   // follows uses of str as a C string
>>   strlen (str); sscanf (str, ...);
>> }
>>
>> chars are involved, but the cast is from a pointer to pointer,
>> to another pointer to pointer, so I end up confused whether
>> the char-aliases-everything rule kicks in around "str".
>>
>> gcc doesn't complain even with -fstrict-aliasing -O3, and I can imagine
>> that if this didn't work, the world would break, but, from a language
>> perspective, is that actually valid?  If not, does gcc support this
>> as a GNU C extension?
> 
> This question would be more appropriate on gcc-help than on gcc-patches.

In fact, it was indeed sent to gcc-help@, but my mailer autocompleted
literal "GCC Patches" in the free text part of To: in the previous
message and I hadn't noticed.  :-)  Fixed it now.

> I'm not sure, but I think you are asking whether a valid C program can
> cast from char ** to unsigned char **, store a value into the unsigned
> char **, and then use that value as a char*.  

Yes.  Basically,

unsigned char uc;
char *str = 0;
unsigned char *m = &uc;
unsigned char **buf = (unsigned char **) &str;
*buf = m;
assert (str == m);

As in, it looked like since 'pointer to pointer to char'
and 'pointer to pointer to unsigned char' are different
types, and neither is 'pointer to char', then the cast
and writing through 'buf' would be a strict alias violation.

> The answer to that
> question is yes, because qualifiers are ignored when considering
> aliasing.  That is, it is safe to cast from T* to unsigned T*.

Ah, okay.  You got me a bit confused because signed/unsigned
aren't called qualifiers though; those would be const/volatile,
I think.

Related, then I assume that this is valid too:

snippet 2:

void
parse (const char **strp)
{
  (*strp)++;
}

void bar ()
{
  char c[2];
  char *ptr = c;
  parse ((const char **) &ptr);
  assert (ptr == &c[1]);
}

> The fact that char * aliases any other pointer does not arise here,
> which is good because the cast from char ** to unsigned char ** does
> not involve a char * pointer on either side.

Thanks a lot,
-- 
Pedro Alves



More information about the Gcc-help mailing list