C Primer Plus 第四章 字符串和格式化输入/输出 阅读笔记

4.1 前导程序

talkback.c程序:

// talkback.c -- 演示与用户交互
#include <stdio.h>
#include <string.h>		//提供strlen()函数的原型
#define	DENSITY 62.4	//人体密度(单位:磅 / 立方英尺)
int 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 cubic 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;
 } 

运行结果:
在这里插入图片描述
该程序包含以下新特性:

  • 用数组储存字符串。该数组占用内存中40个连续的字节,每个字节储存一个字符值。
  • 使用%s来说明来处理字符串的输入和输出。
  • 用C预处理器把字符常量DENSITY定义为62.4。
  • 用C函数strlen()获取字符串的长度。

4.2 字符串简介

  字符串是一个或多个字符的序列。注意,双引号仅是用来告知编译器它括起来的是字符串,并不是字符串的一部分。

4.2.1 char 类型数组和null字符

  在C语言中,字符串都被储存在char类型的数组中。数组由连续的存储单元组成,字符串中的字符被储存在相邻的存储单元中,每个单元储存一个字符。
在这里插入图片描述
  数组末尾位置的字符\0。这是空字符,标记字符串的结束。空字符不是数字0,是非打印字符。C中一定以空字符结束,这意味着数组的容量必须至少比待存储字符串中的字符数多1。4.1的前导程序中有40个存储单元的字符串,只能储存39个字符,剩下一个字节留给空字符。
  数组是同类型数据元素的有序序列。可以把数组看作是一行连续的多个存储单元。
在这里插入图片描述

4.2.2 使用字符串

praise1.c程序:

/* praise1.c -- 使用不同类型的字符串 */
#include <stdio.h>
#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;
 } 

运行结果:
在这里插入图片描述
程序说明:
  %s 打印字符串。scanf()只读取了Angela Plains中的Angela,它在遇到第1个空白(空格、制表符或换行符)时就不再读取输入。根据%s转换说明,scanf()只会读取字符串中的一个单词,而不是一整句。C语言还有其他的输入函数(如,fgets()),用于读取一般字符串。

字符串和字符:
  字符串常量"x"和字符常量’x’不同。
  区别之一:‘x’ 是基本类型,“x” 是派生类型
  区别之一:“x” 实际上由两个字符组成:‘x’ 和空字符 \0。
在这里插入图片描述

4.2.3 strlen()函数

  sizeof 运算符,以字节为单位给出对象的大小。
  strlen() 函数,给出字符串中的字符长度。
praise2.c程序:

/* praise2.c */
//如果编译器不识别 %zd,尝试换成 %u或 %lu。 
#include <stdio.h>
#include <string.h>    /* 提供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;
 } 

运行结果:
在这里插入图片描述
程序说明:
  C把函数库中相关的函数归为一类,并为每类函数提供一个头文件。string.h头文件包含了strlen()函数和其他一些与字符串相关的函数。
  sizeof 运算符报告,name 数组有40个存储单元。但是,只有前11个单元用来储存Serendipity,所以strlen()得出的结果是11。name 数组的第12个单元储存空字符串,strlen()并未将其计入。
在这里插入图片描述
  对于 PRAISE,用 strlen() 得出的也是字符串中的字符数(包括空格和标点符号)。然而,sizeof 运算符给出的数更大,因为它把字符串末尾不可见的空字符也计算在内。该程序并未明确告诉计算机要给字符串预留多少空间,所以它必须计算双引号内的字符数。
  sizeof 何时使用圆括号取决于运算对象是类型还是特定量。对象是类型时,圆括号必不可少;对于特定量,可有可无。建议所有情况下都使用圆括号。

