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的值.
时间: 2024-09-10 11:57:26

struct vs union的相关文章

[C/C++基础知识] 面试再谈struct和union大小问题

        最近找工作参加了很多笔试,其中考察结构体和联合体的大小问题是经常出现的一个问题.虽然题目简单而且分值比较低,但是还是想再给大家回顾下这些C和C++的基础知识.希望文章对你有所帮助~         PS:意外惊喜第三部分,所有权归它们公司所有.我只想分享学习并无它,望海涵~ 一. 真题介绍         1.[2015-9 完美] 在IA32架构下,下面的union结构的sizeof大小为:_______ union PageLayout { struct { int page

C++中数据对齐问题:struct、union、enum。再谈sizeof()

首先是struct,在C++中,结构体其实和class有很大的相似了.但是有一点不同的是,struct默认是public,而class中是private. 当然,struct继承等用法也是可以的. 共用体的声明方式是: 枚举的声明方式与共用体比较相似 其中a初始化为0,后面默认增1,若已经初始化,则后面再增1,比如d=6在这里. struct长度计算 大家猜一下,s1 x;int b=sizeof(x); 他的结果会是多少呢?有人会觉得应该是1+8+4+1=14. 实际上是24.为什么会是这样呢

Python中的struct模块介绍

  这篇文章主要介绍了Python中的struct模块,代码基于Python2.x版本,需要的朋友可以参考下 准确地讲,Python没有专门处理字节的数据类型.但由于str既是字符串,又可以表示字节,所以,字节数组=str.而在C语言中,我们可以很方便地用struct.union来处理字节,以及字节和int,float的转换. 在Python中,比方说要把一个32位无符号整数变成字节,也就是4个长度的str,你得配合位运算符这么写: ? 1 2 3 4 5 6 7 8 >>> n = 1

delphi 记录-如果将 C 的 struct 转为 Delphi Record

问题描述 如果将 C 的 struct 转为 Delphi Record 请教如何将下面的联合转为记录的一部分啊?坐等了,多谢. typedef struct _IP_ADAPTER_ADDRESSES { union { ULONGLONG Alignment; struct { ULONG Length; DWORD IfIndex; }; }; struct _IP_ADAPTER_ADDRESSES* Next; PCHAR AdapterName; PIP_ADAPTER_UNICAS

单链表-C语言求教merge(struct node *p,struct node *q)哪里出错了?

问题描述 C语言求教merge(struct node *p,struct node *q)哪里出错了? #include "stdio.h" #include "stdlib.h" struct node { int data; struct node next; }; struct node *creat(int *a) { struct node*h,*p,*q; int i; h=p=(struct node)malloc(sizeof(struct nod

几道C++笔试题

#include <iostream> using namespace std; class A { public: A() { print(); } void fun() { print(); } virtual void print() { cout<<"A::print()"<<endl; } int m; }; class B: public A { public: virtual void print() { cout<<&qu

Why Java Sucks and C# Rocks(2):基础类型与面向对象

既然已经谈过这次语言比较的意义与目的,而完整的幻灯片和录音也已经放出,那么接下来自然是详细讨论了.在这篇文章中,我会对 两个语言的基本特征进行简单描述,并主要讨论两者对于基础类型的处理方式.在我看来,Java语言对于基础类型的处理方式,并不如C# 中值类型般妥当.如果您有任何觉得不妥或是想要补充的意见,请不吝回复.由于C# 1.0发布于2002年,因此本文内容将基于Java 1.4及 C# 1.0的情况. Java语言简单描述 Java既是一个完整的平台,也是一门语言.Java语言是1995年由

揭开NTFS下流的奥秘

NTFS下,支持一个特殊概念,那就是'流'.怎么个流法呢?先看'流'的定义: stream A sequence of bits, bytes, or other small structurally uniform units. BIT的序列,或者小的统一结构单元.当然,小的统一结构单元并不意味着一定要大小统一,格式统一. 流依附于文件而存在,你可以在流中存储2进制数据,文字或者其他一些东西.就象文件一样.文件存什么,流就能存什么.每个文件可以含有多个流.但是流又和文件有些不同.每个流的打开需

C++学习从零开始(五)

本文将说明自定义类型剩下的内容,并说明各自的语义. 权限 成员函数的提供,使得自定义类型的语义从资源提升到了具有功能的资源.什么叫具有功能的资源?比如要把收音机映射为数字,需要映射的操作有调整收音机的频率以接收不同的电台;调整收音机的音量;打开和关闭收音机以防止电力的损耗.为此,收音机应映射为结构,类似下面: struct Radiogram { double Frequency; /* 频率 */ void TurnFreq( double value ); // 改变频率 float Vol