1

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 Answers 5

3

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Could you give an example of that second case? I.e. c is initialized? I'm a little confused there.
This example goes from int to char instead the other way around. That is a significant change, making different rules apply.
@Deduplicator, sure - a change that makes the code safe and work.
I never argued your example was not safe, just that it's demonstrating the reverse to what the question asked about. Anyway, your rationale for char to int being UB is ... somewhat lacking.
2

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.

8 Comments

char is always size 1.
@CarlNorum fair 'buf. Edited.
The statement *c = 10 is not storing int to a char. It is storing char. Valid values for an 8 bit signed char is -127 to 127. Unsigned char is 0 to 65535.
@alvits Edited. Still an invalid line since you are asking the machine to interpret an int worth of bytes from a single byte in memory.
@alvits, unsigned char is 0 to 255
|
1

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 Comment

You may want to mention char to int promotion.
1

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 Comment

"Bus error" usually indicates an alignment problem, so this is the most likely explanation
1

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.)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.