4.3 常量和C预处理器

  假设程序中的多处使用一个常量,有时需要改变它的值。如果程序使用符号常量,则只需更改符号常量的定义,不用在程序中查找使用常量的地方,然后逐一修改。
  如何创建符号常量?方法一:声明一个变量,然后将该变量设置为所需的常量。例如,float taxrate = 0.015。方法二:C预处理器。例如,#define TAXRATE 0.015。
  格式:首先是#define,接着是符号常量名(TAXRATE),然后是符号常量的值。
  通用格式:#define NAME value
  符号常量要大写。还有一个命名约定,即在名称前带c_或k_前缀来表示常量(如,c_level)。
  符号常量的命名规则与变量相同。可以使用大小写字母、数字、下划线字符,首字母不能为数字。
pizza.c程序:

/* pizza.c -- 在比萨饼程序中使用已定义的常量 */
#include <stdio.h>
#define PI 3.14159
int 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; 
} 

运行结果:
在这里插入图片描述
程序说明:
  %1.2f 表明,结果被四舍五入为两位小数输出。
  #define指令可以定义字符和字符常量。前者使用单引号,后者使用双引号。

4.3.1 const 限定符

  const 关键字,用于限定一个变量为只读。
  const int MONTHS = 12; //MONTHS在程序中不可更改,值为12
  const 用起来比 #define更灵活。

4.3.2 明示常量

  limits.h 和 float.h 分别提供了与整数类型和浮点类型大小限制相关的详细信息。每个头文件都定义了一系列供实现使用的明示常量。例如,limits.h 头文件包含以下类似的代码:
  #define INT_MAX +32767
  #define INT_MIN -32768
在这里插入图片描述
  下表中所列都与float类型相关。把明示常量名中的FLT分别替换成DBL和LDBL,即可分别表示double和Long double类型对应的明示变量(表中假设系统使用2的幂来表示浮点数)。
在这里插入图片描述
defines.c程序:演示如何使用float.h和limits.h中的数据

// defines.c -- 使用limits.h和float.h头文件中定义的明示常量
#include <stdio.h>
#include <limits.h>		//整数限制 
#include <float.h> 		//浮点数限制
int main(void)
{
	printf("Some number limits for this system:\n");
	printf("Biggest int: %d\n", INT_MAX);
	printf("Smalleat long long: %lld\n", LLONG_MIN);
	printf("One byte = %d bits on this system.\n", CHAR_BIT);
	printf("Largest double: %e\n", DBL_MAX);
	printf("Smalleat normal float: %e\n", FLT_MIN);
	printf("float precision = %d digits\n", FLT_DIG);
	printf("float epsilon = %e\n", FLT_EPSILON);
	
	return 0;
 } 

运行结果:
在这里插入图片描述

4.4 printf() 和 scanf()

  printf() 函数和 scanf() 函数,是输入\ 输出函数,或简称为I \ O函数。

4.4.1 printf() 函数

  请求printf()函数打印数据的指令要与待打印数据的类型相匹配。
在这里插入图片描述

4.4.2 使用printf()

printout.c程序:

/* printout.c -- 使用转换说明 */
#include <stdio.h>
#define PI 3.141593
int main(void)
{
	int number = 7;
	float pies = 12.75;
	int cost = 7800;
	
	printf("The %d contestants ate %f berry pies.\n", number,pies);
	printf("The value of pi is %f.\n", PI);
	printf("Farewell! thou art too dear for my possessing,\n");
	printf("%c%d\n",'$',2 * cost);
	
	return 0;
 } 

运行结果:
在这里插入图片描述
程序说明:
  待打印项可以是变量、常量、乘法表达式。
  格式字符串是双引号括起来的内容。
  格式字符串包含两种形式不同的信息:1. 实际要打印的字符 2. 转换说明
在这里插入图片描述

在这里插入图片描述

4.4.3 printf()的转换说明修饰符

  在%和转换字符之间插入修饰符可修饰基本的转换说明。如果要插入多个字符,其书写顺序应该与表4.4中列出的顺序相同。
