views:

134

answers:

2
+10  Q: 

Unsafe conversion

Is the following conversion safe?

int b[10][10];
char *x;
int a[]={0,1,2,3,4,5,6,7,8,9};

for(int i=0;i<10;i++)
  for(int j=0;j<10;j++)
    b[i][j]=a[i];

for(x=(char *)&b[0];x<=(char *)&b[9][9];x+=sizeof(a+1)) // Problem lies here!
    printf("%d\n",*x);

I don't think the above conversion in the for loop is safe (I think it is platform dependent). Please correct me if I am wrong. I am surprised because the code compiles without giving any warnings even when compiled using the -Wall -pedantic options in gcc.

+7  A: 

This whole thing has a chance of being legal for one and only one reason: the 2D int array object is reinterpreted as an array of char objects. While in general case memory reinterpretation leads to undefined behavior, the language specification explicitly allows "array of [signed/unsigned] char" reinterpretations for objects of any type.

However, one formal safety problem is still there. The language does not guarantee that any bit pattern is a valid value of char type. Your attempt to read reinterpreted memory through char type can theoretically cause undefined behavior if it encounters trap representation for char. In order to be safe here you have to use the unsigned char type, which is the only type that has no trap representations. Of course, a platform with a trapping char can be safely called "exotic".

Meanwhile, your sizeof(a + 1) doesn't seem to make any sense. a + 1 is an expression of int * type. Why you'd want to use the pointer size to increment your x value in this case is not clear to me. What were you trying to achieve?

As for the absence of warnings... I wouldn't expect the compiler to issue any warnings here. GCC often warns about type-punning (aka memory reinterpretation), but since char reinterpretations are explicitly allowed (as I said above), there's no warning here. Moreover, explicit casts usually tend to suppress any warnings, since they are a way of telling the compiler that you do actually want to do something regardless of how wrong and/or dangerous it might be.

AndreyT
@Andrey T: _a + 1 is an expression of int * type_ And that is what I want. It is a puzzle from an online coding competition.
Prasoon Saurav
@Prasoon: By the way your cycle looks, I'd expect one to use `sizeof(int)` here, like `sizeof(a[1])` or `sizeof(*(a + 1))` or `sizeof(*a)`. As for `sizeof(a + 1)`, again, I don't understand what is the point of this. It makes no sense to me.
AndreyT
I have assumed the sizeof(int) and sizeof(int *) to be the same on that particular platform. Yes I think sizeof(*a) looks more correct.
Prasoon Saurav
Yeah, why write `sizeof(a+1)` instead of `sizeof(int *)`? On my macbook, it prints each number 5 times, because `sizeof(int *)` is 2 times `sizeof(int)`.
Alok
Yeah that was a "big" mistake I was making. Thanks for suggesting the necessary corrections :)
Prasoon Saurav
The language my not guarantee that any bit pattern is a valid `char` but it's not worth loosing sleep over. There are no CPU architectures in common use where a char might trap, and no forseeable future CPU that might want to change that. This is a case where the language allows something that no-one wants to do.
John Knoeller
This is what I meant by the "exotic" remark in my answer.
AndreyT
A: 

a cast of any pointer type to char* is explicitly allowed by the C language. so most of this is fine.

for(x=(char *)&b[0]; x <= (char *)&b[9][9]; x += sizeof(a+1))  

The first part is fine x = (char*)&b[0]; establishes a char pointer to the beginning of the array. The test is also fine x <= (char *)&b[9][9] will be true as long as x points inside the array.

x += sizeof(a+1) is the iffy part. On most 32 bit CPU architectures sizeof(int*) just happens to be the same as sizeof(int), so this code will probably work, but only by accident.

I'm sure what was intended was x += sizeof(a[0]) or x += sizeof(b[0]), but since the code actually did what was intended, no-one noticed the bug.

John Knoeller