C Prime Plue 第四章 字符串和格式化输入/输出

示例程序

#include <string.h>		//提供strlen()函数的原型
#include <stdio.h>
#define DENSITY 62.4
int main()
{
	float weight = 0.0f;
	float volume = 0.0f;
	int size = 0;
	int letters = 0;
	char name[40] = { 0 };

	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;	
	//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;
}

name是一个可容纳40个字符的数组,name是第一个元素的地址,在用scanf()函数时,使用%s转换说明来处理字符串的输入和输出,name是数组,数组名前不加取地址符&, 而weight前加了&。
string()函数是定义在头文件<string.h>中的函数,可用来获取字符串的长度。

字符串简介

字符串是一个或多个字符的序列

"Zing went the strings of my heart!"

双引号仅用来告知编译器它括起来的是字符串,双引号本身并不是字符串的一部分

char类型数组和null字符

C语言没有专门用于储存字符串的变量类型,字符串都被储存在char类型的数组中
数组由连续的存储单元组成,字符串中的字符被储存在相邻的存储单元中,每个单元储存一个字符。
C语言用空字符 ‘\0’ 标记字符串的结束。‘\0’ 的ASCII码值是0,是非打印字符。
C中的字符串一定以空字符 ‘\0’ 结束,这意味着数组的容量必须必待存储字符串中的字符数多 1 。
数组是同类型数据元素的有序序列。

char ch;//char 类型,分配一个字节
char name[5];//可存储5个char类型字符
#include <stdio.h>
#define PRAISE "You are an extraordinary being"
int main()
{
	char name[40] = { 0 };
	printf("What's your name? \n");
	scanf("%s", name);
	printf("Hello, %s.%s\n", name, PRAISE);
	return 0;
}

如果在使用scanf()函数输入时,会发现scanf()函数在遇到第一个空白(空格、制表符或者换行符)时就不再读取输入,一般而言,根据%s转换说明,scanf()只会读取字符串中的一个单词,而不是一整句。

字符串和字符
'x'; //是基本类型char, 一个字符,占一个字节
"x"; //派生类型(char数组),两个字符,占两个字节('x'和'\0')

在这里插入图片描述

strlen()函数

#include <stdio.h>
#include <string.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);
	printf("Your name of %zd letters occupies %zd memory cells.\n",
	 strlen(name), sizeof(name));//可以在参数之间断为两行
	//strlen(name):输入的字符串长度   可以用%zd或者(%u %ul)(早期的C)
	//sizeof(name):name数组长度
	printf("The phrase of praise has %zd letters", strlen(PRAISE));
	//strlen(PRAISE) = 31
	printf("and occupies %zd memeory cells.\n", sizeof(PRAISE));
	//sizeof(PRAISE) = 32 ,比strlen(name)多1,即'\0'
	return 0;
}

string.h头文件中包含了strlen()函数和其他一些与字符串相关的函数
printf()和scanf()都隶属标准输入和输出函数,使用stdio.h头文件

处理很长的printf()语句方法,第1种方法是将printf()语句分为两行(可以在参数之间断为两行,但是不要在双引号中的字符串中间断开)。第 2 种方法是使用两个printf()语句打印一行内容,只在第2条printf()语句中使用换行符(\n)。

sizeof 运算符的返回类型%zd, 这对于strlen()同样适用。

sizeof运算符的运算对象是类型时,圆括号必不可少;运算对象是特定量时,括号可有可无

//运算对象是类型
sizeof(float);
sizeof(char);
//sizeof float  //error!

//运算对象是特定量时
sizeof name;
sizeof 3.21;
sizeof(6.25);

常量和C预处理器

预处理器可用来定义常量,只需在程序顶部添加下面一行

#define TAXRATE 0.015

编译程序时,程序中所有的TAXRATE都会被替换成0.015。这一过程被称为编译时替换在运行程序时,程序中所有的替换均已完成。通常,这样定义的常量也称为明示常量
注意格式,首先是**#define**,接着是符号常量名(TAXRATE),然后是符号常量的值(0.015)(注意,其中并没有=符号)。末尾不用加分号,用大写表示符号常量
符号常量的命名规则与变量相同。可以使用大小写字母、数字和下划线字符,首字符不能为数字。