在这里插入图片描述
注意 类型可移植性
  sizeof 运算符以字节为单位返回类型或值的大小。 这应该是某种形式的整数, 但是标准只规定了该值是无符号整数。 在不同的实现中, 它可以是unsigned int、 unsigned long甚至是unsigned long long。
  因此, 如果要用printf()函数显示sizeof表达式, 根据不同系统, 可能使用%u、 %lu或%llu。 这意味着要查找你当前系统的用法, 如果把程序移植到不同的系统还要进行修改。
  鉴于此, C提供了可移植性更好的类型。 首先, stddef.h头文件(在包含stdio.h头文件时已包含其中) 把size_t定义成系统使用sizeof返回的类型, 这被称为底层类型 。
  其次, printf()使用z修饰符表示打印相应的类型。 同样, C还定义了ptrdiff_t类型和t修饰符来表示系统使用的两个地址差值的底层有符号整数类型。

注意 float参数的转换
  对于浮点类型, 有用于double和long double类型的转换说明, 却没有float类型的。
  这是因为在K&R C中, 表达式或参数中的float类型值会被自动转换成double类型。 一般而言, ANSI C不会把float自动转换成double。 然而, 为保护大量假设float类型的参数被自动转换成double的现有程序,printf()函数中所有float类型的参数(对未使用显式原型的所有C函数都有效) 仍自动转换成double类型。 因此, 无论是K&R C还是ANSI C, 都没有显示float类型值专用的转换说明。
在这里插入图片描述
width.c程序:字段宽度在打印整数时的效果

/* width.c -- 字段宽度 */
#include <stdio.h>
#define PAGES 959
int main(void)
{
	printf("*%d*\n", PAGES);
	printf("*%2d*\n", PAGES);
	printf("*%10d*\n", PAGES);
	printf("*%-10d*\n", PAGES);
	
	return 0;
} 

运行结果:
在这里插入图片描述
程序说明:
  第一个转换说明:%d 不带任何修饰符,其对应的输出结果与带整数字段宽度的转换说明的输出结果相同。
  第二个转换说明:%2d 对应的输出结果应该是2字段宽度。因为待打印的整数有3位数字,所以字段宽度自动扩大以符合整数的长度。
  第三个转换说明:%10d,其对应的输出结果有10个空格宽度,实际上在两个星号之间有7个空格和3位数字,并且数字位于字段的右侧。
  第四个转换说明:%-10d,其对应的输出结果同样是10个空格宽度,- 标记说明打印的数字位于字段的左侧。

floats.c程序:

// floats.c -- 一些浮点型修饰符的组合
#include <stdio.h>

int main(void)
{
	const double RENT = 3852.99;  // const 变量
	
	printf("*%f*\n", RENT);
	printf("*%e*\n", RENT);
	printf("*%4.2f*\n", RENT);
	printf("*%3.1f*\n", RENT);
	printf("*%10.3f*\n", RENT);
	printf("*%10.3E*\n", RENT);
	printf("*%+4.2f*\n", RENT);
	printf("*%010.2f*\n", RENT);
	
	return 0;
} 

运行结果:
在这里插入图片描述
程序说明:
  第一个转换说明:%f 字段宽度和小数点后面的位数均为系统默认设置,即字段宽度是容纳待打印数字所需的位数和小数点后打印6位数字。
  第二个转换说明:%e 默认情况下,编译器在小数点的左侧打印1个数字,在小数点的右侧打印6个数字。
  第七个转换说明:+ 标记,使打印的值前面多了一个代数符号(+)。
  第八个转换说明:0 标记,使打印的值前面以0填充以满足字段要求。%010.2f 的第1个0是标记,句点(.)之前、标记之后的数字(本例为10)是指定的字段宽度。

flags.c程序:

/* flags.c -- 演示一些格式标记 */
#include <stdio.h>
int main(void)
{
	printf("%x %X %#x\n", 31, 31, 31);
	printf("**%d**% d**% d**\n", 42, 42, -42);
	printf("**%5d**%5.3d**%05d**%05.3d**\n", 6, 6, 6, 6);
	
	return 0;
 } 

