struct vs union

简介:
从定义上来看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的值.

目录
相关文章
|
算法 NoSQL 分布式数据库
深入浅出分布式系统的数据一致性
在探索后端技术的海洋时,数据一致性如同北极星,指引着我们的航向。本文将带你领略分布式系统的奥秘,从浅显易懂的角度出发,逐步深入到数据一致性的核心概念、实现策略及其在真实世界中的应用。我们将一起探讨如何在这个多变的世界中保持数据的和谐与统一,确保每一次数据的读写都能如行云流水般自然和顺畅。
|
数据安全/隐私保护
|
jenkins 持续交付 开发工具
Jenkins-pipline流水线语法介绍并结合Blue Ocean查看流水线(十四)
jenkins集成pipeline流水线 1.pipeline概述 pipeline流水线,可以直观的展示每个阶段做的任务,以及每个阶段耗费的时间。 pipeline不在使用鼠标来实现自动构建,也不要去看控制台日志,而是全程使用代码的方式来实现,构建完成后会展示一个视图,用来展示每个阶段完成的情况 pipeline使用Groovy语言来编写,固定代码语法
444 0
Jenkins-pipline流水线语法介绍并结合Blue Ocean查看流水线(十四)
|
分布式计算 MaxCompute SQL
海胜专访--MaxCompute 与大数据查询引擎的技术和故事
在2019大数据技术公开课第一季《技术人生专访》中,阿里巴巴云计算平台高级技术专家苑海胜为大家分享了《MaxCompute 与大数据查询引擎的技术和故事》,主要介绍了MaxCompute与MPP Database的异同点,分布式系统上Join的实现,且详细讲解了MaxCompute针对Join和聚合引入的Hash Clustering Table和Range Clustering Table的优化。
2935 0
海胜专访--MaxCompute 与大数据查询引擎的技术和故事
|
机器学习/深度学习 人工智能 算法
拍张照片求解数独,计算机如何看懂题目,这个GitHub项目告诉你
现在只需拍张照片,就能快速解决数独问题了。数独对计算机来说不是什么难事,但就是这样一个“平平无奇”的项目却登上了GitHub今日的热榜。
|
监控 前端开发 数据库
实时计算在天猫双十一大屏中的应用
案例与解决方案汇总页:阿里云实时计算产品案例&解决方案汇总 本文为您介绍实时计算在天猫双十一大屏业务中的应用。 天猫双十一大屏背后最强大最核心的支持力量来自于阿里云实时计算。曾经天猫双十一大屏后台流式计算使用开源的Storm来进行开发,整个开发时间长达一个月。
7012 0