8

i'm trying to read contents of PNG file.

As you may know, all data is written in a 4-byte manner in png files, both text and numbers. so if we have number 35234 it is save in this way: [1000][1001][1010][0010].

but sometimes numbers are shorter, so the first bytes are zero, and when I read the array and cast it from char* to integer I get wrong number. for example [0000] [0000] [0001] [1011] sometimes numbers are misinterpreted as negative numbers and simetimes as zero!

let me give you an intuitive example:

char s_num[4] = {120, 80, 40, 1};

int  t_num = 0;

t_num = int(s_num);

I wish I could explain my problem well!

how can i cast such arrays into a single integer value?

ok ok ok, let me change my code to explain it better:

char s_num[4] = {0, 0, 0, 13};
int  t_num;


t_num = *((int*) s_num);
cout << "t_num: " << t_num << endl;

here we have to get 13 as the result, ok? but again with this new solution the answer is wrong, you can test on your computers! i get this number:218103808 which is definitely wrong!

sepisoad
  • 2,201
  • 5
  • 26
  • 37

7 Answers7

11

You cast (char*) to (int). What you should do is cast to pointer to integer, i.e.

t_num = *((int*) s_num));

But really you should extract your code into it's own function and make sure that:

  1. endianness is correct
  2. sizeof(int) == 4
  3. Use C++ casts (i.e. static, dynamic, const, reinterpret)
iammilind
  • 68,093
  • 33
  • 169
  • 336
Axel
  • 13,939
  • 5
  • 50
  • 79
  • what does same results mean? perhaps you should explain, why you expect this to give 241... – Axel Jan 08 '11 at 10:28
  • Oh... what you want is just to sum up the byte values of your 4 characters. That would give 241. Macieks' answer does that. But is that really what you want to do? Looks strange to me (not the code, I'm trying to understand your problem). – Axel Jan 08 '11 at 10:32
  • @iammilind: Did you read the entirety of the answer you linked? `char*` is one of the few occasions where this doesn't break aliasing – AndyG Dec 21 '16 at 19:46
  • 1
    @iammilind: To back up my statement, refer to the C++ standard `§3.10/10 Lvalues and rvalues [basic.lval]` Which makes special provisions for `char` and `unsigned char` – AndyG Dec 21 '16 at 19:55
  • This [does violates strict aliasing rules](https://stackoverflow.com/questions/37259909/cs-strict-aliasing-rule-is-the-char-aliasing-exemption-a-2-way-street). – xskxzr Feb 06 '18 at 19:05
8

Assuming a little-endian machine with a 32-bit integer, you can do:

char s_num[4] = {0xAF, 0x50, 0x28, 0x1};
int t_num = *((int*)s_num);

To break it into steps:

  1. s_num is an array, which can be interpreted as a pointer to its first element (char* here)
  2. Cast s_num to int* because of (1) - it's OK to cast pointers
  3. Access the integer pointed to by the cast pointer (dereference)

To have 0xAF as the low byte of the integer. Fuller example (C code):

#include <stdio.h>

int main()
{
    char s_num[4] = {0xAF, 0x50, 0x28, 0x1};
    int t_num = *((int*)s_num);

    printf("%x\n", t_num);
    return 0;
} 

Prints:

12850af

As expected.

Note that this method isn't too portable, as it assumes endianness and integer size. If you have a simple task to perform on a single machine you may get away with it, but for something production quality you'll have to take portability into account.

Also, in C++ code it would be better to use reinterpret_cast instead of the C-style cast.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
2

I find using the std bitset the most explicit way of doing conversions (In particular debugging.)

The following perhaps is not what you want in your final code (too verbose maybe) - but I find it great for trying to understand exactly what is going on.

http://www.cplusplus.com/reference/stl/bitset/

#include <bitset>
#include <iostream>
#include <string>

int
main  (int ac, char **av)
{

  char s_num[4] = {120, 80, 40, 1};
  std::bitset<8> zeroth   = s_num[0];
  std::bitset<8> first    = s_num[1];
  std::bitset<8> second   = s_num[2];
  std::bitset<8> third    = s_num[3];

  std::bitset<32> combo;
  for(size_t i=0;i<8;++i){
    combo[i]     = zeroth[i];
    combo[i+8]   = first[i];
    combo[i+16]  = second[i];
    combo[i+24]  = third[i];
  }
  for(size_t i = 0; i<32; ++i)
    {
      std::cout<<"bits ["<<i<<"] ="<<combo.test(i)<<std::endl;
    }
  std::cout<<"int = "<<combo.to_ulong()<<std::endl;
}
Tom
  • 5,219
  • 2
  • 29
  • 45
1

Axel's answer violates the strict aliasing rule, at least since C++14. So I post this answer for future users.


Apart from endianness and size issues, a safe way is to use std::memcpy, i.e.

   unsigned char s_num[4] = {13, 0, 0, 0}; 
// ^^^^^^^^               // ^^ fix endianness issue
// use unsigned char to avoid potential issues caused by sign bit

int t_num;

std::memcpy(&t_num, s_num, 4);
xskxzr
  • 12,442
  • 12
  • 37
  • 77
0

Did you know that int's in C++ overflow after the 32767'th value? That would explain your negative number for 35234.

The solution is to use a data type that can handle the larger values. See the Integer Overflow article for more information:

http://en.wikipedia.org/wiki/Integer_overflow

UPDATE: I wrote this not thinking that we all live in the modern world where 32 bit and 64 bit machines exist and flourish!! The overflow for int's is in fact much much larger than my original statement.

jamesmortensen
  • 33,636
  • 11
  • 99
  • 120
0

Conversion is done good, because you aren't summing up these values but assign them as one value. If you want to sum them you have to do it manualy:

int i;
for (i = 0; i<4; ++i)
    t_num += s_num[i];
thkala
  • 84,049
  • 23
  • 157
  • 201
Maciej
  • 3,685
  • 1
  • 19
  • 14
0

EDIT: it seems that you don't want to sum the numbers after all. Leaving this answer here for posterity, but it likely doesn't answer the question you want to ask.

You want to sum the values up, so use std::accumulate:

#include <numeric>
#include <iostream>

int main(void) {
    char s_num[4] = {120,80,40,1};
    std::cout << std::accumulate(s_num, s_num+4,0) << std::endl;
    return 0;
}

Produces output:

pgp@axel02:~/tmp$ g++ -ansi -pedantic -W -Wall foo.cpp -ofoo
pgp@axel02:~/tmp$ ./foo
241
Philip Potter
  • 8,975
  • 2
  • 37
  • 47