运行结果:
在这里插入图片描述
程序说明:
  第一行输出:1f 是十六进制数,等于十进制数31。根据 %x 打印出 1f,%X 打印出 1F, %#x 打印出 0x1f。
  第二行输出:演示了如何在转换说明中用空格在输出的正值前面生成前导空格,负值前面不产生前导空格。
  第三行输出:演示了 如何在整数格式中使用精度(%5.3d)生成足够的前导0以满足最小位数的要求(本例是3)。然而,使用0标记会使得前导0填充满整个字段宽度。最后,如果0标记和精度一起出现,0标记会被忽略。

stringf.c程序:

/* stringf.c -- 字符串格式*/
#include <stdio.h>
#define BLURB "Authentic imitation!"
int main(void)
{
	printf("[%2s]\n", BLURB);
	printf("[%24s]\n", BLURB);
	printf("[%24.5s]\n", BLURB);
	printf("[%-24.5s]\n", BLURB);
	
	return 0;
 } 

运行结果:
在这里插入图片描述
程序说明:
  第一行输出:虽然第1个转换说明是%2s,但是字段被扩大为可容纳字符串中的所有字
符。
  第三行输出:精度限制待打印字符的个数。.5表示只打印5个字符。
  第四行输出:- 标记使得文本左对齐输出。

4.4.4 转换说明的意义

  转换说明把以二进制格式储存在计算机中的值转换成一系列字符(字符串)以便于显示。
  转换说明是翻译说明,%d的意思是“把给定的值翻译成十进制整数文本并打印出来”。

1. 转换不匹配
  转换说明应该与待打印值的类型相匹配。例如,打印 int 类型的值,可以使用%d、%x或%o;打印 double 类型的值,可以用%f、%e或%g。

intconv.c程序:演示一些不匹配的整型转换示例

/* intconv.c -- 一些不匹配的整型转换 */
#include <stdio.h>
#define PAGES 336
#define WORDS 65618
int main(void)
{
	short num = PAGES;
	short mnum = -PAGES;
	
	printf("num as short and unsigned short:  %hd %hu\n", num, num);
	printf("-num as short and unsigned short:  %hd %hu\n", mnum, mnum);
	printf("num as int and char:  %d %c\n", num, num);
	printf("WORDS as int, short, and char: %d %hd %c\n", WORDS, WORDS, WORDS);
	
	return 0;
 } 

运行结果:
在这里插入图片描述
程序说明:
  第二行输出:short int 的大小是2字节,系统使用二进制补码来表示有符号整数。这种方法,数字0 ~ 32767代表它本身,而数字32768 ~ 65535则表示负数。65535表示-1,66534表示-2,以此类推-336表示为65200(65536-336)。所以,被解释成有符号int时,65200代表-336;而被解释成无符号int时,65200则代表65200。
  第三行输出:演示了把一个大于225的值转换成字符会发生什么情况。short int 是2字节,char 是1字节。使用%c打印336时,只会查看储存336的2字节中的后1字节。这种截断相当于用一个整数除以256,只保留其余数(以256为模)。在这种情况下,余数是80,对应的ASCII值是字符P。
在这里插入图片描述
  第四行输出:在该系统中打印比 short int 类型最大整数(32767)更大的整数(65618)。计算机进行求模运算。在本系统中,把数字65618储存为4字节的 int 类型值。用%hd打印时,只使用最后2个字节。相当于65618除以65536(256*256)的余数,此例为82。如果余数在32767 ~ 65618范围内会被打印成负数。

floatcnv.c程序:

/* floatcnv.c -- 不匹配的浮点型转换 */ 
#include <stdio.h>
int main(void)
{
	float n1 = 3.0;
	double n2 = 3.0;
	long n3 = 2000000000;
	long n4 = 1234567890;
	
	printf("%.1e %.1e %.1e %.1e\n", n1, n2, n3, n4);
	printf("%ld %ld\n", n3, n4);
	printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
	
	return 0;
 } 

