从定义上来看struct和union定义的风格都差不多.
但是union只能存储一个元素, 与struct 一样也是固定长度. 长度取决于union中定义的所有ITEM中占用空间最大的那个.
为什么C这么多固定长度的东西呢, 原因是指针, 如果不定长, 存储这些类型的数组及数组型指针将使用起来非常困难. 因为需要定长, 地址运算的时候才比较方便. 如char a[10], a+1 地址加1字节. int a[10], a+1 则地址加4字节. 如果使用下面这个union 定义的 test a[10], a+1 地址加104字节. 如果不是定长, 那就不好搞了.
例如 :
typedef union test {
int a;
long b;
float c;
short d;
char a[100];
} test;
这个union的长度取决于a[100], 理论上应该是100, 但是由于alignment, 需要一些pad空间, 所以是104.
例如 :
[root@db-172-16-3-150 ~]# cat f.c
#include <stdio.h>
typedef union test {
int a;
long b;
float c;
short d;
char e[100];
} test;
int main() {
fprintf(stdout, "sizeof(test):%lu\n", sizeof(test));
return 0;
}
结果
[root@db-172-16-3-150 ~]# gcc -g ./f.c -o f && ./f
sizeof(test):104
union用在什么场景呢? 例如要存储一个数量, 可能是个数(int), 也可能是重量(float), 也可能是体积(float).
如下面的例子 :
[root@db-172-16-3-150 ~]# cat e.c
#include <stdio.h>
typedef union quantity {
int count;
float kg;
float ml;
} quantity;
typedef struct goods {
const char *name;
const char *country;
quantity amount;
} goods;
int main() {
goods apple = {"apple", "china", {.kg = 10}};
goods watermelon = {"watermelon", "china", {5}};
goods mango = {"mango", "china", {.kg = 6}};
goods wine = {"wine", "franch", {.ml=10000}};
fprintf(stdout, "apple.amount.kg:%f, watermelon.amount.count:%i, mango.amount.kg:%f, wine.amount.ml:%f\n", apple.amount.kg, watermelon.amount.count, mango.amount.kg, wine.amount.ml);
// 如果把wine.amount改成count存储, 那读取的时候也要修改. wine.amount.count, 输出为%i.
wine.amount.count=999;
fprintf(stdout, "apple.amount.kg:%f, watermelon.amount.count:%i, mango.amount.kg:%f, wine.amount.count:%i\n", apple.amount.kg, watermelon.amount.count, mango.amount.kg, wine.amount.count);
wine.amount.kg=888.88;
fprintf(stdout, "apple.amount.kg:%f, watermelon.amount.count:%i, mango.amount.kg:%f, wine.amount.ml:%f\n", apple.amount.kg, watermelon.amount.count, mango.amount.kg, wine.amount.kg);
wine.amount.ml=777.77;
fprintf(stdout, "apple.amount.kg:%f, watermelon.amount.count:%i, mango.amount.kg:%f, wine.amount.ml:%f\n", apple.amount.kg, watermelon.amount.count, mango.amount.kg, wine.amount.ml);
return 0;
}
结果 :
[root@db-172-16-3-150 ~]# gcc -O3 -Wall -Wextra -Werror -g ./e.c -o e && ./e
apple.amount.kg:10.000000, watermelon.amount.count:5, mango.amount.kg:6.000000, wine.amount.ml:10000.000000
apple.amount.kg:10.000000, watermelon.amount.count:5, mango.amount.kg:6.000000, wine.amount.count:999
apple.amount.kg:10.000000, watermelon.amount.count:5, mango.amount.kg:6.000000, wine.amount.ml:888.880005
apple.amount.kg:10.000000, watermelon.amount.count:5, mango.amount.kg:6.000000, wine.amount.ml:777.770020
使用union 一个需要注意的地方是, 没有地方跟踪你存储的到底是哪个类型, 如这个union里面包含了int, float, short. 怎么知道我存储的到底是什么类型呢, 如果存储的是int, 但是使用float去格式化输出肯定是有问题的, 结果不可预期.
使用enum 枚举来判别用的union里面的什么类型是一个比较好的方法.
例如 :
[root@db-172-16-3-150 ~]# cat f.c
#include <stdio.h>
typedef enum unit_of_measure {
COUNT, KG, ML
} unit_of_measure;
typedef union quantity {
short count;
float weight;
float volume;
} quantity;
typedef struct fruit_order {
const char *name;
const char *country;
quantity amount;
unit_of_measure units;
} fruit_order;
void display(const fruit_order * order) {
fprintf(stdout, "This order contains ");
if (order->units == ML)
fprintf(stdout, "%2.2f ml of %s\n", order->amount.volume, order->name);
else if (order->units == KG)
fprintf(stdout, "%2.2f kg of %s\n", order->amount.weight, order->name);
else
fprintf(stdout, "%i %s\n", order->amount.count, order->name);
}
int main() {
fruit_order apples = {"apples", "china", .amount.count=188, COUNT};
fruit_order wines = {"wines", "franch", {.volume=650.0}, ML};
fruit_order mangos = {"mangos", "china", .amount.weight=7.6, KG};
display(&apples);
display(&wines);
display(&mangos);
return 0;
}
结果 :
[root@db-172-16-3-150 ~]# gcc -O3 -Wall -Wextra -Werror -g ./f.c -o f && ./f
This order contains 188 apples
This order contains 650.00 ml of wines
This order contains 7.60 kg of mangos
其他 :
1. type name 与 struct name, enum name, union name
typedef struct fish {
int age;
float weight;
char * name;
} fish;
其中strunt name 指的是struct fish这里定义的fish.
type name 指的是最后一行的fish.
struct name 和 type name 可以不一样. 也可以一样.
如果上面的定义改成 :
struct fish {
int age;
float weight;
char * name;
};
那么就没有type name.
如果没有type name , 定义变量时必须写成这样 : struct fish f1 = {.....};
如果有type name, 那么定义变量可以写成fish f1 = {.....}; (注意这里的fish是type name)
2. 赋值
struct :
struct fish {
int age;
float weight;
char * name;
};
// 定义变量的时候赋值struct fish f1 = {10, 5.6, "linux"};
// 或者写成{.age = 10, .weight=5.6, .name="linux"};
// 定义完变量再赋值
// f1.age=10;
// f1.weight=5.6;
// f1.name = "linux";
enum :
与上面类似.
union :
与上面类似, 唯一的不同是.
union a {
int count;
float weight;
float volumn;
};
union a a1 = {1.5};
这个1.5 是union 的第一个元素count的值.
时间: 2024-09-10 11:57:26