Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

compiler warning - cast from pointer to integer of different size

Tags:

c

generics

I'm trying to create a struct that can hold generic values. The code below works but I'm getting a compiler warning about the cast from pointer to integer. This is on a 64-bit system.

struct node
{
    void *key;
    void *value;
};

void insert(struct node *ht, void *key, void *value)
{
    ht->key = key;
    ht->value = value;
    return;
}

int main()
{
    struct node *t = (struct node *)malloc(sizeof(struct node));
    insert(t, (void *)3, (void *)5);
    printf("[%d]->[%d]\n", (int)t->key,(int)t->value);
    free(t);
    return 0;
}

I'm not even sure if this is the correct way to go about it. I sort of hacked it up. Please let me know if there is a proper way to do this.

like image 744
DavidL Avatar asked Feb 05 '13 15:02

DavidL


3 Answers

The compiler tries to warn you that you lose bits when casting from void * to int. It doesn't know that the void * is actually an int cast, so the lost bits are meaningless.

A double cast would solve this (int)(uintptr_t)t->key.
It first casts void * to uintptr_t (same size, no warning), then uintptr_t to int (number to number, no warning).
You need to include <stdint.h> to have the uintptr_t type (an integral type with the same size as a pointer).

like image 50
ugoren Avatar answered Sep 19 '22 22:09

ugoren


One thing that is useful to remember is that in a 64 bit system, a pointer is a 64 bit value [a memory address.]

int is only a 32-bit value, regardless of what architecture you're on. Anytime you try to assign a 64-bit value in to a 32-bit value without explicitly casting it the compiler will throw a warning [granted, it could still work but in general is not good practice.]

If you're not averse to using unsigned integers, it might be easier to use uint_64t or something like that, which will avoid the 64-bit to 32-bit assignment (uint_64t is an unsigned 64-bit int)

Hope that helps.

One thing you could do is:

int key = 3;
int value = 5;
insert(t, (void *) &key, (void *) &value);
printf("[%d]->[%d]\n", (int) *(t->key), (int) *(t->value));

However, be very very careful when doing things like this. It's impossible to absolutely know what the value being stored by that pointer is unless you can guarantee that you set the pointer and it's value/type is unchanged. Unless you add an enum field or something that stores the type of value stored at the pointer location-- but that kind of defeats the purpose.

like image 38
HodorTheCoder Avatar answered Sep 16 '22 22:09

HodorTheCoder


You've got lots of problems with the code you posted.

First:

printf("[%d]->[%d]\n", (int)t->key,(int)t->value);

When printing pointers, print pointers. Use %p. See here:

http://www.cplusplus.com/reference/cstdio/printf/

Second:

insert(t, (void *)3, (void *)5);

I'm not sure what you expected to happen, but you are storing the ADDRESS not the VALUE. (i.e. you are setting your key as a pointer to the contents of address 3, and the value as a pointer to the contents of address 5).

Third:

struct node *t = (struct node *)malloc(sizeof(struct node));

Here you have allocated the node, but you have not allocated anything to hold the contents. Your node just holds some pointers (ADDRESSES), it doesn't hold any VALUES. I'm not sure if this is what you want.

Finally, your compiler is right in warning you. There is a chance you are losing precision. Like @JonathanLeffler said, use uintptr_t.

like image 27
Josh Petitt Avatar answered Sep 16 '22 22:09

Josh Petitt