Reputation: 75
Im developing a library that deals with certain network connections in C, and my plan was for the init function to return a struct containing all relevant data for the connection that the caller will then pass to any of the library functions. I also want the contents of the struct to be accessible to the caller.
Here is my init (library) code
//here is the struct
typedef struct {
SSL_CTX* ctx;
SSL* ssl;
int socket;
int usingSSL;
} twitch_connection;
struct twitch_connection* twlibc_init(int usingSSL){ //returns a pointer to a struct that has to be freed
twitch_connection *temp;
temp = malloc(sizeof(twitch_connection)); //creates the address for the struct
struct sockaddr_in twitchaddr;
...
if(usingSSL == 0){
//not using SSL
temp->ctx = NULL;
temp->ssl = NULL;
}else {
//using SSL
temp->ctx = ctx; //how I assign values to the struct
temp->ssl = ssl;
}
temp->socket = twitchsock;
return temp; //returns the struct back to caller
}
here is the code from my demo
int main(){
twitch_connection* twlibc = twlibc_init(1);
printf("address of struct from main of callers code is: %p\n", twlibc);
}
However when I printf the addresses of the struct I get different results when printing from different areas in the code:
address of struct from inside init function: 0x56299befd260
address of struct from main of callers code is: 0xffffffff9befd260
and if I try to printf a member of the struct from the main function I get a segmentation fault.
Upvotes: 0
Views: 35
Reputation: 17628
twitch_connection* twlibc = twlibc_init(1);
Since twlibc_init
was not declared at this point, C assumes that the return value is an int, so the above is equivalent to twlibc = (twitch_connection*)(int)twlibc_init(1);
.
On a 64-bit platform with 32-bit int
type this will truncate 0x56299befd260
to a 32-bit integer 0x9befd260
then sign-extend it to a 64-bit pointer 0xffffffff9befd260
.
The solution is to declare the function struct twitch_connection* twlibc_init(int usingSSL);
in the main file or, even better, move the declarations to a common header included in both places.
[ EDIT ] The sign-extension when casting an int
to a pointer is implementation dependent and not guaranteed by the C standard, so another compiler might have printed 0x9befd260
instead or, indeed, anything. From the cppreference C cast operator page:
Any integer can be cast to any pointer type. Except for the null pointer constants such as NULL (which doesn't need a cast), the result is implementation-defined, may not be correctly aligned, may not point to an object of the referenced type, and may be a trap representation.
Upvotes: 1