Home > Back-end >  Using Unions to convert an IP address into one 32-bit, two 16-bit, and four 8-bit values
Using Unions to convert an IP address into one 32-bit, two 16-bit, and four 8-bit values

Time:11-15

I'm working on a school assignment. I am writing a program that utilizes unions to convert a given IP address in "192.168.1.10" format into its 32-bit single value, two 16-bit values, and four 8-bit values.

I'm having trouble with implementing my structs and unions appropriately, and am looking for insight on the subject. To my understanding, unions point to the same location as the referenced struct, but can look at specified pieces.

Any examples showing how a struct with four 8-bit values and a union can be used together would help. Also, any articles or books that might help me would also be appreciated.

Below is the assignment outline:

Create a program that manages an IP address. Allow the user to enter the IP address as four 8 bit unsigned integer values (just use 4 sequential CIN statements). The program should output the IP address upon the users request as any of the following. As a single 32 bit unsigned integer value, or as four 8 bit unsigned integer values, or as 32 individual bit values which can be requested as a single bit by the user (by entering an integer 0 to 31). Or as all 32 bits assigned into 2 variable sized groups (host group and network group) and outputted as 2 unsigned integer values from 1 bit to 31 bits each.

I was going to cin to int pt1,pt2,pt3,pt4 and assign them to the IP_Adress.pt1, .... etc.

struct IP_Adress {
    unsigned int pt1 : 8;
    unsigned int pt2 : 8;
    unsigned int pt3 : 8;
    unsigned int pt4 : 8;
};

I have not gotten anything to work appropriately yet. I think I am lacking a true understanding of the implementation of unions.

CodePudding user response:

A union is not a good fit for this assignment. In fact, nothing in the text you quoted even says to use a union at all. And, a union will not help you with the parts of the assignment that deal with "32 individual bit values" or with "32 bits assigned into 2 variable sized groups". Those parts of the assignment will require bit shifting instead. Bit shifting is the better way to solve the other parts of the assignment, as well.

That being said, if you absolutely must use a union, you are probably looking for something more like this instead:

union IP_Adress {
    uint8_t u8[4];   // four 8-bit values
    uint16_t u16[2]; // two 16-bit values
    uint32_t u32;    // one 32-bit value
};

Except that C does not allow you to write into one union field and then read from another. C allows that kind of type puning, but it is undefined behavior in C .

Why is type punning considered UB?

CodePudding user response:

The asker already knows that doing this can blow up in their face a number of different ways, but here's a simple example for 4 byte, 4x1 byte, and 32x1 bit.

union bad_idea
{
    uint32_t ints; // 32 bit unsigned integer
    uint8_t bytes[sizeof(uint32_t)]; // 4 8 bit unsigned integers
};

and then

uint32_t get_int(const bad_idea & in)
{
    return in.ints;
} 
uint8_t get_byte(const bad_idea & in,
                size_t offset)
{
    if (offset >= sizeof(uint32_t))  trap typos and idiots
    {
        throw std::runtime_error("invalid offset");
    }
    return in.bytes[offset];
} 
bool get_bit(const bad_idea & in,
                size_t offset)
{
    if (offset >= sizeof(uint32_t)*8)
    {
        throw std::runtime_error("invalid offset");
    }
    return (in.ints >> offset) & 1; // shift the required bit to the end (in.ints >> offset)
                                    // then mask off all of the other bits (& 1)
} 

Things get a bit ugly getting input because you can't simply

std::cin >> bad.bytes[0];

because it reads a single character. Type in 127 for the first octet and you'll wind up filling bad.bytes[0] through bad.bytes[2] with '1', '2', and '7'.

You need to involve a temporary variable

int temp;
std::cin >> temp;
// tests for valid range in temp
bad.bytes[0] = temp

or risk some explosive batsmurf like

std::cin >> *(int*)&bad.bytes[0];
// tests for valid value in bad.bytes[0] impossible because aliasing has been broken 
// and everything is already <expletive deleted>ed

pardon my C. The more respectable

std::cin >> *reinterpret_cast<int*>(&bad.bytes[0]);

isn't any better. As ugly as it is, use the temporary variable and bundle it up in a function to eliminate the duplication. Frankly this is a time when I'd probably fall back into C and pull out good ol' scanf.

  • Related