运行结果:
在这里插入图片描述
程序说明:
  第1行输出显示,%e没有把整数转换成浮点数。 如果使用%e转换说明打印n3(long类型) 会发生什么情况。 首先,%e转换说明让printf()函数认为待打印的值是double类型(本系统中double为8字节)。当printf()查看n3(本系统中是4字节的值) 时, 除了查看n3的4字节外, 还会查看查看n3相邻的4字节,共8字节单元。 接着,它将8字节单元中的位组合解释成浮点数(如, 把一部分位组合解释成指数)。因此,即使n3的位数正确,根据%e转换说明和%ld转换说明解释出来的值也不同。 最终得到的结果是无意义的值。
  第1行:在本系统中,float是4字节,但是为了printf()能正确地显示该值,n1被扩成8字节。
  第2行输出显示,只要使用正确的转换说明,printf()就可以打印n3和n4。
  第3行输出显示,如果printf()语句有其他不匹配的地方, 即使用对了转换说明也会生成虚假的结果。 用%ld转换说明打印浮点数会失败, 但是在这里, 用%ld打印long类型的数竟然也失败了! 问题出在C如何把信息传递给函数。 具体情况因编译器实现而异。
  参数传递:程序把传入的值放入称为栈的内存区域。计算机根据变量类型把值放入栈中。因此,n1被储存在栈中,占8字节(float类型被转换成double类型)。 n2也在栈中占8字节,而n3和n4在栈中分别占4字节。printf()函数根据转换说明从栈中读取值。%ld转换说明表明printf()应该读取4字节, 所以printf()读取栈中的前4字节作为第1个值。这是n1的前半部分, 将被解释成一个long类型的整数。 根据下一个%ld转换说明, printf()再读取4字节, 这是n1的后半部分, 将被解释成第2个long类型的整数。根据第3个和第4个%ld, printf()读取n2的前半部分和后半部分, 并解释成两个long类型的整数。 因此,对于n3和n4,虽然用对了转换说明,但printf()还是读错了字节。
在这里插入图片描述
2. printf()的返回值

  printf()函数返回打印字符的个数。 如果有输出错误,printf()则返回一个负值 。
  printf()的返回值是其打印输出功能的附带用途,在检查输出错误时可能会用到(如,在写入文件时很常用)。如果一张已满的CD或DVD拒绝写入时,程序应该采取相应的行动, 例如终端蜂鸣30秒。

prntval.c程序:演示如何确定函数的返回值

/* prntval.c -- printf()的返回值 */
#include <stdio.h>
int main(void)
{
	int bph2o = 212;
	int rv;
	
	rv = printf("%d F is water's boiling point.\n", bph2o);
	printf("The printf() function printed %d characters.\n", rv);
	
	return 0;
 } 

运行结果:
在这里插入图片描述
程序说明:
  rv = printf(…); 该语句执行两项任务:打印信息和给变量赋值。注意计算针对所有字符数,包括空格和不可见的换行符(\n) 。

3. 打印较长的字符串

longstrg.c程序: 字符串断行有3种方法

/* longstrg.c ––打印较长的字符串 */
#include <stdio.h>
int main(void)
{ 
	printf("Here's one way to print a ");
	printf("long string.\n");
	printf("Here's another way to print a \
long string.\n");
	printf("Here's the newest way to print a "
			"long string.\n"); 	/* ANSI C */
	return 0;
}

运行结果:
在这里插入图片描述
程序说明:
  方法1:使用多个 printf()语句。因为第1个字符串没有以\n字符结束,所以第2个字符串紧跟第1个字符串末尾输出。
  方法2:用反斜杠()和 Enter(或 Return)键组合来断行。这使得光标移至下一行,而且字符串中不会包含换行符。其效果是在下一行继续输出。但是,下一行代码必须和程序清单中的代码一样从最左边开始。如果缩进该行,比如缩进5个空格,那么这5个空格就会成为字符串的一部分。
  方法3:ANSI C引入的字符串连接。 在两个用双引号括起来的字符串之间用空白隔开, C编译器会把多个字符串看作是一个字符串。要记得在字符串中包含所需的空格。如,“young”“lovers"会成为"younglovers”,而"young " “lovers"才是"young lovers”。

