Union and type conversion

2

Searching about unions I found the following information:

Unions are generally used for type conversions. For example, we can use a union to write the binary representation of an integer in a disk file.

First we create a union between an integer and a 2-byte array:

union pw {
int i;
char ch[2];
};

Then a function that prints the values:

putw(union pw word, FILE *fp)
{
    //  Escreve a primeira metade
    putc(word->ch[0], fp);
    //  Escreve a segunda metade
    putc(word->ch[1], fp);
}

I do not quite understand what the author meant by this example. He meant that through a union we could use, for example, an integer like char? Is there any other example related to type conversion using unions? Another question: According to the arguments of the created function, it would receive a union by copy / value, since there is no pointer specifier (*) in the declaration, but there is a pointer (->) in the function body .

    
asked by anonymous 12.09.2015 / 14:39

1 answer

2

What are unions?

A union functions as a block of memory that is used to store variables of different types so that when a new value is assigned to any of the fields, the existing information is overwritten with the new value. That is, the fields share the same space in memory, or part of it (if the field is less than the total space of the union).

Thus, all fields of union start at the same memory address. That way you can go get pieces of the whole at a time and go write them in the file as you did.

Unions vs Structs

I know this is not part of your question, but just for comparison purposes, this operation differs from struct 's, where each field has its memory address apart, its memory space. That is, in struct 's, each field has its value and when assigning a value to one field, does not change the value of another. In union 's fields have the same start address.

About your example

Leaving the quirks a little aside for didactic issues, an integer (int) in c / c ++ usually occupies 4 bytes (32 bits) of memory (with a 32-bit processor). So in your example, in fact, it is not the 'half' that is being printed first, but the first byte of int , because type char occupies 1 byte. So in the example you wrote the first two bytes of int .

Simple union example

Here you can see how union 's can be used to' pick up pieces' of an integer or other type of information:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

// O tamanho que a union armazena na memória é o tamanho do maior campo da union
union MinhaUnion {
    uint32_t x; // ocupa 4 bytes de memória
    uint16_t y; // ocupa 2 bytes de memória
    uint8_t z; // ocupa 1 byte de memória
}; // tamanho total que a union ocupa na memória é 4 bytes.

int main() {
    MinhaUnion u;

    // coloca o inteiro nos 4 bytes da union
    u.x = 123456789;

    // pega o inteiro total, depois o valor inteiro dos dois primeiros bytes, depois o inteiro somente do primeiro byte
    printf("X = %d, Y = %d, Z = %d\n", u.x, u.y, u.z);

    return 0;
}

About operators - > e.

The -> operator is used with pointers, variables that contain memory addresses. When a variable is passed by reference (declaring the parameter as int* i for example) when referencing variable members within the function, you must use the -> operator. But if the variable was passed by value , you should use the . operator to reference the members of struct or union (or methods of an object if you are using C ++) . By modifying the example above, we now have:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

// O tamanho que a union armazena na memória é o tamanho do maior campo da union
union MinhaUnion {
    uint32_t x; // ocupa 4 bytes de memória
    uint16_t y; // ocupa 2 bytes de memória
    uint8_t z; // ocupa 1 byte de memória
}; // tamanho total que a union ocupa na memória é 4 bytes.

int imprimeUnion(union MinhaUnion un) {
    FILE *fp = NULL;

    fp = fopen("output.txt", "w");

    if (fp != NULL) {
        fprintf(fp, "X = %d, Y = %d, Z = %d\n", un->x, un->y, un->z);
        return 1;
    }

    return 0;
}

int main() {
    MinhaUnion u;

    // coloca o inteiro nos 4 bytes da union
    u.x = 123456789;

    // passa a union por valor e imprime os campos dela no arquivo
    imprimeUnion(u);

    return 0;
}

See what the compiler says about the above code:

union_vs_struct.cpp: In function ‘int imprimeUnion(MinhaUnion)’:
union_vs_struct.cpp:18:51: error: base operand of ‘->’ has non-pointer type ‘MinhaUnion’
     fprintf(fp, "X = %d, Y = %d, Z = %d\n", un->x, un->y, un->z);
                                               ^
union_vs_struct.cpp:18:58: error: base operand of ‘->’ has non-pointer type ‘MinhaUnion’
     fprintf(fp, "X = %d, Y = %d, Z = %d\n", un->x, un->y, un->z);
                                                      ^
union_vs_struct.cpp:18:65: error: base operand of ‘->’ has non-pointer type ‘MinhaUnion’
     fprintf(fp, "X = %d, Y = %d, Z = %d\n", un->x, un->y, un->z);

Saying that the operand of -> (the variable that was passed by value - un ) is not a pointer. Now, if you change -> to . or change the parameter declaration in the function so that the variable is passed by reference, the code compiles, it depends on what you intend to do.

About such 'conversion'

Finally, what happens is not a "conversion" between types. But as stated above, because all members share the same initial memory address, you can get 'chunks' from the total memory space of union and manipulate them through another (variable) field. So if you have the following union with two fields:

union UNION {
    int num;
    char[4] vetor;
};

You can treat the second byte of the number as a char by referencing vetor[1] since vetor and num refer to the same starting memory address.

    
12.09.2015 / 14:45