C语言学习笔记--C位域&typedef

位域

如果程序的结构中包含多个开关量,只有TRUE/FALSE变量,如下:

struct
{
  unsigned int widthValidated;
  unsigned int heightValidated;
} status;

这种结构需要8字节的内存空间,但实际上,在每个变量中,我们只存储0或1。在这种情况下,C语言提供了一种更好的利用内存空间的方式。如果在结构中使用这样的变量,可以定义变量的宽度来告诉编译器,将只使用这些字节。例如上面的结构可以重写成:

struct
{
  unsigned int widthValidated : 1;
  unsigned int heightValidated : 1;
} status;

现在,上面的结构中,status变量将占用4个字节的内存空间,但是只有2位被用来存储值。如果用了32位变量,每一个变量宽度为1位,那么status结构将使用4个字节,但只要再多用一个变量,如果使用了33个变量,那么它将分配内存的下一段来存储第33个变量,这个时候就开始使用8个字节。
实例

#include <stdio.h>
#include <string.h>
 
/* 定义简单的结构 */
struct
{
  unsigned int widthValidated;
  unsigned int heightValidated;
} status1;
 
/* 定义位域结构 */
struct
{
  unsigned int widthValidated : 1;
  unsigned int heightValidated : 1;
} status2;
 
int main( )
{
   printf( "Memory size occupied by status1 : %d\n", sizeof(status1));
   printf( "Memory size occupied by status2 : %d\n", sizeof(status2));
 
   return 0;
}

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

Memory size occupied by status1 : 8
Memory size occupied by status2 : 4

位域声明

在结构体内声明位域的形式如下:

struct
{
  type [member_name] : width ;
};

下面是有关位域中变量元素的描述:

元素描述
type整数类型,决定了如何解释位域的值,类型可以是整型、有符号整型,无符号整型。
member_name位域的名称
width位域中位的数量。宽度必须小于或等于指定类型的位宽度

带有与定义宽度的变量称为位域。位域可以存储多于1位的数,例如,需要一个变量来存储从0到7的值,可以定义一个宽度为3位的位域,如下:

struct
{
  unsigned int age : 3;
} Age;

上面的结构定义只是C编译器,age变量将只使用3位来存储这个值,如果师徒使用超过3位,则无法完成。
实例:

#include <stdio.h>
#include <string.h>
 
struct
{
  unsigned int age : 3;
} Age;
 
int main( )
{
   Age.age = 4;
   printf( "Sizeof( Age ) : %d\n", sizeof(Age) );
   printf( "Age.age : %d\n", Age.age );
 
   Age.age = 7;
   printf( "Age.age : %d\n", Age.age );
 
   Age.age = 8; // 二进制表示为 1000 有四位,超出
   printf( "Age.age : %d\n", Age.age );
 
   return 0;
}

当上面的代码被编译时,会带有警告,当上面的代码被执行时,会产生下列结果:

Sizeof( Age ) : 4
Age.age : 4
Age.age : 7
Age.age : 0

typedef

C语言提供了typedef关键字,可以使用它来为类型去一个新的名字。下面的实例为单字节数字定义了一个属于BYTE

typedef unsigned char BYTE;

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

BYTE  b1, b2;

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

typedef unsigned char byte;

也可以使用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, "谭浩强"); 
   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 教程
书作者 : 谭浩强
书类目 : 编程语言
书 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

typedef与#define的区别

  1. #define可以使用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能这样做。例如:
#define INTERGE int;
unsigned INTERGE n;  //没问题
typedef int INTERGE;
unsigned INTERGE n;  //错误,不能在 INTERGE 前面添加 unsigned
  1. 在连续定义几个变量的是偶,typedef能够保证定义的所有变量均为同一类型,而#define则无法保证。例如:
#define PTR_INT int *
PTR_INT p1, p2;        //p1、p2 类型不相同,宏展开后变为int *p1, p2;
typedef int * PTR_INT
PTR_INT p1, p2;        //p1、p2 类型相同,它们都是指向 int 类型的指针。

输入与输出

当提到输入时,就意味着要向程序填充一些数据。输入可以是以文件的形式或从命令行中进行。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”(整型)来匹配整型变量testlnteger并输出到屏幕。
%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;
}

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

Enter a value :tanhaoqiang

You entered: t

gets()&puts()函数

char * gets(char * s) 函数从stdio读取一行到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;
}

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

Enter a value :tanhaoqiang

You entered: tanhaoqiang

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;
}

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

Enter a value :tanhaoqiang 123

You entered: tanhaoqiang 123 

在这里,应当指出的是,scanf() 期待输入的格式与您给出的 %s 和 %d 相同,这意味着必须提供有效的输入,比如 “string integer”,如果提供的是 “string string” 或 “integer integer”,它会被认为是错误的输入。另外,在读取字符串时,只要遇到一个空格,scanf() 就会停止读取,所以 “this is test” 对 scanf() 来说是三个字符串。
在进行输出时,若要用到用来输出实数的 f 格式符(以小数形式输出),有以下几种用法:
1、基本型,用 %f
不指定输出类型的长度,用系统根据情况决定,一般是实数中的整数部分全部输出,小数部分输出六位。例:

#include<stdio.h>
int main()
{
    double a=1.0;
    printf("%f\n",a/3);
    return 0;
}

运行结果:0.333333
2、指定数据宽度和小数位数,用 %m.nf
例:将上个程序的双精度变量 a 输出 15 位小数,用 %20.15f 的格式声明,指定输出的数据占 20 列,其中包括 15 位小数。改动上面程序如下:

#include<stdio.h>
int main()
{
    double a=1.0;
    printf("%20.15f\n",a/3);
    return 0;
}

运行结果: 0.333333333333333
注意在 0 的前面有 3 个空格,且双精度数只保证 15 位有效数字的准确性。
3、输出的数据相左对齐,用 %-m.nf
m.n 前加一个负号,其作用与 %m.nf 形式作用基本相同,但当数据长度不长过 m 时,数据向左靠,右端补空格。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值