4.4.5 使用scanf()

  C库包含了多个输入函数,scanf()是最通用的一个,因为它可以读取不同格式的数据。从键盘输入的都是文本, 因为键盘只能生成文本字符: 字母、 数字和标点符号。
  scanf()把输入的字符串转换成整数、 浮点数、 字符或字符串, 而printf()正好与它相反, 把整数、 浮点数、 字符和字符串转换成显示在屏幕上的文本。
  scanf()函数使用指向变量的指针。

  • scanf()读取基本变量类型的值, 在变量名前加上一个&;
  • scanf()把字符串读入字符数组中, 不加&。

input.c程序:

// input.c -- 何时使用 & 
#include <stdio.h> 
int 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()函数使用空白(换行符、制表符和空格)把输入分成多个字段。只要在每个输入项之间输入至少一个换行符、制表符或空格即可。
  唯一例外的是%c转换说明。 根据%c, scanf()会读取每个字符, 包括空白。

  scanf()函数所用的转换说明与printf()函数几乎相同。 主要的区别是, 对于float类型和double类型, printf()都使用%f、 %e、 %E、 %g和%G转换说明。 而scanf()只把它们用于float类型, 对于double类型时要使用 l 修饰符。

ASCI C中scanf()的转换说明:
在这里插入图片描述
  可以在百分号和转换字符之间使用修饰符。如果要使用多个修饰符,必须按下表所列顺序书写。

scanf()转换说明中的修饰符:
在这里插入图片描述
在这里插入图片描述

1. 从 scanf() 角度看输入
  假设scanf()根据一个%d转换说明读取一个整数。 scanf()函数每次读取一个字符, 跳过所有的空白字符, 直至遇到第1个非空白字符才开始读取。如果找到一个数字或符号, 它便保存该字符, 并读取下一个字符。scanf()不断地读取和保存字符, 直至遇到非数字字符。然后,scanf()把非数字字符放回输入。这意味着程序在下一次读取输入时, 首先读到的是上一次读取丢弃的非数字字符。 最后, scanf()计算已读取数字(可能还有符号) 相应的数值, 并将计算后的值放入指定的变量中。
  如果使用字段宽度, scanf()会在字段结尾或第1个空白字符处停止读取(满足两个条件之一便停止)
  如果第1个非空白字符是A而不是数字, 会发生什么情况? scanf()将停在那里, 并把A放回输入中, 不会把值赋给指定变量。 程序在下一次读取输入时, 首先读到的字符是A。 如果程序只使用%d转换说明, scanf()就一直无法越过A读下一个字符。 另外, 如果使用带多个转换说明的scanf(), C规定在第1个出错处停止读取输入。
  如果使用%s 转换说明,scanf()会读取除空白以外的所有字符。scanf()根据%s转换说明读取一个单词,即不包含空白字符的字符串。
  当scanf()把字符串放进指定数组中时, 它会在字符序列的末尾加上’\0’, 让数组中的内容成为一个C字符串。
  C 语言还有其他的输入函数, 如 getchar()和 fgets()。 这两个函数更适合处理一些特殊情况, 如读取单个字符或包含空格的字符串。使用scanf()函数,可以读取整数、小数、字符和字符串。

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)。

4.4.6 printf() 和 scanf() 的*修饰符

  printf()和scanf()都可以使用 * 修饰符来修改转换说明的含义,但用法不太一样。

  printf()的 * 修饰符:如果不想预先指定字段宽度, 希望通过程序来指定, 那么可以用 * 修饰符代替字段宽度。 但是要用一个参数告诉函数, 字段宽度应该是多少。也就是说, 如果转换说明是 % * d, 那么参数列表中应包含*和 d对应的值。 这个技巧也可用于浮点值指定精度和字段宽度。

varwid.c程序:

/* varwid.c -- 使用变宽输出字段 */ 
#include <stdio.h>
int 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;
} 

