程序离不开数据。把数字、字母和文字输入计算机,就是希望它利用这些数据完成某些任务。
C语言提供两大系列的多种数据类型。本章详细介绍两大数据类型:整数类型和浮点类型,讲解这些数据类型是什么,如何声明它们、如何以及何时使用它们。除此之外,还将介绍常量和变量的区别。
程序清单3.1 platinum.c程序
/* platinum.c -- your weight in platinum */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
float weight; /* 你的体重 */
float value; /* 相等重量的白金价值 */
printf("Are you worth your weight in platinum?\n");
printf("Let's check it out.\n");
printf("Please enter your weight in pounds: ");
/* 获取用户输入 */
scanf("%f", &weight);
/* 假设白金的价格是每盎司¥1700 */
/* 14.5833用于把英镑常衡盎司转换为金衡盎司 */
value = 1700.0 * weight * 14.5833;
printf("Your weight in platinum is worth $%.2f.\n", value);
printf("You are easily worth that! If platinum price drop,\n");
printf("eat more to maintain your value.\n");
system("pause");
return 0;
}
注意,“Enter your weight”的意思是输入你的体重,然后按下Enter或Return键(不要键入体重后就一直等着)。按下Enter键是告知计算机,你已经完成输入数据。
该程序需要输入一个数字(如,150),而不是单词(如, too much)。如果输入的不是数字,会导致程序出现问题。
VS输出的结果如下:
3.1.1 程序中的新元素
程序清单3.1 中包含了C语言的一些新元素
代码中使用新的变量声明。之前的例子只使用了整数类型的变量(int),但是本例使用了浮点数(float)类型变量,以便处理更大范围的数据。float类型存储带小数的数字。
程序中演示了常量的几种新写法,现在可以使用带小数点的数了。
为了打印新类型的变量,在printf()中使用%f来处理浮点值。%.2f中的.2用于精确控制输出,指定输出的浮点数只显示小数点后面2位。
scanf()函数用于读取键盘的输入。%f说明scanf()要读取用户从键盘输入的浮点数,&weight告诉scanf()把输入的值赋给名为weight的变量。scanf()函数使用&符号表明找到weight变量的地点。
计算机向用户询问信息,然后用户输入数字。交互式使得程序更加灵活。例如,示例程序可以使用任何合理的体重,而不只是150磅。不必重写程序,就可以根据不同的体重进行计算。scanf()和printf()函数用于实现这样的交互。scanf()函数读取用户从键盘输入的数据,并把数据传递给程序;printf()函数读取程序中的数据,并把数据显示在屏幕上。把两个函数结合起来,就可以建立人机双向通信。
3.2 变量与常量
常量:在整个程序的运行过程中没有变化,称为常量。如示例中的14.5833,和1700.0
变量:在程序运行期间可能会改变或被赋值,称为变量。如示例中的weight。
3.3 数据:数据类型关键字
不仅变量与常量不同,不同的数据类型之间也有差异。一些数据类型表示数字,一些数据类型表示字母。C通过识别一些基本的数据类型来区分和使用这些不同的数据类型。
如果数据是常量,编译器一般通过用户书写的形式来识别类型(如,42是整数,42.100是浮点数)。但是,对变量而言,要在声明时指定其类型。
C语言基本类型关键字
最初K&R给出的关键字 | C90标准添加的关键字 | C99标准添加的关键字 |
---|---|---|
int | signed | _Bool |
long | void | _Complex |
short | _Imaginary | |
unsigned | ||
char | ||
float | ||
double |
在C语言中,用int关键字来表示基本的整数类型。后面三个关键字(long、short、和unsigned)和C90新增的signed用于提供基本整数类型的变式,如unsigned short int和long long int。
char关键字用于指定字母和其他字符(如,#、$、%和*),另外,char类型也可以表示较小的整数。
float,double和long double表示带小数点的数
_Bool类型表示布尔值(true或flase)。
Complex和Imaginary分别表示复数和虚数。
3.3.1 整数和浮点数
对用户而言,整数和浮点数的区别是它们的书写方式不同。对计算机而言,它们的区别是储存方式的不同。
3.3.2 整数
在C语言中,整数是没有小数部分的数。如 2,-23和2456都是整数。而3.14、0.22和2.000都不是整数。
计算机以二进制数字存储整数。例如,整数7以2进制写是111。因此要在8位字节中存储该数字,需要把前5位都设置为0,后3位设置为1。
3.3.3 浮点数
2.75、3.16E7、7.00和2e-8都是浮点数。注意,在一个值后面加上一个小数点,该值就成为一个浮点值。所以,7是整数,7.00是浮点数。
这里3.16E7表示3.16 * 10的7次方。
浮点数与整数的存储方式不同。计算机把浮点数分成小数部分和指数部分来表示,而且分开存储这2部分。因此,虽然7.00和7在数值上相同,但是它们的存储方式不同。在10进制下,可以把7.00写成0.7E1。这里0.7是小数部分,1是指数部分。下图演示了一个存储浮点数的例子。
整数与浮点数的区别:
整数没有小数部分,浮点数有小数部分
浮点数表示的范围比整数大
对于一些算术运算(如,两个很大的数相减),浮点数损失的精度更多
因为在任何区间内(如1.0到2.0之间)都存在无穷多个实数,所以计算机的浮点数不能表示区间内所有的值,浮点数通常只是实际值的近似值。例如,7.0可能被存储位浮点数6.99999。
过去,浮点数比整数运算慢。不过现在许多CPU都包含浮点处理器,缩小了速度上的差距。
3.4 C语言基本数据类型
本节介绍C语言的基本数据类型,包括如何声明变量、如何表示字面值常量(如,5或2.78),以及典型的用法。
3.4.1 int类型
C语言提供了许多整数类型,为什么一种类型不够用?因为C语言让程序员针对不同情况选择不同的类型。特别的是,C语言中的整数类型可表示不同的取值范围和正负值。一般情况下使用int类型即可,但是为了满足特定任务和机器的要求,还可以选择其他类型。
int类型是有符号整型,即int类型的值必须是整数,可以是正整数、负整数和0。其取值范围按照计算机系统而异,一般而言,存储一个int要占用一个机器字长。因此,16位计算机int则占用16bit位,取值范围为-32768 ~ + 32767,若是32位系统,则int占用32bit,以此类推。
1、声明int变量
先写上int,然后写变量名,最后加上一个分号。
要声明多个变量,可以单独声明每个变量,也可以在int后面列出多个变量名,变量名之间用逗号隔开。
int erns;
int hogs, cows, goats;
可以分别在4条声明中声明各变量,也可以在一条声明中声明4个变量。两种方法的效果相同,都为4个int大小的变量赋予名称并分配内存空间。
以上声明创建了变量,但是没有给它们提供值。变量如何获得值?
赋值:cows = 112;
通过函数(如 scanf())获得值。
初始化赋值
2、初始化变量
初始化变量就是位变量赋一个初始值。在C语言中,初始化可以直接在声明中完成,只需在变量名后面加上赋值运算符(=)和待赋给变量的值即可。如下所示:
int hogs = 21;
int cows = 32, goats = 14;
int dogs, cats = 94; /* 有效,但是这种格式很糟糕 */
以上示例的最后一行,只初始化了cats,并未初始化dogs。这种写法很容易让人误以为dogs也被初始化为94,所以最好不要把初始化的变量和未初始化的变量放在同一条声明中。
简而言之,声明为变量创建和标记存储空间,并为其指定初始值。
3、int类型常量
上面示例出现的整数(21、32、14和94)都是整型常量或整型字面量。C语言把不含小数点和指数的数作为整数。
4、打印int值
可以使用printf()函数打印int类型的值,程序清单3.2演示了一个简单的程序,程序中初始化了一个变量,并打印该变量的值、一个常量值和一个简单表达式的值。
程序清单3.2 print1.c程序
/* print1.c -- 演示printf()的一些特性 */
#include <stdio.h>
int main(void)
{
int ten = 10;
int two = 2;
printf("Doing it right: ");
printf("%d minus %d is %d\n", ten, 2, ten - two);
printf("Doing it wrong: ");
printf("%d minus %d is %d\n", ten); // 遗漏2个参数
system("pause");
return 0;
}
VS运行的结果如下:
在第一行输出中,第一个%d对应int类型变量ten,第2个%d对应int类型常量2;第3个%d对应int类型表达式ten - two的值。
在第二行输出中,第一个%d对应int类型变量ten,但是由于没有给后两个%d提供任何值,所以打印出来的值是内存中的任意值。
编译器为何不能捕获这种明显的错误,但实际上问题出在printf()不寻常的设计。大部分函数都需要指定数目的参数,编译器会检查参数的数目是否正确。但是printf()函数的参数数目不定,可以有1个、2个、3个或更多,编译器也无法识别。记住,使用printf()函数时,要确保转换说明的数量与待打印的数量相等。
5、八进制和十六进制
C语言都假定整型常量是十进制。然而,许多程序员喜欢使用八进制和十六进制。因为8和16都是2的幂,而10不是。
八进制和十六进制计数系统在表达与计算机相关的值时很方便。例如,十进制的65536。十六进制表示刚好是10000。
在C语言中,用特定前缀表示使用哪种进制。0x或0X表示十六进制,所以十进制16表示成十六进制是0x10或0X10。
0前缀表示8进制。例如,十进制数16表示成八进制是020。
要清楚,使用不同的进制数是为了方便,不会影响被存储的方式。也就是说,无论把数字写成16、020还是0x10,存储该数的方式都相同,因为计算机内部都是以二进制进行编码。
6、显示八进制和十六进制
不同的进制要使用不同的转换说明。
十进制 | %d |
---|---|
八进制 | %o |
十六进制 | %x |
要显示前缀0,需要加上#号键,如、%#o、%#x、%#X。
程序清单3.3 bases.c程序
/* bases.c -- 以十进制、八进制、十六进制打印十进制数100 */
#include <stdio.h>
int main(void)
{
int x = 100;
printf("dec = %d; octal = %o; hex = %x\n", x, x, x);
printf("dec = %d; octal = %#o; hex = %#x\n", x, x, x);
system("pause");
return 0;
}
VS运行结果如下:
八进制和十六进制显示前缀0以及0x前缀,需要在转换说明中加入#
3.4.2 其他整数类型
C语言提供3个附属关键字修饰基本整数类型:short、long和unsigned。
-
short int类型(可简写成short)占用的存储空间可能比int类型少,常用于较小数值的场合以节省空间。与int类似,short是有符号类型。
-
long int或long占用的存储空间可能比int多,适用于较大数值的场景。与int类似,long是有符号类型。
-
long long int或long long占用的存储空间可能比long多,适用于更大数值的场景。该类型至少占64位。与int类似,long long是有符号类型。
-
unsigned int或unsigned只用于非负值的场景。16位的unsigned int允许的取值范围是0~65525,而不是-32768~+32767。用于表示正负号的位现在用于表示另一个2进制位,所以无符号数可以表示更大的数
-
在任何有符号类型前面添加关键字signed,可强调使用有符号类型的意图,可省略。
1、打印各种数据类型
数据类型 | 打印格式 |
---|---|
u8 | %d |
s8 | %d |
u16 | %d or %hu |
s16 | %d or %hd |
u32 | %u |
s32 | %d |
u64 | %llu |
s64 | %lld |
int | %d |
unsigned int | %u |
short int | %d or %hd |
long | %ld |
unsigned long | %lu |
long long | %lld |
unsigned long long | %llu |
char | %c |
char * | %s |
bool (#define stdbool.h) | %d |
unsigned int/int------>十六进制 | %0x |
unsigned long/long---->十六进制 | %0lx |
long long/unsigned long long ----->十六进制 | %0llx |
unsigned int/int------>八进制 | %0o |
unsigned long/long---->八进制 | %0lo |
long long/unsigned long long ----->八进制 | %0llo |
float | %f |
double | %f or %lf |
科学技术类型(必须转化为double类型) | %e |
限制输出字段宽度 | %x.yf (x:整数长度,y:小数点长度) |
3.4.3 使用字符:char类型
char类型用于存储字符(如字母或标点符号),但从技术层面上来看,char是整数类型。因为char类型实际上存储的整数而不是字符。计算机使用数据编码来处理字符,即用特定的数字表示特定的字符。最常用的编码是ASCII编码。
标准的ASCII码的范围是0~127,只需要7位2进制数即可表示。通常,char类型被定位8位的存储单元。
C语言把1字节定义为char类型占用的位数,因此无论是16位还是32位系统,都可以使用char类型。
1、声明char类型变量
char responses;
char itable, latan;
2、字符常量初始化
char grade = 'A';
用单引号括起来的单个字符被称为字符常量。
类似:
char broiled; /* 声明一个char类型的变量 */
broiled = 'T'; /* 为其赋值,正确 */
broiled = T; /* 错误!此时T是一个变量 */
broiled = "T"; /* 错误!此时T是一个字符串 */
3、 非打印字符
单引号值使用与字符、数字和标点符号,ASCII码中,有些ASCII字符无法打印,一些代表行为的字符(退格,换行等)。
下面介绍3中方法打印这些字符
使用ASCII码。例如,蜂鸣字符的ASCII值是7
char beep = 7;使用特殊的符号序列表示一些特殊的字符,被称为转义序列。把转义序列赋给变量时,必须用单引号把转义序列括起来。
char nerf = '\n';- 转义序列可从网上进行查找
4、打印字符
printf()函数使用%c指明待打印的字符。
程序清单3.5 charcode.c程序
/* charcode.c -- 显示字符的代码编号 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
char ch;
printf("Please enter a character.\n");
scanf("%c", &ch);
printf("The code for %c is %d.\n", ch, ch);
system("pause");
return 0;
}
VS的运行结果如下:
scanf()函数会读取用户输入的字符,&符号表示把输入的字符赋给变量ch。
printf()函数打印ch的值两次。第一次打印一个字符(对应代码中的%c),第二次打印一个十进制整数值(对应代码中的%d)。printf()中的转换说明决定了数据的显示方式,而不是数据的存储方式。
3.4.4 _Bool类型
_Bool类型用于表示布尔值,即逻辑值true和false。C语言用值1表示true,值0表示fasle,所以 _Bool类型实际上也是一种整数类型,但原则上它仅占用1位存储空间。
3.4.5 float、double、和long double
面向金融和数学的程序经常使用浮点数,C语言中的浮点类型有float、double、和long double类型。
float类型必须至少能表示6位有效数字,且取值范围至少是10的-37次方到10的37次方。前一项规定float类型必须至少精确表示小数点后的6位有效数字,如33.333333。
系统存储一个浮点数要占用32位。其中8位用于表示指数的值和符号,剩下的24位表示非指数部分及其符号
C语言提供的另一种浮点类型是double(双精度)。double类型和float类型的最小取值范围相同,但至少必须能表示10位有效数字。
一般情况下,double占用64位。
C语言的第三种浮点类型是long double,以满足比double类型更高的精度要求。
1、声明浮点变量
float noah, jonah;
double trouble;
float planck = 6.63e-34;
long double gnp;
2、 浮点型常量
-1.56E+12
2.87e-3
3、打印浮点值
参考上述表格
程序清单3.7 showf_pt.c程序
/* showf_pt.c -- 以两种方式显示float类型的值 */
#include <stdio.h>
int main(void)
{
float aboat = 32000.0;
double abet = 2.14e9;
long double dip = 5.32e-5;
printf("%f can be written %e\n", aboat, aboat);
// 下一行要求编译器支持C99或其中相关特性
printf("And it's %a in hexadecimal, powers of 2 notation\n", aboat);
printf("%f can be written %e\n", abet, abet);
printf("%f can be written %Le\n", dip, dip);
system("pause");
return 0;
}
VS运行结果如下:
3.4.6 类型大小
程序清单3.8 typesize.c程序
/* typeseize.c -- 打印类型大小 */
#include <stdio.h>
int main(void)
{
printf("Type int has a size of %d bytes", sizeof(int));
printf("Type char has a size of %d bytes", sizeof(char));
printf("Type long has a size of %d bytes", sizeof(long));
printf("Type long long has a size of %d bytes", sizeof(long long));
printf("Type double has a size of %d bytes", sizeof(double));
printf("Type long double has a size of %d bytes", sizeof(long double));
system("pause");
return 0;
}
VS运行结果如下:
sizeof是C语言的内置运算符,以字节为单位给出指定类型的大小。
3.5 使用数据类型
通常用int或float类型表示数字,char类型表示字符。在使用变量之前必须先声明,并选择有意义的变量名。初始化变量应使用与变量类型匹配的常数类型。
int apples = 3;
int oranges = 3.0 /* 不好的形式 */
把一个类型的值初始化给不同类型的变量时,编译器会把值转换成与变量匹配的类型,浙江导致部分数据丢失。
int cost = 12.99 /* 用double类型的值初始化int类型的变量 */
float pi = 3.1415926536; /* 用double类型的值初始化float类型的变量 */
第一个声明,cost的值是12。C编译器把浮点数转换成整数时,会直接丢弃小数部分,而不进行四舍五入。
第二个声明会损失一点精度,因为C只保证float类型前6位的精度。
3.6 转义序列示例
下例演示了退格(\b)、水平制表符(\t)和回车的工作方式。
程序清单3.10 escape.c程序
/* escape.c -- 使用转义序列 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
float salary;
printf("\aEnter your desired monthly salary:"); /* 1 */
printf(" $______\b\b\b\b\b\b"); /* 2 */
scanf("%f", &salary);
printf("\n\t$%.2f a month is $%.2f a year.", salary, salary * 12); /* 3 */
printf("\rGee!\n"); /* 4 */
system("pause");
return 0;
}
VS运行结果如下:
3.6.1 程序运行情况
第1条printf()语句发出一声警报(因为使用了\a),然后打印以下内容:
Enter your desired monthly salary:
因为printf()中的字符串末尾没有\n,所以光标停留在冒号后面。
第2条printf()语句在光标处接着打印,屏幕上显示的内容是
Enter your desired monthly salary: $__
冒号和美元符号之间有个空格,这是因为第2条printf()语句中的字符串以一个空格开始。7个退格字符使得光标左移7个位置,即把光标移至7个下划线字符的前面,紧跟在美元符号的后面。
假设键入的数据是4000.00(并按下Enter键),屏幕显示的内容是:
3.7 复习题
1、 指出下面各种数据使用的合适数据类型
East Simpleton的人口:unsigned int
DVD影碟的价格:float
本章出现次数最多的字母:char
本章出现次数最多的字母的次数:unsigned int
2、在什么情况下要用long类型的变量替代int类型的变量
当整型变量的值已经超过int最大取值范围时,需要用long类型来存储其值。
3、使用哪些可移植的数据类型可以获得32位有符号整数?选择的理由是什么?
int32_t
4、 指出下列常量的类型和含义
'\b':char 退格符
1066 int
99.44 float
0XAA int
2.0e30 float
5、Dottie Cawm编写了一个程序,请找出程序中的错误
include <stdio.h>
main
(
float g; h;
float tax, rate;
g = e21;
tax = rate*g;
)
6、写出下列常量在声明中使用的数据类型和在printf()中对应的转换说明:
常量 | 类型 | 转换说明(%转换字符) |
---|---|---|
12 | int | %d |
0X3 | int | %#x |
'C' | char | %c |
2.34E07 | float | %e |
'\040' | char | %oc |
7.0 | float | %.1f |
6L | long | %ld |
6.0f | float | %.1f |
0x5.b6p12 |
7、 写出下列常量在声明中使用的数据类型和在printf()中对应的转换说明(假设int为16位):
常量 | 类型 | 转换说明(%转换字符) |
---|---|---|
012 | int | %#o |
2.9e05L | float | %#x |
's' | char | %c |
100000 | long | %ld |
'\n' | char | %c |
20.0f | float | %.1f |
0x44 | int | %#x |
-40 | int | %df |
8、假设程序的开头有下列声明:
int imate = 2;
long shot = 53456;
char grade = 'A';
float log = 2.71828;
把下面printf()语句中的转换字符补充完整:
printf("The odds against the %d, were %ld to 1.\n", imate, shot);
printf("A score of %.5f is not an %c grade.\n", log, grade);
9、 假设ch是char类型的变量,分别使用转义序列、十进制值、八进制字符常量和十六进制字符常量把回车字符赋值给ch。
10、修正下面的程序(在C中, / 表示除以)
void main(int) / this program is prefect /
{
cows, legs, integer;
printf("How many cow legs did you count?\n");
scanf("%c", legs);
cows = legs / 4;
printf("Thar implies there are %f cows.\n", cows);
}
修正后:
#include <stdio.h>
int main(void) /* this program is prefect */
{
float cows, legs, integer;
printf("How many cow legs did you count?\n");
scanf("%f", &legs);
cows = legs / 4;
printf("Thar implies there are %f cows.\n", cows);
return 0;
}
11、指出下列转义序列的含义:
\n 换行符
\\ 反斜线
\" 双引号
\t 制表符
3.8 编程练习
1、通过实验(即编写带有此类问题的程序)观察系统如何处理整数上溢、浮点数上溢和浮点数下溢的情况。
2、编写一个程序,要求提示输入一个ASCII值(如,值66),然后打印输入的字符。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
int num;
printf("Please enter a num.\n");
scanf("%d", &num);
printf("The code for %d is %c.\n", num, num);
system("pause");
return 0;
}
VS运行的结果如下:
3、 编写一个程序,发出一声警报,然后打印下面的文本:
Startled by the sudden sound, Sally shouted,
"By the Great Pumpkin, what was that!"
#include <stdio.h>
int main(void)
{
printf("\aStartled by the sudden sound, Sally shouted,\n\"By the Great Pumpkin, what was that!\"\n");
system("pause");
return 0;
}
VS运行结果如下:
5、 一年大约有3.156*10的7次方秒。编写一个程序,提示用户输入年两,然后显示该年龄对应的秒数。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
int age;
float seconds = 3.156e7;
printf("Please enter your age: ");
scanf("%d", &age);
printf("Total seconds:%e\n", seconds * age);
system("pause");
return 0;
}
VS运行结果如下:
6、1个水分子的质量约为3.0 * 10负23次方克。1夸脱水大约是950克。编写一个程序,提示用户输入水的夸脱数,并显示水分子的数量。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
int water;
printf("Please Enter water: ");
scanf("%d", &water);
printf("Total weight:%e %d\n", 3.0e-23 * 950 * water, 950 * water);
system("pause");
return 0;
}
VS运行结果如下:
7、1英寸相当于2.54厘米。编写一个程序,提示用户输入身高(/英寸),然后以厘米为单位显示身高。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
float height;
printf("Please Enter your height(yingcun): ");
scanf("%f", &height);
printf("You height is %f cm.\n", 2.54 * height);
system("pause");
return 0;
}
VS运行结果如下:
8、在美国的体积测量系统中,1品脱等于2杯,1杯等于8盎司,1盎司等于2大汤勺,1大汤勺等于3茶勺。编写一个程序,提示用户输入杯数,并以品脱、盎司、汤勺、茶勺为单位显示等价容量。思考对于该程序,微核使用浮点数比整数类型更合适?
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
float beishu;
printf("Please Enter bei shu: ");
scanf("%f", &beishu);
printf("%f beishu = %f pingtuo.\n", beishu, beishu / 2.0);
printf("%f beishu = %f anngsi.\n", beishu, beishu * 8.0);
printf("%f beishu = %f tangshao.\n", beishu, beishu * 8.0 * 2.0);
printf("%f beishu = %f chashao.\n", beishu, beishu * 8.0 * 2.0 * 3.0);
system("pause");
return 0;
}
VS运行结果如下: