I'm reading in an id3 tag where the size of each frame is specified in 3 bytes. How would I be able to utilize this value as an int?
-
1What do mean "reading"? From memory? From a file? – Carl Norum Mar 27 '12 at 19:47
-
1In C you could utilize something called a bit field typed as int occupying 3 bytes = 3*8=24 bits. In C, this is denoted by `int int_3byte : 24` (inside a struct). I suppose C++ has an equivalent mechanism for this. – phimuemue Mar 27 '12 at 19:49
-
@CarlNorum Why not? Say I need a 100-element array of 3-byte values-- it would be easier with a bit field, no? – Alex May 31 '17 at 14:20
-
@Alex, I'm not sure how you'd do that; a bitfield can only be declared as part of a struct, not on its own. How are you going to set this array up? – Carl Norum Jun 01 '17 at 21:59
4 Answers
Read each byte and then put them together into your int
:
int id3 = byte0 + (byte1 << 8) + (byte2 << 16);
Make sure to take endianness into account.

- 219,201
- 40
- 422
- 469
-
1
-
1Also make sure that `byte0`, `byte1`, and `byte2` have `unsigned char` type (or have values in the range `0..255`) – Michael Burr Mar 27 '12 at 20:31
Read the bytes in individually, and put them into the correct places in an int:
int value = 0;
unsigned char byte1 = fgetc(ID3file);
unsigned char byte2 = fgetc(ID3file);
unsigned char byte3 = fgetc(ID3file);
value = (byte1 << 16) | (byte2 << 8) | byte3;
Edit: it appears that ID3 uses network (big-endian) byte order -- changed code to match.

- 476,176
- 80
- 629
- 1,111
-
This assumes that `char` is unsigned... (Also, `fgetc` returns an `int`!) – Oliver Charlesworth Mar 27 '12 at 19:49
-
@OliCharlesworth: fgetc returns an int, but the only values that can be negative (as an int) are things like EOF (which I'm ignoring for the moment). – Jerry Coffin Mar 27 '12 at 19:52
-
3The C++ people probably hate it for using `fgetc()` at all, but I'm with Jerry. – Carl Norum Mar 27 '12 at 19:52
If your process is computation intensive, and you want to save time (esp. if you are doing embedded systems), my suggestion will be to use union/struct. This takes away the load of left shift and right shift.
union{
unsigned int ui32;
struct{
unsigned char ll;
unsigned char lh;
unsigned char hl;
unsigned char hh;
}splitter;
}combine;
combine.splitter.ll = 0x78;
combine.splitter.lh = 0x56;
combine.splitter.hl = 0x34;
combine.splitter.hh = 0x12;
printf("This is the combined Value: %d", combine.ui32);
This Should work. If anyone feels this should not be used, please let me know the reason.
Cheers! Aditya

- 53
- 6
-
-
1@JackAvante..Well if you don't want the fourth byte, set it to 0 initially and don't update the combine.splitter.hh variable in your process. – indiaaditya Oct 13 '19 at 20:37
-
1Because there is much better way. Struct is not necessary. The problem is, it is not 3 byte conversion solution. template
union Buffer { unsigned char arr[sizeof(T)]; T variable; }; – Doctor Apr 20 '20 at 12:30 -
@Doctor According to the C++ Standard (unlike C), it's undefined behaviour to access inactive member of union except for a special case regarding common initial sequence of structs (which is not the case for your `Buffer`). Many compilers support this and define behaviour of such accesses in a way that's intuitively expected, but it's not required. – YurkoFlisk Jul 26 '22 at 17:46
You can read an arbitrary number of byte (Little Endian) and put them inside an integer, using the function read of an ifstream ("is" in the code). I use the function "my_read" for support, but it's not mandatory:
template <typename T>
std::istream& my_read(std::istream& is, T& num, size_t size = sizeof(T)) {
return is.read(reinterpret_cast<char*>(&num), size);
}
std::ifstream is(filename, std::ios::binary);
if(!is) { /*Error*/ }
size_t y = 0;
my_read(is, y, sizeof(uint8_t) * 3);
std::cout << y << std::endl;
The important thing is the inizialization of y to 0. It prevents the most significant bytes of y to mess up the 3 byte value.

- 31
- 3
-
Your code works if both current system byte order and 3-byte input value are little endian. In other cases, this is incorrect. E.g. if they are both big endian, you would need to read 3 bytes into `reinterpret_cast
(&y) + sizeof(size_t) - 3`. Mismatch byte order cases are a bit more complicated and would require reversing bytes. – YurkoFlisk Jul 25 '22 at 12:32 -
Of course, I take for granted that we were working with bytes stored in Little Endian. – Namibò Jul 26 '22 at 13:58
-
It's ok, no much problem with that. It'd just be a bit better if a sentence or two about endianness was present in your answer, as in some other answers. Also note that `size_t y = 0;` is, as currently written, within `if` statement (since it's the first statement after `if` clause). Your placeholder for error handling code could be something like `if (!is) { /* Error */ }` to avoid this. You can [edit] your answer to fix this minor issue. – YurkoFlisk Jul 26 '22 at 14:53
-
1You are right. I'll edit the answer right away. I didn't know that such precision was required in this platform. Next time I'll be more accurate. – Namibò Jul 27 '22 at 15:43