示例程序
#include <stdio.h>
int main(void)
{
float weight = 0.0f;
float value = 0.0f;
printf("Are you worth your weight in paltinum?\n");
printf("Let's check it out.\n");
printf("Please enter your weight in pounds: ");
scanf("%f", &weight);
value = 1700.0 * weight * 14.5833;
printf("Your weight in platinum is worth $%.2f.\n", value);
printf("You are easily worth that! If platinum prices drop,\n");
printf("eat more to maintain your value.\n");
return 0;
}
scanf()函数用于读取键盘的输入。%f说明scanf()要读取用户从键盘输入的浮点数,&weight告诉scanf()把输入的值赋给名为weight的变量。
变量与常量数据
有些数据类型在程序使用之前已经预先设定好了,在整个程序的运行过程中没有变化,这些称为常量。 其他数据类型在程序运行期间可能会改变或被赋值,这些称为变量。
数据:数据类型关键字
位、字节和字
最小的存储单位是位(bit) ,可以存储0或1。位是计算机内存的基本构件块。 字节(byte)是常用的计算机存储单位。1byte=8bit 字(word)是设计计算机时给定的自然存储单位。 对于8位的微型计算机,1个字长只有8位。从那以后,个人计算机字长增至16位、32位,直到目前的64位。计算机的字长越大,其数据转移越快,允许的内存访问也更多。
整数和浮点数
对我们而言,整数和浮点数的区别是它们的书写方式不同。对计算机而言,它们的区别是储存方式不同。 在C语言中,整数是没有小数部分的数。计算机以二进制数字储存整数。 在一个值后面加上一个小数点,该值就成为一个浮点值,7是整数,7.00是浮点数。 计算机把浮点数分成小数部分和指数部分来表示,而且分开储存这两部分。 浮点数可以表示的范围比整数大。对于一些算术运算(如,两个很大的数相减),浮点数损失的精度更多。 因为在任何区间内都存在无穷多个实数,所以计算机的浮点数不能表示区间内所有的值。浮点数通常只是实际值的近似值。
C语言基本数据类型
int类型
int类型是有符号整型,int的取值范围最小为-32768~32767。
声明int变量
int rens;
int hogs, cows, goats;
可以分别在4条声明中声明各变量,也可以在一条声明中声明4个变量。两种方法的效果相同,都为4个int大小的变量赋予名称并分配内存空间。
初始化变量
int hogs = 21;
int cows = 32, goats = 14;
int dogs, cats = 94;
为变量创建和标记存储空间,并为其指定初始值
int类型常量
C语言把不含小数点和指数的数作为整数。C语言把大多数整型常量视为int类型,但是非常大的整数除外。
打印int值
#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);
return 0;
}
第一行输出中,第一个**%d对应的是int类型变量ten,第二个%d对应的是int类型常量2,第三个%d对应的是int** 类型表达式ten-two的值。 第二行输出中,第一个%d对应的是ten的值,后两个%d没有提供值,打印任意值。 使用printf()函数时,要确保转换说明的数量与待打印值的数量相等。
八进制和十六进制
8和16都是2的幂,十六(八)进制数的每一位的数恰好由4(3)位二进制数表示,0x或0X前缀表示十六进制值,0前缀表示八进制. 使用不同的进制数是为了方便,不会影响数被储存的方式。计算机内部都以二进制进行编码。
显示八进制和十六进制
以十进制显示数字,使用*%d*;以八进制显示数字,使用*%o*;以十六进制显示数字,使用*%x*。 另外,要显示各进制数的前缀0、0x和0X,必须分别使用*%#o*、%#x、%#X。
#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);
return 0;
}
其他整数类型
short int类型(或者简写为short)占用的存储空间可能比int类型少,常用于较小数值的场合以节省空间。与int类似,short是有符号类型。 long int或long占用的存储空间可能比int多,适用于较大数值的场合。与int类似,long是有符号类型。 long long int或long long(C99标准加入)占用的储存空间可能比long多,适用于更大数值的场合。该类型至少占64位。与int类似,long long是有符号类型。 unsigned int或unsigned只用于非负值的场合。这种类型与有符号类型表示的范围不同。用于表示正负号的位现在用于表示另一个二进制位,所以无符号整型可以表示更大的数。 在任何有符号类型前面添加关键字signed,可强调使用有符号类型的意图。
声明其他整数类型
long int estine;
long johns;
short int rens;
unsigned int s_count;
unsigned players;
unsigned long headcount;
unsigned short yesvotes;
long long ago;
使用多种整数类型的原因
C语言只规定了short占用的存储空间****不能多于int,long占用的存储空间****不能少于int。 现在,个人计算机上最常见的设置是,long long占64位,long占32位,short占16位,int占16位或32位(依计算机的自然字长而定)。 如果一个数超出了int类型的取值范围,且在long类型的取值范围内时,使用long类型。然而,对于那些long占用的空间比int****大的系统,使用long类型会减慢运算速度。因此,如非必要,请不要使用long类型。另外要注意一点:如果在long类型和int类型占用空间相同的机器上编写代码,当确实需要32位的整数时,应使用long类型而不是int类型,以便把程序移植到16位机后仍然可以正常工作。 如果在int设置为32位的系统中要使用16位的值,应使用short类型以节省存储空间。通常,只有当程序使用相对于系统可用内存较大的整型数组时,才需要重点考虑节省空间的问题。使用short类型的另一个原因是,计算机中某些组件使用的硬件寄存器是16位。
long常量和long long 常量
把一个较小的常量作为long类型对待,可以在值的末尾加上l(小写的L)或L后缀。 在支持long long类型的系统中,也可以使用ll或LL后缀来表示long long类型的值。 u或U后缀表示unsigned long long。如5ull、10LLU、6LLU或9Ull。
整数溢出
#include <stdio.h>
int main(void)
{
int i = 2147483647;
unsigned int j = 4294967295;
printf("%d %d %d\n", i, i + 1, i + 2);
printf("%u %u %u\n", j, j + 1, j + 2);
return 0;
}
超过最大值时,unsigned int 类型的变量 j 从 0开始;而int类型的变量i则从?2147483648开始。
打印short、long、long long和unsigned类型
打印unsigned int类型的值,使用**%u转换说明;打印long类型的值,使用%ld转换说明。 %lx表示以十六进制格式打印long类型整数,%lo表示以八进制格式打印long类型整数。 对于short类型,可以使用h前缀**。%hd表示以十进制显示short类型的整数,%ho表示以八进制显示short类型的整数。
#include <stdio.h>
int main(void)
{
unsigned int un = 3000000000;
short end = 200;
long big = 65537;
long long verybig = 12345678908642;
printf("un = %u and not %d\n", un, un);
printf("end = %hd and %d\n", end, end);
printf("big = %ld and not %hd\n", big, big);
printf("verybig = %lld and not %ld\n", verybig, verybig);
return 0;
}
该例表明,使用错误的转换说明会得到意想不到的结果。如果告诉printf()该数是无符号数,它打印一个值;如果告诉它该数是有符号数,它将打印另一个值。在待打印的值大于有符号值的最大值时,会发生这种情况。对于较小的正数(如96),有符号和无符号类型的存储、显示都相同。 对于short类型的变量end,在printf()中无论指定以short类型(%hd)还是int类型(%d)打印,打印出来的值都相同。这是因为在给函数传递参数时,C编译器把short类型的值自动转换成int类型的值。 int类型被认为是计算机处理整数类型时最高效的类型。因此,在short和int类型的大小不同的计算机中,用int类型的参数传递速度更快。使用h修饰符可以显示较大整数被截断成 short 类型值的情况。 在使用 printf()函数时,切记检查每个待打印值都有对应的转换说明,还要检查转换说明的类型是否与待打印值的类型相匹配。
使用字符:char类型
char类型用于储存字符,但是从技术层面看,char是整数类型。因为char类型实际上储存的是整数而不是字符。计算机使用数字编码来处理字符,即用特定的整数表示特定的字符。C语言把1字节定义为char类型占用的位(bit)数。
1.声明char类型变量
char response;
char itable, latan;
2.字符常量和初始化
char grade = 'A';
char grade = 65;
C语言将字符常量视为int类型而非char类型。利用字符常量的这种特性,可以定义一个字符常量’FATE’,即把4个独立的8位ASCII码储存在一个32位存储单元中。如果把这样的字符常量赋给char类型变量grade,只有最后8位有效。因此,grade的值是’E’。
3.非打印字符
单引号只适用于字符、数字和标点符号,浏览ASCII表会发现,有些ASCII字符打印不出来。 C语言提供了3种方法表示这些字符 使用ASCII码
char beep = 7;
使用特殊的符号序列表示一些特殊的字符。这些符号序列叫做转义序列
char nerf = '\n';
printf("Gramps sez, \"a \\ is a backslash.\"\n");
打印Gramps sez, “a \ is a backslash.”
beep = '\007';
无论是普通字符还是转义序列,只要是双引号括起来的字符集合,就无需用单引号括起来。 如printf(“Hello!\007\n”);中就嵌入了\007。
打印字符
#include <stdio.h>
int main(void)
{
char ch = 0;
printf("Please enter a character.\n");
scanf("%c", &ch);
printf("The code for %c is %d.\n", ch, ch);
return 0;
}
运行该程序时,在输入字母后不要忘记按下Enter键。随后,scanf()函数会读取用户输入的字符,&符号表示把输入的字符赋给变量ch。接着,printf()函数打印ch的值两次,第1次打印一个字符(对应代码中的%c),第2次打印一个十进制整数值(对应代码中的%d)。注意,printf()函数中的转换说明决定了数据的显示方式,而不是数据的储存方式
有符号还是无符号
有些C编译器把char实现为有符号类型,这意味着char可表示的范围是-128~127。而有些C编译器把char实现为无符号类型,那么char可表示的范围是0~255。请查阅相应的编译器手册,确定正在使用的编译器如何实现char类型。无论编译器默认char是什么类型,signed char表示有符号类型,而unsigned char表示无符号类型。这在用char类型处理小整数时很有用。如果只用char处理字符,那么char前面无需使用任何修饰符。
_Bool类型
C99标准添加了**_Bool类型,用于表示布尔值**,即逻辑值true和false。因为C语言用值1表示true,值0表示false,所以_Bool类型实际上也是一种整数类型。但原则上它仅占用1位存储空间,因为对0和1而言,1位的存储空间足够了。
可移植类型:stdint.h和inttypes.h
C99 新增了两个头文件stdint.h和inttypes.h,以确保C语言的类型在各系统中的功能相同。 C语言为现有类型创建了更多类型名。这些新的类型名定义在stdint.h头文件中。例如,int32_t表示32位的有符号整数类型。在使用32位int的系统中,头文件会把int32_t作为int的别名。不同的系统也可以定义相同的类型名。例如,int为16位、long为32位的系统会把int32_t作为long的别名。然后,使用int32_t类型编写程序,并包含stdint.h头文件时,编译器会把int或long替换成与当前系统匹配的类型。 上面讨论的类型别名是精确宽度整数类型的示例。int32_t表示整数类型的宽度正好是32位。但是,计算机的底层系统可能不支持。因此,精确宽度整数类型是可选项。 C99和C11提供了第2类别名集合。一些类型名保证所表示的类型一定是至少有指定宽度的最小整数类型。这组类型集合被称为最小宽度类型。例如,int_least8_t是可容纳8位有符号整数值的类型中宽度最小的类型的一个别名。如果某系统的最小整数类型是16位,可能不会定义int8_t类型。尽管如此,该系统仍可使用int_least8_t类型,但可能把该类型实现为16位的整数类型。 C99和C11定义了一组可使计算达到最快的类型集合。这组类型集合被称为最快最小宽度类型。例如,int_fast8_t被定义为系统中对8位有符号值而言运算最快的整数类型的别名。 C99定义了最大的有符号整数类型intmax_t,可储存任何有效的有符号整数值。类似地,unitmax_t表示最大的无符号整数类型。顺带一提,这些类型有可能比long long和unsigned long类型更大,因为C编译器除了实现标准规定的类型以外,还可利用C语言实现其他类型。 C99 和 C11 不仅提供可移植的类型名,还提供相应的输入和输出。
#include <stdio.h>
#include <inttypes.h>
int main(void)
{
int32_t me32 = 45933945;
printf("First, assume int32_t is int: ");
printf("me32 = %d\n", me32);
printf("Next, let's not make any assumptions.\n");
printf("Instead, use a \"macro\" from inttypes.h: ");
printf("me32 = %"PRId32"\n", me32);
return 0;
}
float、double和long double
C标准规定,float类型必须至少能表示6位有效数字,且取值范围至少是10-37 ~10 +37 。规定float类型必须至少精确表示小数点后的6位有效数字。通常,系统储存一个浮点数要占用32位。其中8位用于表示指数的值和符号,剩下24位用于表示非指数部分(也叫作尾数或有效数)及其符号。 C语言提供的另一种浮点类型是double(意为双精度)。double类型和float类型的最小取值范围相同,但至少必须能表示10位有效数字。一般情况下,double占用64位而不是32位。一些系统将多出的 32 位全部用来表示非指数部分,这不仅增加了有效数字的位数(即提高了精度),而且还减少了舍入误差。另一些系统把其中的一些位分配给指数部分,以容纳更大的指数,从而增加了可表示数的范围。无论哪种方法,double类型的值至少有13位有效数字,超过了标准的最低位数规定。 C语言的第3种浮点类型是long double,以满足比double类型更高的精度要求。不过,C只保证long double类型至少与double类型的精度相同。
声明浮点型变量
float noah, jonah;
double trouble;
float planck = 6.63e-34;
long double gnp;
浮点型常量
浮点型常量的基本形式是:有符号的数字(包括小数点),后面紧跟e或E,最后是一个有符号数表示10的指数。 如:-1.56E+12, 2.87e-3 正号可以省略。可以没有小数点或指数部分,但是不能同时省略两者。 可以省略小数部分或整数部分,但是不能同时省略两者。 如:3.14159,.2,4e16, .8E-5, 100., 不要在浮点型常量中间加空格:1.56E+12(错误!) 默认情况下,编译器假定浮点型常量是double类型的精度。
float some = 2.0 * 4.0;
通常,4.0和2.0被储存为64位的double类型,使用双精度进行乘法运算,然后将乘积截断成float类型的宽度。这样做虽然计算精度更高,但是会减慢程序的运行速度。在浮点数后面加上f或F后缀可覆盖默认设置,编译器会将浮点型常量看作float类型,如2.3f和9.11E9F。使用l或L后缀使得数字成为long double类型,如54.3l和4.32L。 C99 标准添加了一种新的浮点型常量格式——用十六进制表示浮点型常量,即在十六进制数前加上十六进制前缀(0x或0X),用p和P分别代替e和E,用2的幂代替10的幂(即,p计数法)。
打印浮点型
printf()函数使用**%f转换说明打印十进制记数法的float和double类型浮点数,用%e打印指数记数法的浮点数。如果系统支持十六进制格式的浮点数,可用a和A分别代替e和E**。打印long double类型要使用%Lf、%Le或%La转换说明。
#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);
printf("And it's %a in hexadecimal, powers of 2 notation\n",
aboat);
printf("%f can be written %e\n", abet, abet);
printf("%Lf can be written %Le\n", dip, dip);
return 0;
}
浮点数的上溢和下溢
上溢示例:
float toobig = 3.4E38*100.0f;
printf("%e\n", toobig);
C语言规定,在这种情况下会给toobig赋一个表示无穷大的特定值,而且printf()显示该值为inf或infinity(或者具有无穷含义的其他内容)。 以十进制为例,把一个有4位有效数字的数(如,0.1234E-10)除以10,得到的结果是0.0123E-10。虽然得到了结果,但是在计算过程中却损失了原末尾有效位上的数字。这种情况叫作下溢。C语言把损失了类型全精度的浮点值称为低于正常的浮点值。 float类型的数以指数和尾数部分来储存。存在这样一个数,它的指数部分是最小值,即由全部可用位表示的最小尾数值。该数字是float类型能用全部精度表示的最小数字。现在把它除以 2。通常,这个操作会减小指数部分,但是假设的情况中,指数已经是最小值了。所以计算机只好把尾数部分的位向右移,空出第1 个二进制位,并丢弃最后一个二进制数。把最小的正浮点数除以 2将得到一个低于正常的值。如果除以一个非常大的值,会导致所有的位都为0。 还有另一个特殊的浮点值NaN(not a number的缩写)。例如,给asin()函数传递一个值,该函数将返回一个角度,该角度的正弦就是传入函数的值。但是正弦值不能大于1,因此,如果传入的参数大于1,该函数的行为是未定义的。在这种情况下,该函数将返回NaN值,printf()函数可将其显示为nan、NaN或其他类似的内容。
浮点数舍入错误
#include <stdio.h>
int main(void)
{
float b = 2.0e20 + 1.0;
float a = b - 2.0e20;
printf("%f\n", a);
return 0;
}
2.0e20是 2后面有20个0。如果把该数加1,那么发生变化的是第21位。要正确运算,程序至少要储存21位数字。而float类型的数字通常只能储存按指数比例缩小或放大的6或7位有效数字。
复数和虚数类型
C语言有3种复数类型:float_Complex、double_Complex和long double _Complex。float _Complex类型的变量应包含两个float类型的值,分别表示复数的实部和虚部。 C语言的3种虚数类型是float_Imaginary、double _Imaginary和long double _Imaginary 如果包含complex.h头文件,便可用complex代替_Complex,用imaginary代替_Imaginary,还可以用I代替-1的平方根。
类型大小
#include <stdio.h>
int main(void)
{
printf("Tpye int has a size of %zd bytes.\n", sizeof(int));
printf("Tpye char has a size of %zd bytes.\n", sizeof(char));
printf("Tpye long has a size of %zd bytes.\n", sizeof(long));
printf("Tpye long long has a size of %zd bytes.\n",
sizeof(long long));
printf("Tpye double has a size of %zd bytes.\n",
sizeof(double));
printf("Tpye long double has a size of %zd bytes.\n",
sizeof(long double));
return 0;
}
sizeof是C语言的内置运算符,以字节为单位给出指定类型的大小。C99和C11提供**%zd**转换说明匹配sizeof的返回类型,size_t类型。
参数和陷阱
传递给函数的信息被称为参数。关键是要理解无论双引号中包含多少个字符和标点符号,一个字符串就是一个参数。 C语言用逗号分隔函数中的参数。
#include <stdio.h>
int main()
{
int n = 4;
int m = 5;
float f = 7.0f;
float g = 8.0f;
printf("%d\n", n, m);
printf("%d %d %d\n", n);
printf("%d %d\n", f, g);
return 0;
}
转义序列示例
#include <stdio.h>
int main()
{
float salary = 0.0f;
printf("\aEnter your desired monthly salary:");
printf(" $_______\b\b\b\b\b\b\b");
scanf("%f", &salary);
printf("\n\t$%.2f a month is $%.2f a year.",
salary, salary * 12.0);
printf("\rGee!\n***");
return 0;
}
第1条printf()语句发出一声警报(因为使用了\a) 第2条printf()语句的光标在$后闪动,因为\b是退格符,7个退格字符使得光标左移7个位置,即把光标移至7个下划线字符的前面,紧跟在美元符号后面。 假设键入的数据是4000.00(并按下Enter键),屏幕显示的内容应该是: Enter your desired monthly salary: $4000.00 键入的字符替换了下划线字符。按下Enter键后,光标移至下一行的起始处。 第3条printf()语句中的字符串以\n\t开始。换行字符使光标移至下一行起始处。水平制表符使光标移至该行的下一个制表点,一般是第9列(但不一定)。因为这条printf()语句中没有使用换行字符,所以光标停留在最后的点号后面。 第4条printf()语句以***\r开始。这使得光标回到当前行的起始处*。然后打印Gee!,接着\n使光标移至下一行的起始处。
刷新输出
最初,printf()语句把输出发送到一个叫作缓冲区的中间存储区域,然后缓冲区中的内容再不断被发送到屏幕上。C 标准明确规定了何时把缓冲区中的内容发送到屏幕:当缓冲区满、遇到换行字符或需要输入的时候(从缓冲区把数据发送到屏幕或文件被称为刷新缓冲区)。例如,前两个 printf()语句既没有填满缓冲区,也没有换行符,但是下一条 scanf()语句要求用户输入,这迫使printf()的输出被发送到屏幕上。
编程练习
#include <stdio.h>
int main()
{
int num = 0;
printf("Enter a number: ");
scanf("%d", &num);
printf("%c\n", num);
return 0;
}
#include <stdio.h>
int main()
{
printf("\aStartled by the sudden soun, Sally shouted,\n");
printf("\"By the Great Pumpkin, what was that!\"\n");
return 0;
}
#include <stdio.h>
int main()
{
float f = 0.0f;
printf("Enter a floating-point value: ");
scanf("%f", &f);
printf("fixed-point notation: %f\n", f);
printf("exponential notation: %e\n", f);
printf("p notation: %a\n", f);
return 0;
}
#include <stdio.h>
int main()
{
int age = 0;
printf("Enter your age: ");
scanf("%d", &age);
printf("seconds: %e\n", age * 3.156e7);
return 0;
}
#include <stdio.h>
int main()
{
float quart = 0.0f;
printf("Enter the number of quack: ");
scanf("%f", &quart);
printf("Number: %e\n", quart * 950.0f / 3.0e-23);
}
#include <stdio.h>
int main()
{
float height = 0.0f;
printf("Enter your height: ");
scanf("%f", &height);
printf("%f\n", height * 2.54f);
return 0;
}
#include <stdio.h>
int main()
{
float cups = 0.0f;
printf("Enter the number of cups: ");
scanf("%f", &cups);
printf("Pint: %f Ounce: %f\n", cups / 2, cups * 8);
printf("Spoon: %f Teaspoon: %f\n", cups * 16, cups * 48);
return 0;
}
个人读书记录,如有错误,欢迎指正。
|