字符串和格式化输入输出
1. 前导程序
#include#include //提供strlen()函数的原型#define DENSITY 62.4int main(){ float weight,volume; int size,letters; char name[40];//name是一个可容纳40个字符的数组 printf("hi!what's your first name?\n") ; scanf("%s",name); printf("%s,what's your weight in pounds?\n",name); scanf("%f",&weight); size=sizeof name; letters=strlen(name); volume=weight /DENSITY; printf("Well,%s,your volume is %2.2f cuic feet.\n",name,volume); printf("also,your first name has %d letters,\n",letters); printf("and we have %d bytes to store it.\n",size); return 0; }
输出结果
该程序包含以下特性:
1. 用数组(array)储存字符串 ,在该程序中,用户输入的名被储存在数组中,该数组占用内存中40个连续的字节,每个字节储存一个字符值
2. 使用%s转化说明来处理字符串的输入和输出,注意,在scanf()中,name没有&前缀,而weight有
3. 用C预处理把字符常量DENSITY定义为62.4
4. 用C函数strlen()获取字符串的长度
2. 字符串
注意图中数组末尾位置的字符\0。这是空字符,C语言用他标记字符串的结束
空字符不是数字0,它是非打印字符,其ASCII码是0。C中的字符串一定以空字符结束,这意味着数组的容量必须至少比待定存储字符串中的字符数多1 ,因此,程序中有40个存储单元的字符串,只能储存39个字符,剩下一个字节留给空字符
数组:是同数据元素的有序序列,上面程序通过声明创建一个包含40个存储单元的数组,每个单元储存一个char类型的值:
char name[40]
name后面的方括号表明这是一个数组,方括号中的40表明该数组中的元素数量,char表明每个元素的类型
字符串看上去比较复杂,必须先创建一个数组,把字符串中字符逐个放入数组,还要记得在末尾加上一个\0,计算机会自己处理这些细节
#include#define PRAISE "you are an extraordinary being."int main(void){ char name[40]; printf("what's your name?"); scanf("%s",name); printf("hello,%s. %s\n",name,PRAISE); return 0; }
输出结果:
what's your name?yang yuqing
hello,yang. you are an extraordinary being.注意:不用亲自把空字符放入字符串末尾,scanf()在读取输入时就已经完成这项工作,也不用在字符串常量PRAISE末尾添加空字符
scanf()只读取了yang yuqing中的yang,它在遇到第一个空白(空格、制表符或换行符)时就不再读取了,一般而言,scanf()只会读取字符串中的一个单词,而不是一整句,C语言还有其他的输入函数(如fget()),用于读取一般字符串
字符和字符串
字符串常量"x"和字符常量'x'不同,区别之一在于'x'是基本类型(char),而"x"是派生类型(char数组)
区别之二是"x"实际上由两个字符组成:'x'和空字符\0
strlen()函数
sizeof运算符,它以字节为单位给出对象的大小
strlen()函数给出字符串中的字符长度
因为1字节储存一个字符,可能会认为两种方法应用于字符串得到的结果相同,但事实并非如此
#include#include //提供strlen()函数的原型#define PRAISE "you are an extraordinary being."int main(void){ char name[40]; printf("what's your name?"); scanf("%s",name); printf("hello,%s. %s\n",name,PRAISE); printf("your name of %zd letters occupies %zd memory cells.\n",strlen(name),sizeof name); printf("the phrase of praise has %zd letters ",strlen(PRAISE)); printf("and occupies %zd memory cells.\n",sizeof PRAISE); return 0;}
输出结果:
string.h头文件包含多个与字符串相关的函数原型,包括strlen()
一般而言,C把函数库中相关的函数归为一类,并为每类函数提供一个头文件,例如printf()和scanf()都属于标准输入和输出函数
使用stdio.h头文件,string.h头文件包含了strlen() 函数和其他一些与字符串相关的函数
sizeof运算符报告,name数组有40个存储单位,但是,只有前4个单元用来储存yang,suoystrl()得出的结果是4
name的第5个单元储存空字符,strlen()并未将其计入
对于PRAISE,用strlen()得出的也是字符串中的字符数(包括空格和标点符号),然而sizeof运算符给出的数更大,因为它把字符串末尾不可见的空字符也计算在内
注意:上一个章节的sizeof使用了圆括号,但本次示例没有,圆括号的使用时机取决于运算对象是类型还是特定量
运算对象是类型时,圆括号必不可少,但是对于特定量,可有可无
也就是说对于类型,应写成sizeof(char)或sizeof(float);对于特定量,可写成sizeof name或sizeof 6.28,尽管如此还是建议所有情况下都使用圆括号,如sizeof(6.28)
3. 常量和C预处理器
创建常量
1. 声明一个变量,然后将该变量设置为所需要的常量
float taxrate;taxrate = 0.015;
2. C预处理器也可以用来定义常量,只需在程序顶部添加下面一行:
#define TAXRATE 0.015
编译程序时,程序中所有的TAXRATE都会被替换炒年糕0.015,这一过程称为编译是替换,在运行程序时,程序中所有的替换均已完成,通常,这样的定义的常量也称为明示常量
请注意格式,首先是#define,接着是符号常量名(TAXRATE),然后是符号常量的值(0.015)注意并没有=符号
通用格式:#define NAME value
末尾不用加分好,因为这是一种又预处理器处理的替换机制,其次大写表示符号常量是C语言一贯的传统
#include#define PI 3.1415926int main(void){ float area,circum,radius; printf("what is the radius of your pizza?\n"); scanf("%f",&radius); area=PI * radius * radius; circum=2.0 * PI * radius; printf("your basic pizza parameters are as follows:\n"); printf("circumference= %1.2f,area = %1.2f\n",circum,area); return 0;}
输出结果:
注意:printf()语句中的%1.2f表明,结果被四舍五入为两位小数输出
#define指令还可定义字符和字符串常量,前者使用单引号,后者使用双引号
const限定符
const关键字,用于限定一个变量为只读,其声明如下:
const int MONTHS = 12;//MONTHS在程序中不可更改,值为12
这使得MONTHS成为一个只读值,可以使用MONTHS,可以打印MONTHS,但是不能更改MONTHS的值
明示常量
C头文件limits.h和float.h分别提供了与整数类型和浮点类型大小限制相关的详细信息
每个头文件都定义了一系列供事先使用的明示常量
#include#include //整型限制 #include // 浮点型限制int main(void){ printf("some number limits for this system:\n"); printf("biggest int:%d\n",INT_MAX); printf("smallest long long:%lld\n",LLONG_MIN); printf("one byte=%d bits on this system.\n",CHAR_BIT); printf("largest doubleL:%e\n",DBL_MAX); printf("smallest normal float:%e\n",FLT_DIG); printf("float epsilon=%e\n",FLT_EPSILON); return 0; }
输出结果:
4. printf()和scanf()
它们是输入/输出函数,或简称为I/O函数
printf()函数
打印数据的指令要与待打印数据的类型相匹配,这些符号被称为转换说明,它们指定如何把数据转换成可显示的形式
格式字符串包含两种形式不同的信息:
1. 实际要打印的字符
2. 转换说明
警告:
格式字符串中的转换说明一定要与后面的每个项相匹配,若忘记这个基本要求会导致严重的后果
如果只打印短语或句子,就不需要使用任何转换说明,如果只打印数据,也不用加入说明文字
注意:
由于printf()函数使用%符号来标识转换说明,因此打印%符号需要用两个%符号
printf()中的标记
#include#define PAGES 959int main(void){ printf("*%d*\n",PAGES); printf("*%2d*\n",PAGES); printf("*%10d*\n",PAGES); printf("*%-10d*\n",PAGES); return 0; }
输出结果:
打印较长的字符
方法1:
使用多个printf()语句,因为第一个字符串没有以\n字符结束,所以第二个字符串紧跟第一个字符串末尾输出
方法2:
用反斜杠(\)和Enter键组合来断行,这使得光标移至下一行,而且字符串中不会包含换行符
其效果是在下一行继续输出,但是,下一行代码和程序清单中的代码一样从最左边开始,如果缩进该行,比如缩进5个空格,那么这5个空格就会成为字符串的一部分
方法3:
在两个用双引号括起来的字符串之间用空白隔开,C编译器会把多个字符串看做是一个字符串
printf("hello,young lovers,wherever you are.")printf("hello,young " "lovers" ",wherever you are.")printf("hello,young lovers" ",wherever you are.")
以上三种形式是等效的,要记得在字符串中包含所需的空格,如,"young""lovers"会成为"younglovers",而"young " "lovers"才是"young lovers"
使用scanf()
两个简单规则:
1. 如果使用scanf()读取基本变量类型的值,在变量名前加上一个&;
2. 如果使用scanf()把字符串读入字符数组中,不要使用&
#includeint main(void){ int age; // 变量 float assets; // 变量 char pet[30]; // 字符数组,用于储存字符串 printf("Enter your age, assets, and favorite pet.\n"); scanf("%d %f", &age, &assets); // 这里要使用& scanf("%s", pet); // 字符数组不使用& printf("%d $%.2f %s\n", age, assets, pet); return 0;}
scanf()函数使用空白(换行符、制表符和空格)把输入分成多个字段。只要在每个输入项之间输入至少一个换行 符、空格或制表符即可
1.从scanf()角度看输入
假设scanf()根据一个%d转换说明读取一个整数。scanf()函数每次读取一个字符,跳过所有的空白字符,直至遇到第1个非空白字符才开始读取。因为要读取整数,所以 scanf()希望发现一个数字字符或者一个符号(+或-)。如果找到一个数字或 符号,它便保存该字符,并读取下一个字符。如果下一个字符是数字,它便保存该数字并读取下一个字符。scanf()不断地读取和保存字符,直至遇到非数字字符。如果遇到一个非数字字符,它便认为读到了整数的末尾。然后, scanf()把非数字字符放回输入。这意味着程序在下一次读取输入时,首先读到的是上一次读取丢弃的非数字字符。最后,scanf()计算已读取数字(可能 还有符号)相应的数值,并将计算后的值放入指定的变量中。 如果使用字段宽度,scanf()会在字段结尾或第1个空白字符处停止读取 (满足两个条件之一便停止)。
如果第1个非空白字符是A而不是数字,会发生什么情况?scanf()将停在那里,并把A放回输入中,不会把值赋给指定变量。程序在下一次读取输入 时,首先读到的字符是A。如果程序只使用%d转换说明, scanf()就一直无法越过A读下一个字符。
另外,如果使用带多个转换说明的scanf(),C规定在第1个出错处停止读取输入。 用其他数值匹配的转换说明读取输入和用%d 的情况相同。区别在于 scanf()会把更多字符识别成数字的一部分。例如,%x转换说明要求scanf()识 别十六进制数a~f和A~F。浮点转换说明要求scanf()识别小数点、e记数法 (指数记数法)和新增的p记数法(十六进制指数记数法)。 如果使用%s 转换说明,scanf()会读取除空白以外的所有字符。scanf()跳 过空白开始读取第 1 个非空白字符,并保存非空白字符直到再次遇到空白。 这意味着 scanf()根据%s 转换说明读取一个单词,即不包含空白字符的字符 串。如果使用字段宽度,scanf()在字段末尾或第1个空白字符处停止读取。 无法利用字段宽度让只有一个%s的scanf()读取多个单词。最后要注意一点: 当scanf()把字符串放进指定数组中时,它会在字符序列的末尾加上'\0',让数组中的内容成为一个C字符串
2.格式字符串中的普通字符
scanf()函数允许把普通字符放在格式字符串中。除空格字符外的普通字符必须与输入字符串严格匹配。
例如,假设在两个转换说明中添加一个逗号:
scanf("%d,%d", &n, &m);
scanf()函数将其解释成:用户将输入一个数字、一个逗号,然后再输入一个数字。
也就是说,用户必须像下面这样进行输入两个整数: 88,121
由于格式字符串中,%d后面紧跟逗号,所以必须在输入88后再输入一 个逗号。
但是,由于scanf()会跳过整数前面的空白,所以下面两种输入方式 都可以:
88, 121
和
88,
121
格式字符串中的空白意味着跳过下一个输入项前面的所有空白。例如, 对于下面的语句:
scanf("%d ,%d", &n, &m);
以下的输入格式都没问题:
88,121
88 ,121
88 , 121
请注意,“所有空白”的概念包括没有空格的特殊情况。 除了%c,其他转换说明都会自动跳过待输入值前面所有的空白。
因此,scanf("%d%d", &n, &m)与scanf("%d %d", &n, &m)的行为相同。对于%c,在格式字符串中添加一个空格字符会有所不同。
例如,如果把%c放在格式字符串中的空格前面,scanf()便会跳过空格,从第1个非空白字符开始读取。
也就是说,scanf("%c", &ch)从输入中的第1个字符开始读取,而scanf(" %c", &ch)则从第1个非空白字符开始读取。
3.scanf()的返回值
scanf()函数返回成功读取的项数。如果没有读取任何项,且需要读取一 个数字而用户却输入一个非数值字符串,scanf()便返回0。
当scanf()检测 到“文件结尾”时,会返回EOF(EOF是stdio.h中定义的特殊值,通常用 #define指令把EOF定义为-1)。
在学会if语句和while语句后,便可使用 scanf()的返回值来检测和处理不匹配的输入。
printf()和scanf()的*修饰符
printf()的*修饰符
如果不想预先指定字段宽度,希望通过程序来指定,那么可以用*修饰符代替字段宽度。
但还是要用一个参数告诉函数,字段宽度应该是多少。 也就是说,如果转换说明是%*d,那么参数列表中应包含*和 d对应的值。这个技巧也可用于浮点值指定精度和字段宽度。
#includeint main(void){ unsigned width, precision; int number = 256; double weight = 242.5; printf("Enter a field width:\n"); scanf("%d", &width); printf("The number is :%*d:\n", width, number); printf("Now enter a width and a precision:\n"); scanf("%d %d", &width, &precision); printf("Weight = %*.*f\n", width, precision, weight); printf("Done!\n"); return 0;}
输出结果:
Enter a field width:
6The number is : 256:Now enter a width and a precision:8 3Weight = 242.500Done!用户首先输入6,因此6是程序使用的字段宽度。类似地,接下来 用户输入8和3,说明字段宽度是8,小数点后面显示3位数字。一般而言,程序应根据weight的值来决定这些变量的值
scanf()中*的用法
把*放在%和转换字符之间时,会使得 scanf()跳过相应的输出项
#includeint main(void){ int n; printf("Please enter three integers:\n"); scanf("%*d %*d %d", &n); printf("The last integer was %d\n", n); return 0;}
输出结果:
Please enter three integers:
2013 2014 2015
The last integer was 2015
格式输入与输出
&a,&b
分别表示变量a和变量b的地址
这个地址就是编译系统在内存中给a,b变量分配的地址,在C语言中,使用了地址这个概念,这是与其他语言不同的
应该把变量的值和变量的地址这两个不同的概念区别开来,变量的地址是C编译系统分配的,用户不必关系具体的地址是多少
变量的地址和变量值的关系如下:
在赋值表达式中给变量赋值,如
a=567;
则a为变量名,567是变量的值,&a是变量a的地址
但是在赋值号左边是变量名,不能写地址,而scanf函数在本质上也是给变量赋值,但要求写变量的地址,如&a
这两者在形式上不同的,&是一个取地址运算符,&a是一个表达式,其功能是求变量的地址
使用scanf()函数注意以下几点:
1. scanf函数中没有精度限制,如scanf("%5.2",&a);是非法的,不能企图用此语句输入小数为2位的实数
2. scanf中要求给出变量地址,如给出变量名则会出错,如scanf("%d",a);是非法的,应改为scanf("%d",&a);才是合法的
3. 在输入多个数值数据时,若格式控制串中没有非格式字符作为输入数据之间的间隔则可用空格,TAB或回车做间隔,C编译器在碰到空格,TAB,回车或非法数据
(如对"%d"输入“12A”时,A即为非法数据)时,即认为该数据结束
4. 在输入字符数据时,若格式控制字符串中无非格式字符,则认为所有输入的字符均为有效字符
5. 如果格式控制串中有非格式字符则输入时也要输入该非格式字符
例如scanf("%d,%d,%d",&a,&b,&c);其中用非格式字符“,”,作为间隔符,故输入时应为:5,6,7