C++ pointer conversion

Saijin

Member
Aug 30, 2005
25
0
0
I am reading data from a file and I need to use this data as input to a function api call. The data is read in as a char* pointer, but the function api call takes int* only. So I need to convert the char* pointer to a int* pointer and still make sure the underlying data stays the same so that I can pass this data to the function.

Since the size of an int is 4 bytes while a char is 1 byte, will this work properly if I do something like going through the char* array and fitting 4 chars into a single int location, such as:

int *temp = new int[(size of the char*)/4];
byteIndex=0; // this is the location of where to put the char in the int
intArrayIndex // this is the index to the int pointer
for(i from char[0] to char[max) {
// put char(I) into int[intArrayIndex]'s byteIndex
int temp = char(I);
int shift = byteIndex*4;
int[intArrayIndex] = int[intArrayIndex] | (temp<<shift);

// reset byteIndex back to 0 when it reaches the last index
if(byteIndex==3)
byteIndex = 0;

//increment the intArrayIndex every 4th char
if(i%4==0)
intArrayIndex++;
}


Let me know if this doesnt make sense, thanks.
 

Markbnj

Elite Member <br>Moderator Emeritus
Moderator
Sep 16, 2005
15,682
14
81
www.markbetz.net
Um, I don't know about that. First off, char is often 16 bits on current systems, depending on compilation settings. An int is probably 32. Second, that memcpy will move, say, 32 bits from a place where presumably there are only 16 known, good bits. Third, it doesn't address the main issue of a data conversion like this, which is: does the pointer really address an int?

If the char* points to a character representation of an integer, then it is probably a null terminated array. You need to convert that character array into an integer, and then pass a pointer to the int.

int a = atoi(char_pointer);
api_call(&a);

If it actually points to a scalar value that can be interpreted as an integer then all you need to do is cast it.

int *pi = (int *)char_pointer;

But of course this is the very essence of unsafe, and you should make sure the interpretation of the data is correct.

Not sure what I'm missing, but I don't see where this calls for a memcpy.
 

xtknight

Elite Member
Oct 15, 2004
12,974
0
71
You said you're reading data from a file. But ASCII '9' is not actually 9 (it is in fact 57). Are you sure this is what you want to do? [Markbnj accounted for this by using the atoi, ascii to integer, function]

Would you like to pass a 9 or a 57 from within the data pointed to by int* ?

Data pointed to by a char* is typically ended by a NULL character (or two if you're using 16-bit chars). That's not the case with ints (NULL==0, and it's not odd for an int to be 0), so you will need to know how long to traverse the data pointed to by the int*. Each time, you will need to advance the memory location by sizeof(int) but you need to know how many times to do this (in other words, the total number of consecutive ints you have at that memory location).

Keep in mind that char* is the same size as int*, because they are both pointers. Pointers are on a 32-bit system are 32-bit, and with 64 they are 64. I hope these are some helpful...pointers for you :)
 

Saijin

Member
Aug 30, 2005
25
0
0
I believe what I want is to pass the actual value (scalar value)and not the ASCII value (in your example I'd want the 57). Let me clarify a bit on what I'm trying to do, I have a binary file that I want to read into my software, and I want to read in the data from this binary file and pass it to a function that will eventually transmit this data over some type of network, for another software program to use. The important thing is that is binary file will be a file that is called to be executed, and so I want to preserve the data exactly as it is.

I have a function that will read in the file using standard C++ I/O. The code looks something like:

//...
ifstream file (filename, ios::in|ios::binary|ios::ate);
if(file.isOpen()) {
char *block;
size = (int)file.tellg();
printf("size: %i\n", size);

block = new char [size];
file.seekg (0, ios::beg);
file.read (block, size);
file.close();
}
///...

The file.read() method seems to only return a char*, that is why I'm trying to convert it into a int* for my other function call to be able to accept. I dont want any changes to the underlying data nor do I care what it is, I just want to convert it to a int* for the other function to use.

So, from what Markbnj said, it seems that I can just cast it like:

int *pi = (int *)char_pointer;

Will that be safe enough to do?

Edit: And thanks for all the helpful pointers so far. =)
 

Markbnj

Elite Member <br>Moderator Emeritus
Moderator
Sep 16, 2005
15,682
14
81
www.markbetz.net
Yes, read() expects an array of bytes so you can cast that pointer to int* as long as you understand that it's just an array of bytes.
 

degibson

Golden Member
Mar 21, 2008
1,389
0
0
Originally posted by: Saijin

int *pi = (int *)char_pointer;

This is safe on architectures that support unaligned memory operations (e.g. x86).
 

Saijin

Member
Aug 30, 2005
25
0
0
Markbnj was also saying that it could be unsafe, so what about on other systems, what is the risk of that cast? And how do I find out or where I can look to learn more?
 

Markbnj

Elite Member <br>Moderator Emeritus
Moderator
Sep 16, 2005
15,682
14
81
www.markbetz.net
Originally posted by: Saijin
Markbnj was also saying that it could be unsafe, so what about on other systems, what is the risk of that cast? And how do I find out or where I can look to learn more?

Yes, but degibson is also right. I was thinking of type-safety at the level of program semantics and correctness, not whether it will cause a runtime exception. Direct casts of one pointer type to another turn off the compiler's strong type checking and put the responsibility for guaranteeing type compatibility in the hands of the programmer. I maintain and correct one body of very old code that has been worked on by hundreds of people, so this kind of thing gets to be a big issue. We run into uncommented casts all the time, and then have the responsibility of going down to the data to verify the column type, tracing it up and verifying that yes, it actually can be represented as in the cast pointer. We then enter the comment that the lazy shit who originally cast it should have put in back in 1990. :)

There are no safe casts, in my view, but there are necessary ones, as in your case when you bump up against an API you need to use. The safest way to deal with it, again in my view, is to wrap the API in a class of your own that performs the cast internally, so it occurs in exactly one well-documented place in the code.

 
Sep 29, 2004
18,656
67
91
Originally posted by: Markbnj
Originally posted by: Saijin
Markbnj was also saying that it could be unsafe, so what about on other systems, what is the risk of that cast? And how do I find out or where I can look to learn more?

Yes, but degibson is also right. I was thinking of type-safety at the level of program semantics and correctness, not whether it will cause a runtime exception. Direct casts of one pointer type to another turn off the compiler's strong type checking and put the responsibility for guaranteeing type compatibility in the hands of the programmer. I maintain and correct one body of very old code that has been worked on by hundreds of people, so this kind of thing gets to be a big issue. We run into uncommented casts all the time, and then have the responsibility of going down to the data to verify the column type, tracing it up and verifying that yes, it actually can be represented as in the cast pointer. We then enter the comment that the lazy shit who originally cast it should have put in back in 1990. :)

There are no safe casts, in my view, but there are necessary ones, as in your case when you bump up against an API you need to use. The safest way to deal with it, again in my view, is to wrap the API in a class of your own that performs the cast internally, so it occurs in exactly one well-documented place in the code.
Music ot my ears. To bad 90% of programs seem to ignore fundamentals like this.

80% of my my job:
Take other people software that was delivered 2 years ago and fix bugs that appeared later in life or enhance the software to pride a better user experience. Tha crap I have run into is amazing. The number of times that I have run into projects that are a mish-mash of C and C++ is amazing. It seems liek half the programs out there (or more) are done this way. it's a convoluted mess. And do not get me started on extern!
 

degibson

Golden Member
Mar 21, 2008
1,389
0
0
Originally posted by: Saijin
Markbnj was also saying that it could be unsafe, so what about on other systems, what is the risk of that cast? And how do I find out or where I can look to learn more?

One way to learn more is to experiment of different architectures! Check out this toy program:

1 #include <stdio.h>
2
3 int main( int argc, char * argv[] ) {
4 char cBuff[16]; // We're going to find an unaligned byte in here
5 int i; // loop iterator
6 char * p_char;
7 int * p_int;
8 int myInt;
9 char myChar;
10
11 // Fill it with something meaningful
12 for(i=0;i<16;i++) {
13 cBuff[ i ] = i;
14 }
15
16 // Now find our unaligned pointer
17 for(i=5;i<10;i++) {
18 p_char = &cBuff[ i ];
19 // If either of the lower bits are set on the pointer,
20 // then the pointer isn't 32-bit (int) aligned
21 if( ((unsigned long long) p_char) & 0x03 ) break;
22 }
23
24 printf("The pointer is 0x%x\n",p_char);
25 myChar = *p_char;
26 printf("The 8-bit value there is %x\n",(int) myChar);
27 printf("Tring to cast to an int* and deference...");
28 fflush(stdout);
29 p_int = (int*) p_char;
30 myInt = *p_int;
31 printf("Success!\n");
32 printf("The 32-bit value there is %x\n", myInt);
33
34 }

Now, if I compile and run it on an x86 machine:
hms-surprise(6)% gcc unaligned.c
unaligned.c: In function 'main':
unaligned.c:21: warning: cast from pointer to integer of different size
hms-surprise(7)% ./a.out
The pointer is 0xbfb0f531
The 8-bit value there is 5
Tring to cast to an int* and deference...Success!
The 32-bit value there is 8070605

x86 will allow you to have unaligned words in memory.

However, this is what happens when the same program is run on a SPARC machine:
hms-boadicea(1)% gcc unaligned.c
unaligned.c: In function `main':
unaligned.c:21: warning: cast from pointer to integer of different size
hms-boadicea(2)% ./a.out
The pointer is 0xffbffa5d
The 8-bit value there is 5
Tring to cast to an int* and deference...Bus error (core dumped)

Unaligned accesses trigger bus errors on SPARC-Solaris. Therein lies one of the many small dangers of casting.

Edit: Modified code to fix italics... silly [ i ]