#include <stdio.h>
#define PI 3.14159
int main()
{
	float area = 0.0f;
	float circum = 0.0f;
	float radius = 0.0f;

	printf("What's 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;
}

#define指令还可定义字符和字符串常量前者使用单引号,后者使用双引号

#define BEEP '\a'
#define TEE 'T'
#define ESC '\033'
#define OOPS "Now you have done it!"

const 限定符

const关键字用于限定一个变量为只读

const int MONTHS = 12;//MONTHS在程序中不可更改,值为12

只读值:可以在计算中使用MONTHS,可以打印MONTHS,但是不能更改MONTHS的值。

明示变量

C头文件limits.h和float.h分别提供了与整数类型和浮点类型大小限制相关的详细信息。每个头文件都定义了一系列供实现使用的明示常量
如果在程序中包含limits.h头文件,就可编写下面的代码:

ptintf("Maximum int value on this system = %d\n", INT_MAX);

如果系统使用4字节的int,limits.h头文件会提供符合4字节int的INT_MAX和INT_MIN。
类似地,float.h头文件中也定义一些明示常量,如FLT_DIG和DBL_DIG,分别表示float类型和double类型的有效数字位数

//defines.c  使用 limit.h 和 float.h 头文件中定义的明示常量
#include <stdio.h>
#include <limits.h>
#include <float.h>
int main()
{
	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 double: %e\n", DBL_MAX);
	printf("float precision = %d digits\n", FLT_DIG);
	printf("float epsilon = %e\n", FLT_EPSILON);
	return 0;
}

printf()和scanf()

printf()函数和scanf()函数能让用户可以与程序交流,它们是输入/输出函数,或简称为I/O函数。

printf()函数

请求printf()函数打印数据的指令要与待打印数据的类型相匹配
打印整数时使用%d,打印字符时使用%c。这些符号被称为转换说明,它们指定了如何把数据转换成可显示的形式。

//printout.c  使用转换说明
#include <stdio.h>
#define PI 3.141593
int main()
{
	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!thos art too dear for my possessing,\n");
	printf("%c%d\n", '$', 2 * cost);
	return 0;
}

格式字符串应包含每个待打印项对应的转换说明,待打印项可以是变量、常量,甚至是在打印之前先要计算的表达式。

printf("The %d contestants ate %f berry pies.\n", number, pies);

格式字符串是双引号括起来的内容。上面语句的格式字符串包含了两个待打印项number和poes对应的两个转换说明。

printf("The value of pi is %f.\n", PI);

该语句中,待打印项列表只有一个项——符号常量PI。

格式字符串中的转换说明一定要与后面的每个项相匹配,若忘记这个基本要求会导致严重的后果。

如果只打印短语或句子,就不需要使用任何转换说明。如果只打印数据,也不用加入说明文字。

printf("Farewell!thou art too dear for my possessing,\n");
printf("%c%d\n", '$', 2 * cost);

打印%符号,使用两个%符号

pc = 2 * 6;
printf("Only %d%% of Sally's gribbles were edible.\n", pc);

printf()的转换说明修饰符

在%和转换字符之间插入修饰符可修饰基本的转换说明。

类型可移植性

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  字段宽度
#include <stdio.h>
#define PAGES 959
int main()
{
	printf("*%d*\n", PAGES);
	printf("*%2d*\n", PAGES);
	printf("*%10d*\n", PAGES);
	printf("*%-10d*\n", PAGES);
	return 0;
}

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

//float.c  一些浮点型修饰符的组合
#include <stdio.h>
int main()
{
	const double RENT = 3852.99;
	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;
}

第1个转换说明是 %f。在这种情况下,字段宽度和小数点后面的位数均为系统默认设置,即字段宽度是容纳带打印数字所需的位数和小数点后打印6位数字
第2个转换说明是 %e。默认情况下,编译器在小数点的左侧打印1个数字,在小数点的右侧打印6个数字
第3个转换说明是 %4.2f小数点后保留两位,因为带打印的字符共有7位所以会扩展为7位。
第4个转换说明是%3.1f,小数点获保留一位,会进行四舍五入,然后保留一位小数,带打印字符共6位,仍会打印6位。
第5个转换说明是%10.3f,小数点后保留3位,共打印10位,默认右对齐,该数前有2个空格。
第6个转换说明是%10.3E,小数点后保留3位,进行四舍五入,以科学计数法的方式进行打印,共打印10位。
第7个转换说明中包含了+标记,这使得打印的值前面多了一个代数符号(+)。
第8个转换说明是 %010.2f0标记使得打印的值前面以0填充以满足字段要求,保留两位小数,共打印10位

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


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

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


虽然第1个转换说明是 %2s,但是字段被扩大为可容纳字符串中的所有字符。还需注意,精度限制了待打印字符的个数。.5告诉printf()只打印5个字符。另外, - 标记使得文本左对齐输出

转换说明的意义

转换说明把以二进制格式储存在计算机中的值转换成一系列字符(字符串)以便于显示。

转换不匹配
//intconv.c  一些不匹配的类型转换
#include <stdio.h>
#define PAGES 336
#define WORDS 65618
int main()
{
	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;
}

在这里插入图片描述
第1行,num变量对应的转换说明%hd和%hu输出的结果都是336。
第2行mnum变量对应的转换说明%ud(无符号)输出的结果却为65200
首先,short int的大小是2字节;
其次,系统使用二进制补码来表示有符号整数。数字0~32767代表它们本身,而数字32768~65535则表示负数。
其中,65535表示-1,65534表示-2,以此类推。因此,-336表示为65200(即, 65536-336)。
第3行演示了如果把一个大于255的值转换成字符会发生什么情况,short int是2字节,char是1字节。当printf()使用%c打印336时,它只会查看储存336的2字节中的后1字节这种截断相当于用一个整数除以256,只保留其余数。在这种情况下,余数是80,对应的ASCII值是字符P。
第4行,在该系统中打印比short int类型最大整数(32767)更大的整数(65618)。这次,计算机也进行了求模运算。在本系统中,应把数字65618储存为4字节的int类型值。用%hd转换说明打印时, printf()只使用最后2个字节。这相当于65618除以65536的余数。这里,余数是82。

//floatcnv.c 不匹配的浮点型转换
#include <stdio.h>
int main()
{
	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转换说明让printf()函数认为待打印的值是double类型(本系统中double为8字节)。 当printf() 查看n3(本系统中是4字节的值)时,除了查看n3的4字节外,还会查看n3相邻的4字节,共8字节单元。接着,它将8字节单元中的位组合解释成浮点数(如,把一部分位组合解释成指数)。因此,即使n3的位数正确,根据%e转换说明和%ld转换说明解释出来的值也不同。最终得到的结果是无意义的值。
第1行也说明了前面提到的内容:float类型的值作为printf()参数时会被转换成double类型。 在本系统中,float是4字节,但是为了printf()能正确地显示该值,n1被扩成8字节。
第2行输出显示,只要使用正确的转换说明,printf()就可以打印n3和n4。
第3行输出显示,如果printf()语句有其他不匹配的地方,即使用对了转换说明也会生成虚假的结果。用%ld转换说明打印浮点数会失败,但是在这里,用%ld打印long类型的数也失败了,问题出在C如何把信息传递给函数。具体情况因编译器实现而异。

参数传递

函数调用如下:

printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);

该调用告诉计算机把变量n1、n2、、n3和n4的值传递给程序。这是一种常见的参数传递方式。程序把传入的值放入被称为栈的内存区域计算机根据变量类型(不是根据转换说明)把这些值放入栈中。 因此,n1被储存在栈中,占8字节(float类型被转换成double类型)。同样,n2也在栈中占8字节,而n3和n4在栈中分别占4字节。然后,控制转到printf()函数。该函数根据转换说明(不是根据变量类型)从栈中读取值。 %ld转换说明表明printf()应该读取4字节,所以printf()读取栈中的前4字节作为第1个值。这是n1的前半部分,将被解释成一个long类型的整数。根据下一个%ld转换说明,printf()再读取4字节,这是n1的后半部分,将被解释成第2个long类型的整数(见图4.9)。类似地,根据第3个和第4个%ld,printf()读取n2的前半部分和后半部分,并解释成两个long类型的整数。因此,对于n3和n4,虽然用对了转换说明,但printf()还是读错了字节。

printf()的返回值

printf()函数也有一个返回值,它返回打印字符的个数。如果有输出错误,printf()则返回一个负值(printf()的旧版本会返回不同的值)。printf()的返回值是其打印输出功能的附带用途,通常很少用到,但在检查输出错误时可能会用到(如,在写入文件时很常用)。

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


首先,程序用rv = printf(…);的形式把printf()的返回值赋给rv。因此,该语句执行了两项任务:打印信息和给变量赋值。
其次,注意计算针对所有字符数,包括空格和不可见的换行符(\n)。

打印较长的字符串
//longstrg.c  打印较长的字符串
#include <stdio.h>
int main()
{
	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");
	return 0;
}

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

使用scanf()

scanf()和 printf()类似,也使用格式字符串和参数列表。scanf()中的格式字符串表明字符输入流的目标数据类型。两个函数主要的区别在参数列表中。printf()函数使用变量、常量和表达式,而scanf()函数使用指向变量的指针。
如果用scanf()读取基本变量类型的值,在变量名前加上一个&;
如果用scanf()把字符串读入字符数组中,不要使用&。

//input.c  何时使用&
#include <stdio.h>
int main()
{
	int age = 0;
	float assets = 0.0f;
	char pet[30] = { 0 };
	printf("Enter your age, aeests, and favourite 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修饰符。

1.从scanf()角度看输入

假设scanf()根据一个%d转换说明读取一个整数。scanf()函数每次读取一个字符,跳过所有的空白字符,直至遇到第1个非空白字符才开始读取。因为要读取整数,所以scanf()希望发现一个数字字符或者一个符号(+或-)。如果 找到一个数字或符号,它便保存该字符,并读取下一个字符。如果下一个字符是数字,它便保存该数字并读取下一个字符。scanf()不断地读取和保存字符,直至遇到非数字字符。如果遇到一个非数字字符,它便认为读到了整数的末尾。然后,scanf()把非数字字符放回输入。 这意味着程序在下一次读取输入时,首先读到的是上一次读取丢弃的非数字字符。最后,scanf()计算已读取数字(可能还有符号)相应的数值,并将计算后的值放入指定的变量中。
如果使用字段宽度,scanf()会在字段结尾或第1个空白字符处停止读取(满足两个条件之一便停止)。
如果第1个非空白字符是A而不是数字,scanf()将停在那里,并把A放回输入中,不会把值赋给指定变量。程序在下一次读取输入时,首先读到的字符是A。如果程序只使用%d转换说明, scanf()就一直无法越过A读下一个字符。另外,如果使用带多个转换说明的scanf(),C规定在第1个出错处停止读取输入。
如果使用 %s 转换说明,scanf()会读取除空白以外的所有字符。scanf()跳过空白开始读取第 1 个非空白字符,并保存非空白字符直到再次遇到空白。这意味着 scanf()根据%s 转换说明读取一个单词,即不包含空白字符的字符串。如果使用字段宽度,scanf()在字段末尾或第1个空白字符处停止读取。无法利用字段宽度让只有一个%s的scanf()读取多个单词。最后要注意一点:当scanf()把字符串放进指定数组中时,它会在字符序列的末尾加上’\0’,让数组中的内容成为一个C字符串。

2.格式字符串中的普通字符

scanf()函数允许把普通字符放在格式字符串中。除空格字符外的普通字符必须与输入字符串严格匹配

scanf("%d, %d", &n, &m);

scanf()函数将其解释成:用户将输入一个数字、一个逗号,然后再输入一个数字。
格式字符串中的空白意味着跳过下一个输入项前面的所有空白。“所有空白”的概念包括没有空格的特殊情况。

scanf("%d,%d", &n, &m);

除了%c,其他转换说明都会自动跳过待输入值前面所有的空白。
以下的两个语句的行为相同:

scanf("%d%d", &n, &m);
scanf("%d %d", &n, &m);

对于%c,在格式字符串中添加一个空格字符会有所不同。
scanf(“%c”, &ch)从输入中的第1个字符开始读取,而scanf(" %c", &ch)则从第1个非空白字符开始读取。

3.scanf()的返回值

scanf()函数返回成功读取的项数。如果没有读取任何项,且需要读取一个数字而用户却输入一个非数值字符串,scanf()便返回0。当scanf()检测到“文件结尾”时,会返回EOF(EOF是stdio.h中定义的特殊值,通常用#define指令把EOF定义为-1)。

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

//varwid.c  使用变宽输出字段
#include <stdio.h>
int main()
{
	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()跳过相应的输出项。

//skiptwo.c  跳过输入中的前两个整数
#include <stdio.h>
int main()
{
	int n = 0;
	printf("Please enter three integers:\n");
	scanf("%*d %*d %d", &n);
	printf("The last integer was %d\n", n);
	return 0;
}


在程序需要读取文件中特定列的内容时,这项跳过功能很有用。

printf()的用法提示

想把数据打印成列,指定固定字段宽度很有用。因为默认的字段宽度是待打印数字的宽度,如果同一列中打印的数字位数不同,打印出来的数字可能参差不齐。
使用足够大的固定字段宽度可以让输出整齐美观。
在两个转换说明中间插入一个空白字符,可以确保即使一个数字溢出了自己的字段,下一个数字也不会紧跟该数字一起输出。
如果要在文字中嵌入一个数字,通常指定一个小于或等于该数字宽度的字段会比较方便。这样,输出数字的宽度正合适,没有不必要的空白。

printf("Count Beppo ran %.2f miles in 3 hours.\n", distance);
printf("Count Beppo ran %10.2f miles in 3 hours.\n", distance);//会出现一段空格

关键概念

C语言用char类型表示单个字符,用字符串表示字符序列。字符常量是一种字符串形式,即用双引号把字符串括起来:“Good luck, my friend”。可以把字符串存储在字符数组(由内存中相邻的字节组成)中。字符串,无论是表示成字符常量还是储存在字符数组中,都以一个叫做空字符的隐藏字符结尾。
在程序中,最好用#define 定义数值常量,用 const 关键字声明的变量为只读变量。在程序中使用符号常量(明示常量),提高了程序的可读性和可维护性。
C 语言的标准输入函数(scanf())和标准输出函数(printf())都使用一种系统。在该系统中,第1个参数中的转换说明必须与后续参数中的值相匹配。 必须格外小心,确保转换说明的数量和类型与函数的其余参数相匹配。对于scanf(),一定要记得在变量名前加上地址运算符(&)。
空白字符(制表符、空格和换行符)在 scanf()处理输入时起着至关重要的作用。除了%c 模式(读取下一个字符),scanf()在读取输入时会跳过非空白字符前的所有空白字符,然后一直读取字符,直至遇到空白字符或与正在读取字符不匹配的字符。
假设有如下输入行:
" -13.45e12# 0"
如果其对应的转换说明是%d,scanf()会读取3个字符(-13)并停在小数点处,小数点将被留在输入中作为下一次输入的首字符。如果其对应的转换说明是%f,scanf()会读取-13.45e12,并停在#符号处,而#将被留在输入中作为下一次输入的首字符;然后,scanf()把读取的字符序列-13.45e12转换成相应的浮点值,并储存在float类型的目标变量中。如果其对应的转换说明是%s,scanf()会读取-13.45e12#,并停在空格处,空格将被留在输入中作为下一次输入的首字符;然后,scanf()把这 10个字符的字符码储存在目标字符数组中,并在末尾加上一个空字符。如果其对应的转换说明是%c,scanf()只会读取并储存第1个字符,该例中是一个空格。

编程练习

1.编写一个程序,提示用户输入名和姓,然后以“名,姓”的格式打印出来。

#include <stdio.h>
int main()
{
	char firstname[40], lastname[40];
	printf("Input your firstname: ");
	scanf("%s", firstname);
	printf("Input your lastname: ");
	scanf("%s", lastname);
	printf("Your name is %s,%s\n", firstname, lastname);
	return 0;
}

2.编写一个程序,提示用户输入姓名,并执行一下操作:
a.打印姓名,包括双引号;
b.在宽度为20的字段右端打印姓名,包括双引号;
c.在宽度为20的字段左端打印姓名,包括双引号;
d.在比姓名宽度宽3的字段中打印姓名。

#include <stdio.h>
#include <string.h>
int main()
{
	char name[40];
	printf("Input your name: ");
	scanf("%s", name);
	printf("\"%s\"\n", name);
	printf("\"%20s\"\n", name);
	printf("\"%-20s\"\n", name);
	int width = (int)strlen(name) + 3;
	printf("\"%*s\"\n", width, name);
	return 0;
}

3.编写一个程序,读取一个浮点数,首先以小数点记数法打印,然后以指数记数法打印。用下面的格式进行输出(系统不同,指数记数法显示的位数可能不同):
a.输入21.3或2.1e+001;
b.输入+21.290或2.129E+001;

#include <stdio.h>
int main()
{
	double num = 0.0;
	printf("Enter a floating number: ");
	scanf("%lf", &num);
	printf("num = %f\n", num);
	printf("num = %e\n", num);
	return 0;
}

4.编写一个程序,提示用户输入身高(单位:英寸)和姓名,然后以下面的格式显示用户刚输入的信息:
Dabney, you are 6.208 feet tall
使用float类型,并用/作为除号。

#include <stdio.h>
int main()
{
	float height = 0.0f;
	char name[40] = { 0 };

	printf("Enter your height in inches: ");
	scanf("%f", &height);
	printf("Enter your name: ");
	scanf("%s", name);
	printf("%s, you are %.3f feet tall\n", name, height / 12.0f);
	
	return 0;
}

5.编写一个程序,提示用户输入以兆位每秒(Mb/s)为单位的下载速度和以兆字节(MB)为单位的文件大小。程序中应计算文件的下载时间。注意,这里1字节等于8位。使用float类型,并用/作为除号。该程序要以下面的格式打印 3 个变量的值(下载速度、文件大小和下载时间),显示小数点后面两位数字:

At 18.12 megabits per second, a file of 2.20 megabytesdownloads in 0.97 seconds.

#include <stdio.h>
int main()
{
	float speed = 0.0f;
	float size = 0.0f;
	float time = 0.0f;

	printf("Enter the speed(Mb/s) : ");
	scanf("%f", &speed);
	printf("Enter the file size(MB): ");
	scanf("%f", &size);
	time = size / speed * 8;
	printf("At %.2f megabits per second, a file of %.2f megaby"
	"tes downloads in %.2f seconds.\n", speed, size, time);

	return 0;
}

6.编写一个程序,先提示用户输入名,然后提示用户输入姓。在一行打
印用户输入的名和姓,下一行分别打印名和姓的字母数。字母数要与相应名
和姓的结尾对齐,如下所示:
Melissa Honeybee
7     8
接下来,再打印相同的信息,但是字母个数与相应名和姓的开头对齐,
如下所示:
Melissa Honeybee
7   8

#include <stdio.h>
#include <string.h>
int main()
{
	char firstname[40] = { 0 };
	char lastname[40] = { 0 };
	int len_first = 0;
	int len_last = 0;

	printf("Enter your firstname: ");
	scanf("%s", firstname);
	printf("Enter your lastname: ");
	scanf("%s", lastname);

	len_first = (int)strlen(firstname);
	len_last = (int)strlen(lastname);

	printf("%s %s\n", firstname, lastname);
	printf("%*d %*d\n", -len_first, len_first, -len_last, len_last);
	printf("%s %s\n", firstname, lastname);
	printf("%*d %*d\n", len_first, len_first, len_last, len_last);

	return 0;
}

7.编写一个程序,将一个double类型的变量设置为1.0/3.0,一个float类型的变量设置为1.0/3.0。分别显示两次计算的结果各3次:一次显示小数点后面6位数字;一次显示小数点后面12位数字;一次显示小数点后面16位数字。程序中要包含float.h头文件,并显示FLT_DIG和DBL_DIG的值。1.0/3.0的值与这些值一致吗?

#include <stdio.h>
#include <float.h>
int main()
{
	float n1 = 1.0f / 3.0f;
	double n2 = 1.0 / 3.0;

	printf(" float: %.6f \ndouble: %.6f\n", n1, n2);
	printf(" float: %.12f \ndouble: %.12f\n", n1, n2);
	printf(" float: %.16f \ndouble: %.16f\n", n1, n2);
	printf("FLT_DIG: %d\n", FLT_DIG);
	printf("DBL_DIG: %d\n", DBL_DIG);

	return 0;
}

8.编写一个程序,提示用户输入旅行的里程和消耗的汽油量。然后计算并显示消耗每加仑汽油行驶的英里数,显示小数点后面一位数字。接下来,使用1加仑大约3.785升,1英里大约为1.609千米,把单位是英里/加仑的值转换为升/100公里(欧洲通用的燃料消耗表示法),并显示结果,显示小数点后面 1 位数字。注意,美国采用的方案测量消耗单位燃料的行程(值越大越好),而欧洲则采用单位距离消耗的燃料测量方案(值越低越好)。使用#define 创建符号常量或使用 const 限定符创建变量来表示两个转换系数。

#include <stdio.h>
#define GALLON 3.758
#define MILE 1.609
int main()
{
	double gallon = 0.0;
	double mile = 0.0;

	printf("Input gallons: ");
	scanf("%lf", &gallon);
	printf("Input miles: ");
	scanf("%lf", &mile);
	printf("Miles per gallon: %.1f\n", mile / gallon);
	printf("Litres per 100 km: %.1f\n", (gallon * GALLON / (mile * MILE)) * 100);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值