1
votes

In this answer, the author discussed how it was possible to cast pointers in C. I wanted to try this out and constructed this code:

#include <stdio.h>

int main(void) {
    char *c;
    *c = 10;
    int i = *(int*)(c);
    printf("%d", i);
    return 1;
}

This compiles (with a warning) and when I execute the binary it just outputs bus error: 10. I understand that a char is a smaller size than an int. I also understand from this post that I should expect this error. But I'd really appreciate if someone could clarify on what is going on here. In addition, I'd like to know if there is a correct way to cast the pointers and dereference the int pointer to get 10 (in this example). Thanks!

EDIT: To clarify my intent, if you are worried, I'm just trying to come up with a "working" example of pointer casting. This is just to show that this is allowed and might work in C.

5

5 Answers

3
votes

c is uninitialized when you dereference it. That's undefined behaviour.

Likewise, even if c were initialized, your typecast of it to int * and then a dereference would get some number of extra bytes from memory, which is also undefined behaviour.

A working (safe) example that illustrates what you're trying:

int main(void)
{
    int i = 10;
    int *p = &i;
    char c = *(char *)p;
    printf("%d\n", c);
    return 0;   
}

This program will print 10 on a little-endian machine and 0 on a big-endian machine.

2
votes

These lines of code are problematic. You are writing through a pointer that is uninitialized.

char *c;
*c = 10;

Change to something like this:

char * c = malloc (sizeof (char));

Then, the following line is invalid logic, and the compiler should at least warn you about this:

int i = *(int*)(c);

You are reading an int (probably 4 or 8 bytes) from a pointer that only has one byte of storage (sizeof (char)). You can't read an int worth of bytes from a char memory slot.

1
votes

First of all your program has undefined behaviour because pointer c was not initialized.

As for the question then you may write simply

int i = *c;
printf("%d", i);

Integral types with rankes less than the rank of type int are promoted to type int in expressions.

1
votes

I understand that a char is a smaller size than an int. I also understand from this post that I should expect this error. But I'd really appreciate if someone could clarify on what is going on here

Some architectures like SPARC and some MIPS requires strict alignment. Thus if you want to read or write for example a word, it has to be aligned on 4 bytes, e.g. its address is multiple of 4 or the CPU will raise an exception. Other architectures like x86 can handle unaligned access, but with performance cost.

1
votes

Let's take your code, find all places where things go boom as well as the reason why, and do the minimum to fix them:

#include <stdio.h>

int main(void) {
    char *c;
    *c = 10;

The preceding line is Undefined Behavior (UB), because c does not point to at least one char-object. So, insert these two lines directly before:

    char x;
    c = &x;

Lets move on after that fix:

    int i = *(int*)(c);

Now this line is bad too.
Let's make our life complicated by assuming you didn't mean the more reasonable implicit widening conversion; int i = c;:

If the implementation defines _Alignof(int) != 1, the cast invokes UB because x is potentially mis-aligned.
If the implementation defines sizeof(int) != 1, the dereferencing invokes UB, because we refer to memory which is not there.

Let's fix both possible issues by changing the lines defining x and assigning its address to c to this:

    _Alignas(in) char x[sizeof(int)];
    c = x;

Now, reading the dereferenced pointer causes UB, because we treat some memory as if it stored an object of type int, which is not true unless we copied one there from a valid int variable - treating both as buffers of characters - or we last stored an int there.

So, add a store before the read:

    *(int*)c = 0;

Moving on...

    printf("%d", i);
    return 1;
}

To recap, the changed program:

#include <stdio.h>

int main(void) {
    char *c;
    _Alignas(in) char x[sizeof(int)];
    c = x;
    *c = 10;
    *(int*)c = 0;
    int i = *(int*)(c);
    printf("%d", i);
    return 1;
}

(Used the C11 standard for my fixes.)