-1

I have a struct with same type members in it. I am trying to convert it into uint8_t type. I am able to do that but cannot see the output please tell me where I am going wrong. Also I know there are another ways to do it? I am trying to do it this way because I want to get use to static_cast and reinterpret_cast.

Code is below:

int main()
{
    struct xs{
        bool x :1 ;
        bool y :1;
        bool z :1;
        uint8_t num :5;
    } zs;

    uint8_t* P = static_cast<uint8_t*>(static_cast<void*>(&zs));

    cout << *P << endl;

    return 0;
}
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
Mandeep
  • 335
  • 3
  • 13
  • 3
    You are falling foul of the annoying fact that a `uint8_t` is in reality an `unsigned char`, so will be printed as some ascii character - quite likely not a visible one. Cast this up to some other integer type. But bear in mind what you are doing (treating a type as something it isn't) is horrible [**Undefined Behaviour**](http://en.cppreference.com/w/cpp/language/ub), there are no guarantees whatsoever what will happen. – BoBTFish Apr 18 '18 at 11:21
  • @BoBTFish Tried with char type still no output – Mandeep Apr 18 '18 at 11:28
  • As @BoBTFish says this is undefined behaviour. The size and layout of `struct xs` is implementation defined and may not be a `uint8_t` in size. Even if it was, what your casts are doing is defined to not work. See __Notes__ in: http://en.cppreference.com/w/cpp/language/bit_field – Richard Critten Apr 18 '18 at 11:30
  • 3
    just a warning: maybe "getting used to" reinterpret cast isnt the best idea. reinterpret casts are for special circumstances and shouldnt be used too often, actually they are red flags that indicate that something weird is going on. – 463035818_is_not_an_ai Apr 18 '18 at 11:33

3 Answers3

1

There a lot of problems here:

  1. You seem to believe that x, y, and z will all pack into the single uint_8. This is not the case. "Adjacently declared bit fields of the same type can then be packed by the compiler into a reduced number of words"[1]
  2. "The value of sizeof(bool) is implementation defined and might differ from 1"[2] Therefore your xs will be implementation defined, but certainly not equivilent to sizeof(uint_8)
  3. Because xs is not "similar" to a uint_8 according to the rules defined for C++'s type aliasing the behavior of your reinterpret_cast<uint_8*> is undefined
  4. Finally as has been pointed out by many others the reason that you can't see anything is that whatever implementation defined value is at *P it is likely a control character with no visible representation when treated by cout as a char

A possible workaround to the code you have would be to use these definitions:

constexpr uint8_t X = 0B1000'0000;
constexpr uint8_t Y = 0B0100'0000;
constexpr uint8_t Z = 0B0010'0000;
constexpr uint8_t NUM = 0B0001'1111;

uint8_t zs;

Then given that some value is assigned to zs you can perform these functions to output the former bit fields:

cout << ((zs & X) != 0) << endl;
cout << ((zs & Y) != 0) << endl;
cout << ((zs & Z) != 0) << endl;
cout << (zs & NUM) << endl;

Live Example

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • Thank you what u described in "1" is exactly what i wanted. I now tried implementing int* P = static_cast(static_cast(&zs)); It works. However, if i define x as true than output is 1 but if i define y as true then output is 2. In which order it is working like that tthank You. – Mandeep Apr 18 '18 at 13:39
  • I think i know its working in 8421 rule as on the basis of bit position it gives me a int number. But then y it does not work when trying to use char instead of int there. – Mandeep Apr 18 '18 at 13:44
  • Is there a reason you used `#define` for the constants? It seems to be a beginner's quesiton, so I would not show something that you shouldn't use. – Jens Apr 18 '18 at 13:46
  • 1
    @Mandeep I am afraid that at least I don't understand what you saying. What is 8421? Where do you use a char insteaf of int? There is no `int` in the code. – Jens Apr 18 '18 at 13:47
  • @Jens No particular reason. `const uint8_t x = 0B1000'0000` would have worked just as well as the `#define`s – Jonathan Mee Apr 18 '18 at 13:48
  • @Mandeep I'm not sure what the 8421 number you speak of is, but that won't fit in a `uint8_t`/`char`. – Jonathan Mee Apr 18 '18 at 13:49
  • @Jens By 8421 rule i mean "Binary-coded decimal" secondly i tried using int * instead of Uint * . it works "int* P = static_cast(static_cast(&zs));" It gives me output on the "Binary-coded decimal" value. – Mandeep Apr 18 '18 at 13:53
  • @Jens now when i try using char instead of int * it gives me no output – Mandeep Apr 18 '18 at 13:54
  • @Mandeep Note that as mentioned in **3** of my answer you may not cast a `struct xs` pointer to an `int` pointer. To do so would be undefined behavior. – Jonathan Mee Apr 18 '18 at 13:56
  • @Mandeep I am still not sure I understand. Your approach with `static_cast` or `reinterpret_cast` is undefined behavior, as pointed out many times in comments in the answer above. It may look like it is correct because it behaves as you expect, but that is not guaranteed by the language semantics. http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html contains some usefull explanation. If you cast it to a `char` it may also not work because your struct may not fit into `sizeof(char)`, which is probably one byte. – Jens Apr 18 '18 at 13:57
  • @JonathanMee I am not getting. My question is simple that int pointer Works !!! but Char pointer does not work y – Mandeep Apr 18 '18 at 13:58
  • @Mandeep Maybe you need to open a new question because the text you posted in the question asks something else. But all answers are here in the comments and in the answer. Accessing something which is not an `int` through a pointer of type `int*` is undefined behavior (UB). It does something which may look correct, but another compiler or even just different compiler flags will do something else. – Jens Apr 18 '18 at 14:00
  • @Mandeep Hey I kinda agree with Jens that a new question would be helpful in understanding what you're asking. It's just hard to get it in the comments. If you could ask and link here I'll try to come help. – Jonathan Mee Apr 18 '18 at 14:15
0
  1. it is undefined behavior to access an object through a pointer to a type other than the type of the object. it works with most compilers, but technically your programm is allowed to do whatever it wants.

  2. assuming we are not running into a problem mentioned in 1, my guess is that uint8_t is an alias for char, so cout will put a char onto your console. you did not initialize the memory you are inspecting, so it can be 0. a char with value zero wont print out anything "observeable" in your console, look at the ascii table. try filling your struct with a 50 for example

phön
  • 1,215
  • 8
  • 20
  • _"you did not initialize the memory you are inspecting, so it can be 0"_ no, it will be uninitialized garbage. – Jonathan Wakely Apr 18 '18 at 11:40
  • 1
    I don't think it is UB. There is an exception for pointers of type `char*`, `unsigned char*` and `std::byte*`. – Jens Apr 18 '18 at 11:53
  • @JonathanWakely so techniccally it CAN be 0 :P sure, according to the standard its garbage. what i initially thought of was a compiler "extension" where the int would get zero initialized in debug mode only. i think i read it somewhere that visual studio did it? but i may be super wrong here. so i let it open to "it can be 0". – phön Apr 18 '18 at 11:53
  • 1
    @Jens you are right. but is uint8_t always(!) aliasing char,unsigned char or byte? – phön Apr 18 '18 at 11:56
  • @phön You are right. `std::uint8_t` can be any type that is exactly 8 bits, so even if `CHAR_BITS==8` it could be something else. `char` can also be larger than 8 bits (which would make `sizeof` interesting since it measures in multiples of `sizeof(char)`), and in this case `std::uint8_t` has ot be different. I have never seen this in real life. – Jens Apr 18 '18 at 12:18
  • @phon Please tell me with an example how to initzialise for the memory. thank You – Mandeep Apr 18 '18 at 13:20
  • you created an object of type `xs` with the name `zs`. so you can access the data with `zs.num = 40` for example – phön Apr 18 '18 at 13:25
  • @Mandeep xs a{1,0,1,40}; – Jens Apr 18 '18 at 13:40
  • @phön thank you. I now tried implementing int* P = static_cast(static_cast(&zs)); It works. However, if i define x as true than output is 1 but if i define y as true then output is 2. In which order it is working like that tthank You. – Mandeep Apr 18 '18 at 13:40
  • @phön I think i know its working in 8421 rule as on the basis of bit position it gives me a int number. But then y it does not work when trying to use char instead of int there. – Mandeep Apr 18 '18 at 13:44
  • what output do you wanna see/what do you expect? – phön Apr 18 '18 at 14:50
0

There are two things that need to be solved here. First off, as some pointed out, you cannot access your object this way. If you want to construct a uint8_t properly, you would need to read the variables inside your struct and do some bit shifts, something like this:

uint8_t value = 0;
value |= (zs.x ? 1 << 7 : 0);
value |= (zs.y ? 1 << 6 : 0);
value |= (zs.z ? 1 << 5 : 0);
value |= zs.num;

Now, the second problem you are facing is that you're trying to output a number of 8-bits wide. By default, this is interpreted as a 'character' and will display as such. In order to accomplish what you want to do, you can either use a variable with a different length (uint16_t, uint32_t, ...) or use std::to_string.

cout << std::to_string(value) << endl;
AlexG
  • 1,091
  • 7
  • 15