共用体

共用体允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。

定义共用体

为了定义共用体,您必须使用 union 语句,方式与定义结构体类似。union 语句定义了一个新的数据类型,带有多个成员。union 语句的格式如下:

union [union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];

union tag 是可选的,每个 member definition 是标准的变量定义,比如 int i; 或者 float f; 或者其他有效的变量定义。在共用体定义的末尾,最后一个分号之前,您可以指定一个或多个共用体变量,这是可选的。下面定义一个名为 Data 的共用体类型,有三个成员 i、f 和 str:

union Data
{
int i;
float f;
char str[20];
} data;

现在,Data 类型的变量可以存储一个整数、一个浮点数,或者一个字符串。这意味着一个变量(相同的内存位置)可以存储多个多种类型的数据。您可以根据需要在一个共用体内使用任何内置的或者用户自定义的数据类型。

共用体占用的内存应足够存储共用体中最大的成员。例如,在上面的实例中,Data 将占用 20 个字节的内存空间,因为在各个成员中,字符串所占用的空间是最大的。下面的实例将显示上面的共用体占用的总内存大小:

#include <stdio.h>
#include <string.h> union Data
{
int i;
float f;
char str[20];
}; int main( )
{
union Data data; printf( "Memory size occupied by data : %d\n", sizeof(data)); return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Memory size occupied by data : 20

访问共用体成员

为了访问共用体的成员,我们使用成员访问运算符(.)。成员访问运算符是共用体变量名称和我们要访问的共用体成员之间的一个句号。您可以使用 union 关键字来定义共用体类型的变量。

#include <stdio.h>
#include <string.h> union Data
{
int i;
float f;
char str[20];
}; int main( )
{
union Data data; data.i = 10;
data.f = 220.5;
strcpy( data.str, "C Programming"); printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str); return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

data.i : 1917853763

data.f : 4122360580327794860452759994368.000000

data.str : C Programming

在这里,我们可以看到共用体的 if 成员的值有损坏,因为最后赋给变量的值占用了内存位置,这也是 str 成员能够完好输出的原因。现在让我们再来看一个相同的实例,这次我们在同一时间只使用一个变量,这也演示了使用共用体的主要目的:

#include <stdio.h>
#include <string.h> union Data
{
int i;
float f;
char str[20];
}; int main( )
{
union Data data; data.i = 10;
printf( "data.i : %d\n", data.i); data.f = 220.5;
printf( "data.f : %f\n", data.f); strcpy( data.str, "C Programming");
printf( "data.str : %s\n", data.str); return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

data.i : 10

data.f : 220.500000

data.str : C Programming

在这里,所有的成员都能完好输出,因为同一时间只用到一个成员。

typedef

typedef 关键字,您可以使用它来为类型取一个新的名字。下面的实例为单字节数字定义了一个术语 BYTE

typedef unsigned char BYTE;

在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如:

BYTE  b1, b2;

按照惯例,定义时会大写字母,以便提醒用户类型名称是一个象征性的缩写,但您也可以使用小写字母。

您也可以使用 typedef 来为用户自定义的数据类型取一个新的名字。例如,您可以对结构体使用 typedef 来定义一个新的数据类型名字,然后使用这个新的数据类型来直接定义结构变量,如下:

#include <stdio.h>
#include <string.h> typedef struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} Book; int main( )
{
Book book; strcpy( book.title, "C 教程");
strcpy( book.author, "Runoob");
strcpy( book.subject, "编程语言");
book.book_id = 12345; printf( "书标题 : %s\n", book.title);
printf( "书作者 : %s\n", book.author);
printf( "书类目 : %s\n", book.subject);
printf( "书 ID : %d\n", book.book_id); return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

书标题 : C 教程

书作者 : Runoob

书类目 : 编程语言

书 ID : 12345

typedef vs #define

#define 是 C 指令,用于为各种数据类型定义别名,与 typedef 类似,但是它们有以下几点不同:

  • typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。
  • typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。

下面是 #define 的最简单的用法:

#include <stdio.h>

#define TRUE  1
#define FALSE 0 int main( )
{
printf( "TRUE 的值: %d\n", TRUE);
printf( "FALSE 的值: %d\n", FALSE); return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

TRUE 的值: 1

FALSE 的值: 0

输入&输出

当我们提到输入时,这意味着要向程序填充一些数据。输入可以是以文件的形式或从命令行中进行。C 语言提供了一系列内置的函数来读取给定的输入,并根据需要填充到程序中。

当我们提到输出时,这意味着要在屏幕上、打印机上或任意文件中显示一些数据。C 语言提供了一系列内置的函数来输出数据到计算机屏幕上和保存数据到文本文件或二进制文件中。

标准文件

C 语言把所有的设备都当作文件。所以设备(比如显示器)被处理的方式与文件相同。以下三个文件会在程序执行时自动打开,以便访问键盘和屏幕。

标准文件 文件指针 设备
标准输入 stdin 键盘
标准输出 stdout 屏幕
标准错误 stderr 您的屏幕

文件指针是访问文件的方式,本节将讲解如何从屏幕读取值以及如何把结果输出到屏幕上。

C 语言中的 I/O (输入/输出) 通常使用 printf() 和 scanf() 两个函数。

scanf() 函数用于从标准输入(键盘)读取并格式化, printf() 函数发送格式化输出到标准输出(屏幕)。

#include <stdio.h>      // 执行 printf() 函数需要该库
int main()
{
printf("c语言教程"); //显示引号中的内容
return 0;
}

编译以上程序,输出结果为:

c语言教程

实例解析:

  • 所有的 C 语言程序都需要包含 main() 函数。 代码从 main() 函数开始执行。
  • printf() 用于格式化输出到屏幕。printf() 函数在 "stdio.h" 头文件中声明。
  • stdio.h 是一个头文件 (标准输入输出头文件) , #include 是一个预处理命令,用来引入头文件。 当编译器遇到 printf() 函数时,如果没有找到 stdio.h 头文件,会发生编译错误。
  • return 0; 语句用于表示退出程序。

%d 格式化输出整数

#include <stdio.h>
int main()
{
int testInteger = 5;
printf("Number = %d", testInteger);
return 0;
}

编译以上程序,输出结果为:

Number = 5

在 printf() 函数的引号中使用 "%d" (整型) 来匹配整型变量 testInteger 并输出到屏幕。

%f 格式化输出浮点型数据

#include <stdio.h>
int main()
{
float f;
printf("Enter a number: ");
// %f 匹配浮点型数据
scanf("%f",&f);
printf("Value = %f", f);
return 0;
}

getchar() & putchar() 函数

int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。

int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。您可以在循环内使用这个方法,以便在屏幕上输出多个字符。

请看下面的实例:

#include <stdio.h>

int main( )
{
int c; printf( "Enter a value :");
c = getchar( ); printf( "\nYou entered: ");
putchar( c );
printf( "\n");
return 0;
}

当上面的代码被编译和执行时,它会等待您输入一些文本,当您输入一个文本并按下回车键时,程序会继续并只会读取一个单一的字符,显示如下:

$./a.out

Enter a value :runoob

You entered: r

gets() & puts() 函数

char *gets(char *s) 函数从 stdin(键盘) 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF。

int puts(const char *s) 函数把字符串 s 和一个尾随的换行符写入到 stdout(屏幕)。

#include <stdio.h>

int main( )
{
char str[100]; printf( "Enter a value :");
gets( str ); printf( "\nYou entered: ");
puts( str );
return 0;
}

当上面的代码被编译和执行时,它会等待您输入一些文本,当您输入一个文本并按下回车键时,程序会继续并读取一整行直到该行结束,显示如下:

$./a.out

Enter a value :runoob

You entered: runoob

scanf() 和 printf() 函数

int scanf(const char *format, ...) 函数从标准输入流 stdin 读取输入,并根据提供的 format 来浏览输入。

int printf(const char *format, ...) 函数把输出写入到标准输出流 stdout ,并根据提供的格式产生输出。

format 可以是一个简单的常量字符串,但是您可以分别指定 %s、%d、%c、%f 等来输出或读取字符串、整数、字符或浮点数。还有许多其他可用的格式选项,可以根据需要使用。通过下面这个简单的实例来加深理解:

#include <stdio.h>
int main( ) { char str[100];
int i; printf( "Enter a value :");
scanf("%s %d", str, &i); printf( "\nYou entered: %s %d ", str, i);
printf("\n");
return 0;
}

当上面的代码被编译和执行时,它会等待您输入一些文本,当您输入一个文本并按下回车键时,程序会继续并读取输入,显示如下:

$./a.out

Enter a value :runoob 123

You entered: runoob 123

在这里,应当指出的是,scanf() 期待输入的格式与您给出的 %s 和 %d 相同,这意味着您必须提供有效的输入,比如 "string integer",如果您提供的是 "string string" 或 "integer integer",它会被认为是错误的输入。另外,在读取字符串时,只要遇到一个空格,scanf() 就会停止读取,所以 "this is test" 对 scanf() 来说是三个字符串。


参考自:https://www.runoob.com/cprogramming/c-tutorial.html

C语言笔记 09_共用体&typedef&输入|输出的更多相关文章

  1. C语言中的共用体(union)和枚举(enum)

    1 union union Data{ int i; char ch; float f; }a={1, 'a', 1.5}; //错误 union Data a = {16}; //正确 union ...

  2. (转)C语言union(联合体 共用体)

    一直以来,union都是个很少用到的东西,对于这些不常用的结构往往记不住.这次看书又看到了,还是学习一下吧.一般在Windows API的一些数据结构中才能看到这个union,其实并不复杂.本质上来说 ...

  3. C语言笔记(结构体与offsetof、container_of之前的关系)

    关于结构体学习,需要了解:结构体的定义和使用.内存对齐.结构体指针.得到结构体元素的偏移量(offsetof宏实现) 一.复习结构体的基本定义和使用 typedef struct mystruct { ...

  4. C语言笔记之结构体

    结构的本质是C语言的一种数据抽象,通俗的说,是基本数据类型的重组. 为什么要重组呢?由于基本数据类型不够用了.为什么不够用了呢?由于须要的信息类型太多了. 这是一个非常大的话题.信息本来是没有什么类型 ...

  5. 【C语言笔记】#define与typedef的区别

    1.#define define是预处理指令,在编译时不进行任何检查,只进行简单的替换 宏定义的一般形式为: #define 宏名 字符串 这里所说的字符串是一般意义上的字符序列,不要和C语言中的字符 ...

  6. [C#.NET 拾遗补漏]14:使用结构体实现共用体

    在 C 和 C# 编程语言中,结构体(Struct)是值类型数据结构,它使得一个单一变量可以存储多种类型的相关数据.在 C 语言中还有一种和结构体非常类似的语法,叫共用体(Union),有时也被直译为 ...

  7. 瘋子C语言笔记(结构体/共用体/枚举篇)

    (一)结构体类型 1.简介: 例: struct date { int month; int day; int year; }; struct student { int num; char name ...

  8. C语言基础 (11) 结构体 ,共用体 枚举 typedef

    1 课堂回顾 作用域与生命周期 2 static 局部变量 2 打字游戏 3 内存分区代码分析 4 结构体基本操作 (复合类型[自定义类型 #include <stdio.h> #incl ...

  9. C++学习笔记(七)--共用体、枚举、typedef

    1.共用体 union其定义与结构体类似:union 类型名{ 成员表列;};声明变量的方法也类似: a. union 类型名{            b. union { c.类型名 变量名; 成员 ...

随机推荐

  1. java多线程上篇(三) -- 进程通信和线程死锁简单介绍

    进程通信指的是进程间的信息交换 ,IPC(Inter-Process Communication,进程间通信) 进程通信就相当于一种工作方式.沟通形式,进程通信主要指的就是操作系统提供的进程通信工具( ...

  2. 1205: 求一元二次方程的实数根(C)

    一.题目 acm.wust.edu.cn/problem.php?id=1205&soj=0 二.分析 一元二次方程有三个系数a.b.c,两个根x1.x2,以及d(德尔塔): a.b.c均为实 ...

  3. C++ std::string 在一个字符串前插入一个字符串几种方式

    目录 1.直接使用字符串相加 2.使用insert函数 比较:通过Quick C++ Benchmarks 可得到结果 1.直接使用字符串相加 std::string a = "hello& ...

  4. Intercity Travelling CodeForces - 1009E (组合计数)

    大意: 有一段$n$千米的路, 每一次走$1$千米, 每走完一次可以休息一次, 每连续走$x$次, 消耗$a[1]+...+a[x]$的能量. 休息随机, 求消耗能量的期望$\times 2^{n-1 ...

  5. C#对象转换工具类

    using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Sy ...

  6. (五)Activiti之获取流程定义图片和流程定义删除

    一.获取流程定义图片 /** * 通过流程部署ID获取流程图图片 */ @Test public void getImageById()throws Exception{ InputStream in ...

  7. Asp.Net Mvc项目添加WebApi

    1.添加一个WebApi 空项目 2.删除WebApi项目下的 Global.asax 文件,因为我们要把WebApi项目整合到Mvc项目中去,全局只需要一个Global 3.修改 WebApi 项目 ...

  8. 帝国cms“建立目录不成功,请检查目录权限”的解决方法

    就这个看似简单的问题我折腾了两天,百度看产生这个问题的原因有很多也很宽泛,大部分说的是初始化内置数据,但我出现“建立目录不成功,请检查目录权限”的原因估计只有少部分人会遇到. 内置初始化数据是你上传文 ...

  9. Django admin 外键关联默认显示用户的username

    使用默认User表.默认显示用户username,转换成get_full_name() /home/labsmith/venv_labsmit/lib/python3.6/site-packages/ ...

  10. CPCT精细化运营:客户、产品、渠道、时机

    关键词:CPCT.精细化运营思维.客户.产品.渠道.时机.运营 电信运营商市场饱和,用户新增主要靠弃卡后重新入网以及异网用户策反. 用户新增已如此艰难,所以更加关注存量用户经营. 运营商营销资源不断收 ...