如果程序的结构中包含多个开关量,只有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
#include
/* 定义简单的结构 */
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
#include
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
C语言提供了typedef关键字,可以使用它来为类型去一个新的名字。下面的实例为单字节数字定义了一个属于BYTE:
typedef unsigned char BYTE;
在这个类型定义之后,标识符BYTE可作为类型unsigned char的缩写,例如:
BYTE b1, b2;
按照惯例,定义时会大写字母,以便提醒用户类型名称是一个象征性的缩写,但也可以使用小写字母,如下:
typedef unsigned char byte;
也可以使用typedef来为用户自定义的数据类型去一个新名字。例如,可以对结构体使用typedef来定义一个新的数据类型名字,然后使用这个新的数据类型来定义结构变量,如下:
#include
#include
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
#define是C指令,用于为各种数据类型定义别名,与typedef类似,但是有以下几点不同:
#include
#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的区别
#define INTERGE int;
unsigned INTERGE n; //没问题
typedef int INTERGE;
unsigned INTERGE n; //错误,不能在 INTERGE 前面添加 unsigned
#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 // 执行 printf() 函数需要该库
int main()
{
printf("C语言"); //显示引号中的内容
return 0;
}
编译并执行以上程序,输出结果为:
C语言
实例解析:
#include
int main()
{
int testInteger = 5;
printf("Number = %d", testInteger);
return 0;
}
编译以上程序,输出结果为:
Number = 5
在printf()函数的引号中使用“%d”(整型)来匹配整型变量testlnteger并输出到屏幕。
%f 格式化输出浮点型数据
#include
int main()
{
float f;
printf("Enter a number: ");
// %f 匹配浮点型数据
scanf("%f",&f);
printf("Value = %f", f);
return 0;
}
int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内会读取一个单一的字符。可以在循环内使用这个方法,以便从屏幕上读取多个字符。
int putchar(int c) 函数把字符输入到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。可以在循环内使用这个方法,以便在屏幕上输出多个字符。
实例:
#include
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
char * gets(char * s) 函数从stdio读取一行到s所指向的缓冲区,知道一个终止符或EOF。
int puts(const char * s) 函数把字符串s和一个尾随的换行符写入到stdout。
实例:
#include
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
int scanf(const char * format, …) 函数从标准输入流stdin读取输入,并根据提供的format来浏览输入。
int printf(const char * format, …) 函数吧输出写入到标准输出流stdout,并根据提供的格式产生输出。
format可以是一个简单的常量字符串,但是可以分别制定%s、%d、%c、%f 等来输出或读取字符串、整数、字符或浮点数。还有许多其他可用的格式选项,可以根据需要使用。现在通过下面这个简单的实例来加深理解:
#include
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
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
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 时,数据向左靠,右端补空格。