C Language
Związki
Szukaj…
Różnica między struct a union
To pokazuje, że członkowie związku dzielą pamięć, a członkowie struktury nie dzielą pamięci.
#include <stdio.h>
#include <string.h>
union My_Union
{
int variable_1;
int variable_2;
};
struct My_Struct
{
int variable_1;
int variable_2;
};
int main (void)
{
union My_Union u;
struct My_Struct s;
u.variable_1 = 1;
u.variable_2 = 2;
s.variable_1 = 1;
s.variable_2 = 2;
printf ("u.variable_1: %i\n", u.variable_1);
printf ("u.variable_2: %i\n", u.variable_2);
printf ("s.variable_1: %i\n", s.variable_1);
printf ("s.variable_2: %i\n", s.variable_2);
printf ("sizeof (union My_Union): %i\n", sizeof (union My_Union));
printf ("sizeof (struct My_Struct): %i\n", sizeof (struct My_Struct));
return 0;
}
Używanie związków do reinterpretacji wartości
Niektóre implementacje języka C pozwalają na zapis kodu do jednego elementu typu unii, a następnie odczytanie go w innym celu wykonania pewnego rodzaju rzutowania reinterpretacyjnego (parsowanie nowego typu jako reprezentacji bitowej starego).
Należy jednak pamiętać, że nie jest to dozwolone w obecnym lub przeszłym standardzie C i spowoduje niezdefiniowane zachowanie, jednak jest to bardzo popularne rozszerzenie oferowane przez kompilatory (więc sprawdź dokumentację kompilatora, jeśli planujesz to zrobić) .
Jednym z prawdziwych przykładów tej techniki jest algorytm „szybkiego odwrotnego pierwiastka kwadratowego”, który opiera się na szczegółach implementacji liczb zmiennoprzecinkowych IEEE 754 w celu wykonania odwrotnego pierwiastka kwadratowego szybciej niż przy użyciu operacji zmiennoprzecinkowych, algorytm ten można wykonać albo za pomocą rzutowania wskaźnika (co jest bardzo niebezpieczne i łamie ścisłą zasadę aliasingu) lub poprzez połączenie (które jest nadal niezdefiniowanym zachowaniem, ale działa w wielu kompilatorach):
union floatToInt
{
int32_t intMember;
float floatMember; /* Float must be 32 bits IEEE 754 for this to work */
};
float inverseSquareRoot(float input)
{
union floatToInt x;
int32_t i;
float f;
x.floatMember = input; /* Assign to the float member */
i = x.intMember; /* Read back from the integer member */
i = 0x5f3759df - (i >> 1);
x.intMember = i; /* Assign to the integer member */
f = x.floatMember; /* Read back from the float member */
f = f * (1.5f - input * 0.5f * f * f);
return f * (1.5f - input * 0.5f * f * f);
}
Technika ta była w przeszłości szeroko stosowana w grafice komputerowej i grach ze względu na jej większą prędkość w porównaniu do operacji zmiennoprzecinkowych, i stanowi bardzo duży kompromis, tracąc pewną dokładność i będąc bardzo nieprzenośnym w zamian za szybkość.
Pisanie do jednego członka związku i czytanie od innego
Członkowie związku dzielą tę samą przestrzeń w pamięci. Oznacza to, że zapis do jednego członka powoduje nadpisanie danych we wszystkich innych elementach, a odczyt z jednego elementu daje te same dane, co odczyt z wszystkich innych elementów. Ponieważ jednak członkowie związku mogą mieć różne typy i rozmiary, odczytywane dane mogą być interpretowane w różny sposób, patrz http://www.riptutorial.com/c/example/9399/using-unions-to-reinterpret-values
Prosty przykład poniżej pokazuje związek z dwoma członkami tego samego typu. Pokazuje, że zapis do elementu m_1
powoduje odczytanie zapisanej wartości z elementu m_2
a zapis do elementu m_2
powoduje odczytanie zapisanej wartości z elementu m_1
.
#include <stdio.h>
union my_union /* Define union */
{
int m_1;
int m_2;
};
int main (void)
{
union my_union u; /* Declare union */
u.m_1 = 1; /* Write to m_1 */
printf("u.m_2: %i\n", u.m_2); /* Read from m_2 */
u.m_2 = 2; /* Write to m_2 */
printf("u.m_1: %i\n", u.m_1); /* Read from m_1 */
return 0;
}
Wynik
u.m_2: 1
u.m_1: 2