运行结果:
在这里插入图片描述
程序说明:
  变量width提供字段宽度, number是待打印的数字。 因为转换说明中*在d的前面, 所以在printf()的参数列表中, width在number的前面。 同样, width和precision提供打印weight的格式化信息。用户首先输入6, 因此6是程序使用的字段宽度。 类似地, 接下来用户输入8和3, 说明字段宽度是8, 小数点后面显示3位数字。

  scanf()的 * 修饰符:把*放在%和转换字符之间时, 会使得scanf()跳过相应的输出项。

skip2.c程序:

/* skip2.c -- 跳过输入中的前两个整数 */
#include <stdio.h>
int 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;
 } 

运行结果:
在这里插入图片描述
程序说明:
  程序中的scanf()指示:跳过两个整数, 把第3个整数拷贝给n。
  在程序需要读取文件中特定列的内容时,这项跳过功能很有用。

4.4.7 printf() 的用法提示

  想把数据打印成列,使用足够大的固定字段宽度可以让输出整齐美观。例如:printf("%9d %9d %9d\n", val1, val2, val3);
  输出:  12  234  1222
  在两个转换说明中间插入一个空白字符,可以确保即使一个数字溢出了自己的字段,下一个数字也不会紧跟着该数字一起输出。这是因为格式字符串中的普通字符(包括空格)会被打印出来。
  如果要在文字中嵌入一个数字, 通常指定一个小于或等于该数字宽度的字段会比较方便。 这样, 输出数字的宽度正合适, 没有不必要的空白。 例如,printf(“Count Beppo ran %.2f miles in 3 hours.\n”, distance);
  输出:Count Beppo ran 10.22 miles in 3 hours.
  如果把转说明改为%10.2f
  输出:Count Beppo ran    10.22 miles in 3 hours.

4.5 关键概念

  1. char 类型表示单个字符
  2. 字符串表示字符序列。可以把字符串储存在字符数组中。字符串无论表示成字符常量还是储存在字符数组中,都以一个叫做空字符的隐藏字符结尾。
  3. 字符常量是一种字符串形式,即用双引号把字符括起来。
  4. #define 定义数值常量
  5. const 关键字声明的变量为只读变量
  6. 第一个参数中的转换说明必须与后续参数中的值相匹配,确保转换说明的数量和类型与函数的其余参数相匹配。
  7. scanf(),一定要在变量名前面加上地址运算符(&)。
  8. 除了%c 模式(读取下一个字符),scanf()在读取输入时会跳过非空白字符前的所有空白字符(制表符、空格、换行),然后一直读取字符,直至遇到空白字符或与正在读取字符不匹配的字符。
  9. 例子:假如有如下输入行
    -13.45e12# 0 //负号前面有一个空格
      如果其对应的转换说明是%d, scanf()会读取3个字符(-13) 并停在小数点处, 小数点将被留在输入中作为下一次输入的首字符。
      如果其对应的转换说明是%f, scanf()会读取-13.45e12, 并停在#符号处, 而#将被留在输入中作为下一次输入的首字符; 然后, scanf()把读取的字符序列-13.45e12转换成相应的浮点值, 并储存在float类型的目标变量中。
      如果其对应的转换说明是%s, scanf()会读取-13.45e12#, 并停在空格处, 空格将被留在输入中作为下一次输入的首字符; 然后, scanf()把这 10个字符的字符码储存在目标字符数组中, 并在末尾加上一个空字符。
      如果其对应的转换说明是%c, scanf()只会读取并储存第1个字符, 该例中是一个空格 。
  10. strlen()函数(声明在 string.h头文件中)可用于获得字符串的长度(末尾的空字符不计算在内)。
  11. scanf()函数中的转换说明是%s时,可读取一个单词。
  12. C预处理器为预处理器指令(以#符号开始)査找源代码程序,并在开始编译程序之前处理它们。
  13. 处理器根据 #include 指令把另一个文件中的内容添加到该指令所在的位置。
  14. limits.h 和 float.h头文件用#define定义了一组表示整型和浮点型不同属性的符号常量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值