【C语言基础】字符串和格式化输入/输出
字符串的介绍
数组由连续的存储单元组成,字符串中的字被存储在相邻的存储单元中,每个单元存储一个字符。在数组末尾位置的字符为\0,代表字符串的结束。
#define _CRT_SECURE_NO_WARNINGS
#include<stdlib.h>
#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);
printf("The phrase of praise has %zd letters ",strlen(PRAISE));
printf("and occupies %zd memory cells.\n", sizeof PRAISE);
// system("pause");
return 0;
}
// 一般来说,根据%是转换说明,scanf只读取第一个单词,而不是一整句
// 当输入ding c时,scanf函数只读取到ding
// What's your name? ding c
// Hello, ding.You are an extraordinary being.
// Your name of 4 letters occupies 40 memory cells.
// The phrase of praise has 31 letters and occupies 32 memory cells.
// 当输入ding_c时,scanf可以读取到ding_c
// What's your name? ding_c
// Hello, ding_c.You are an extraordinary being.
// Your name of 6 letters occupies 40 memory cells.
// The phrase of praise has 31 letters and occupies 32 memory cells.
// 请按任意键继续. . .
// string.h头文件包含多个与字符串相关的函数原型,包括strlen()
// 用 strlen()得出的也是字符串中的字符数(包括空格和标点符号)。
// 然而,sizeof运算符给出的数更大,因为它把字符串末尾不可见的空字符也计算在内。
// 该程序并未明确告诉计算机要给字符串预留多少空间,所以它必须计算双引号内的字符数。
// sizeof 运算符,它以字节为单位给出对象的大小。
// strlen()函数给出字符串中的字符长度。因为 1 字节储存一个字符,读者可能认为把两种方法应用于字符串得到的结果相同,但事实并非如此。
在用scanf输入字符串时,不用在字符串末尾放入空字符,scanf在读取输入时就已经完成了这项工作,但是scanf在遇到空格后,会自动结束,也就是scanf只会读取一个单词,祥见上述程序的注解。
字符串和字符:
字符串常量"x":x+\0
字符常量'x':x
sizeof运算符:以字节为单位给出对象大小,计算范围包括空格、标点符号、空字符
strlen()函数:给出字符串中字符的长度,计算范围包括空格、标点符号
常量和c预处理器
在程序运行过程中,一个数会多次参与运行,且为了提高程序的可读性,一般设置一个变量来代替常量。
float pi=3.14;
circum=pi*diameter;
#define _CRT_SECURE_NO_WARNINGS
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<limits.h> // 整型限制
#include<float.h> // 浮点型限制
#define PI 3.14159
int main()
{
float area, circum, radius;
// const int pi=3.14;//const是不可修改的变量
// printf("pi=%d",pi);
// pi=5;
// // 编辑器警告,表达式必须是可修改的左值C/C++
// // 但是还是可以运行
// printf("new_pi=%d\n",pi);
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);
// What is the radius of your pizza?
// 12
// Your basic pizza parameters are as follows:
// circumference = 75.40, area = 452.39
// 请按任意键继续. . .
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("Smallest normal float: %e\n", FLT_MIN);
printf("float precision = %d digits\n", FLT_DIG);
printf("float epsilon = %e\n", FLT_EPSILON);
// Some number limits for this system:
// Biggest int: 2147483647
// Smallest long long: -9223372036854775808
// One byte = 8 bits on this system.
// Largest double: 1.797693e+308
// Smallest normal float: 1.175494e-038
// float precision = 6 digits
// float epsilon = 1.192093e-007
system("pause");
return 0;
}
在上述程序中运行过程中,会反复调用pi,于是可以用c预处理器处理,只需要在主函数上面添加一行代码:
#define PI 3.14159
编译程序时,程序中所有的PI都会被替换成3.14159,这一过程被称为编译时替换。在运行程序时,程序中所有的替换均已完成。通常,这样定义的常量也称为明示常量。
格式:#define 符号常量名 符号常量的值
其中dfine还可以定义字符和字符串常量,前者使用单引号,后者使用双引号,如:
#define BEEP "Now you have done it!"
#define BEEP 'T'
const限定符
const定义的变量是一个只读变量,如:
const int a=12;//其中a在运行过程中值不能被更改,但在运行过程中可以打印,参与运算等
明示常量
C头文件limits.h和float.h分别提供了与整数类型和浮点类型大小限制相关的详细信息。每个头文件都定义了一系列供实现使用的明示常量。例如,limits.h头文件包含以下类似的代码:
#define INT_MAX +32767
#define INT_MIN -32768
limits.h中的一些明示常量,如下表所示:
float.h中的一些明示常量,如下表所示:
printf()和scanf()
这两个函数能让用户可以与程序交流,它们是输入/输出函数,或者简称I/O函数,虽然printf()是输出函数,scanf()是输入函数,但是它们的工作原理几乎相同。两个函数都使用格式字符串和参数列表。
printf()
printf打印输出函数,但打印时打印数据的指令要与待打印数据的类型相匹配,此时需要一些表示转换说明的符号,转换说明及其打印的输出结果:
printf( 格式字符串, 待打印项1, 待打印项2,...);
在%和转换字符之间插入修饰符可修饰基本的转换说明。
#include <stdio.h>
#define PAGES 959
#define BLURB "Authentic imitation!"
int main(void)
{
const double RENT = 3852.99;// const变量
// 打印数字
printf("*%f*\n", RENT);
// 打印浮点数
printf("*%e*\n", RENT);
// 打印科学计数法的方法输出
printf("*%4.2f*\n", RENT);
// 打印浮点数,字段宽度占4个字符,其中小数点2位
printf("*%3.1f*\n", RENT);
// 打印浮点数,字段宽度占3个字符,其中小数点1位
printf("*%10.3f*\n", RENT);
// 打印浮点数,字段宽度占10个字符,其中小数点3位
printf("*%10.4E*\n", RENT);
// 打印科学计数法的方法输出*3.8530E+003*,小数点后保留四位
printf("*%+4.2f*\n", RENT);
// 打印项显示符号,正的显示+,负的显示-,有10个字宽,字段宽度占10个字符,其中小数点3位
printf("*%010.2f*\n", RENT);
// 打印浮点数,字段宽度占10个字符,其中小数点3位
printf("*%d*\n", PAGES);
// %d:以十进制数打印
printf("*%2d*\n", PAGES);
// 打印以十进制数,字段宽度占2个字符
printf("*%10d*\n", PAGES);
// 打印使用10个字宽
printf("*%-10d*\n", PAGES);
// 打印项左对齐,有10个字宽
// 打印字符串
printf("[%2s]\n", BLURB);
// 打印字宽为2的字符,由于字符串长度大于2,则整个都会打印
printf("[%24s]\n", BLURB);
// 打印字宽位24的字符串
printf("[%24.5s]\n", BLURB);
// 打印字宽为24的字符串,输出前5位
printf("[%-24.5s]\n", BLURB);
// 靠左打印,打印字宽为24的字符串,输出前5位
}
// 试试如何用一个语句打印以下格式的内容:
// The NAME family just may be $XXX.XX dollars richer!
// 这里,NAME和XXX.XX代表程序中变量(如name[40]和cash)的值。
// 可参考以下代码:
// printf("The %s family just may be $%.2f richer!\n",name,cash);
# include<stdio.h>
int main()
{
char name[40];
float cash;
printf("please,write name:");
scanf("%s",name);
fflush (stdin);//清理缓冲区
printf("enter cash:");
scanf("%f",&cash);
printf("The %s family just may be $%.2f richer!\n",name,cash);
}
当在C语言中需要两个及以上scanf,运行时却发现只能第一个可以输入,通过查询得知在C语言中,如果使用字符型变量(char类型)时在有连续输入两个及以上的情况下,很容易因为出现垃圾字符二导致程序的流程非法。因为第一个scanf输入后当我们点击enter时第一个存放字符串的(str1)并没有接受这个回车符,而是存入缓冲区中了,当执行第二个scanf时这个回车符就赋给(str2)了所以程序没有提示输入字符就直接执行下面程序了。解决方法:可以使用fflush (stdin);函数 此函数是清除缓冲区的,将此函数放到第一个scanf输入函数后即可。
#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);
// num as short and unsigned short: 336 336
printf("-num as short and unsigned short: %hd %hu\n", mnum,mnum);
// -num as short and unsigned short: -336 65200
printf("num as int and char: %d %c\n", num, num);
// num as int and char: 336 P
printf("WORDS as int, short, and char: %d %hd %c\n",WORDS,WORDS,WORDS);
// WORDS as int, short, and char: 65618 82 R
return 0;
/*
代码说明:
首先,short int的大小是2字节;其次,系统使用二进制补码来表示有符号整数。这种方法,数字0~32767代表它们本身,而数字32768~65535则表示负数。其中,65535表示-1,65534表示-2,以此类推。因此,-336表示为65200(即, 65536-336)。所以被解释成有符号int时,65200代表-336;而被解释成无符号int时,65200则代表65200。一定要谨慎!一个数字可以被解释成两个不同的值。尽管并非所有的系统都使用这种方法来表示负整数,但要注意一点:别期望用%u转换说明能把数字和符号分开。
*/
}
scanf()
scanf()和 printf()类似,也使用格式字符串和参数列表。scanf()中的格式字符串表明字符输入流的目标数据类型。两个函数主要的区别在参数列表中。printf()函数使用变量、常量和表达式,而scanf()函数使用指向变量的指针。这里,读者不必了解如何使用指针,只需记住以下两条简单的规则:
如果用scanf()读取基本变量类型的值,在变量名前加上一个&;
如果用scanf()把字符串读入字符数组中,不要使用&。
复习题
2.假设下列示例都是完整程序中的一部分,它们打印的结果分别是什么?
a.printf("He sold the painting for $%2.2f.\n", 2.345e2);
// He sold the painting for $234.50.
// 2.345e2表示科学计数发,原始数值为234.5,打印时占两个字宽,小数两位
b.printf("%c%c%c\n", 'H', 105, '\41');
// Hi!:第一个字符正常输出,第二个对应ascll字母表查出是i,第三个所有的数字加上\按八进制表示,转换成十进制是33,对应ascll字母表查出是!
c.#define Q "His Hamlet was funny without being vulgar."printf("%s\nhas %d characters.\n", Q, strlen(Q));
// His Hamlet was funny without being vulgar.
//has 42 characters.
d.printf("Is %2.2e the same as %2.2f?\n", 1201.0, 1201.0);
// Is 1.20e+003 the same as 1201.00?
3.在第2题的c中,要输出包含双引号的字符串Q,应如何修改:
#define Q "\"His Hamlet was funny without being vulgar.\""
printf("%s\nhas %d characters.\n", Q, strlen(Q));
#define Q "His Hamlet was funny without being vulgar."
printf("\"%s\"\nhas %d characters.\n", Q, strlen(Q));
6.格式打印:
a.一个字段宽度与位数相同的十进制整数
// %d
b.一个形如8A、字段宽度为4的十六进制整数
// %4x
c.一个形如232.346、字段宽度为10的浮点数
// %10.3f
d.一个形如2.33e+002、字段宽度为12的浮点数
// %12.3e
e.一个字段宽度为30、左对齐的字符串
// %-30s
7.打印下面各项内容要分别使用什么转换说明?
a.字段宽度为15的unsigned long类型的整数
// %15lu
b.一个形如0x8a、字段宽度为4的十六进制整数
// %#4x
c.一个形如2.33E+02、字段宽度为12、左对齐的浮点数
// %-12.2E
d.一个形如+232.346、字段宽度为10的浮点数
// %+10.3f
e.一个字段宽度为8的字符串的前8个字符
// %8.8s
8.打印下面各项内容要分别使用什么转换说明?
a.一个字段宽度为6、最少有4位数字的十进制整数
// %6.4d
b.一个在参数列表中给定字段宽度的八进制整数
// %*o
c.一个字段宽度为2的字符
// %2c
d.一个形如+3.13、字段宽度等于数字中字符数的浮点数
// %+0.2f
e.一个字段宽度为7、左对齐字符串中的前5个字符
// %-7.5s
编程练习
1.编写一个程序,提示用户输入名和姓,然后以“名,姓”的格式打印出来。
#include<stdio.h>
int main()
{
char surname[20];
char name[20];
printf("请输入用户的姓:");
scanf("%s",surname);
fflush (stdin);
printf("请输入用户的名:");
scanf("%s",name);
printf("姓名为:%s,%s",surname,name);
}
2.编写一个程序,提示用户输入名和姓,并执行一下操作:
a.打印名和姓,包括双引号;
b.在宽度为20的字段右端打印名和姓,包括双引号;
c.在宽度为20的字段左端打印名和姓,包括双引号;
d.在比姓名宽度宽3的字段中打印名和姓。
#include<stdio.h>
int main()
{
char surname[20];
int size;
printf("请输入用户的姓名:");
scanf("%s",surname);
size=strlen(surname)+3;
printf("姓名为:\"%s\"\n",surname);
printf("姓名为:\"%20s\"\n",surname);
printf("姓名为:\"%-20s\"\n",surname);
printf("姓名为:\"%s\"\n",surname);
printf("%*s\n",size,surname);
}
// printf("%*s%s",xx,xx,xx);
//printf("%*s",xx,xx);“*”表示的是占多少位置,格式控制符“s”表示的是字符串格式控制,
//第一个参数是占多少位,第二个参数为字符串,两个结合起来的意思就是字符串占多少位,如果不够那么多余位左边用空格来补齐。
#include<stdio.h>
int main()
{
char x1[20]="ding_c";
char x2='a';
printf("%*s\n",10,x1);
printf("%*c\n",4,x2);
}
// ding_c
// a
//请按任意键继续. . .
3.编写一个程序,读取一个浮点数,首先以小数点记数法打印,然后以指数记数法打印。用下面的格式进行输出(系统不同,指数记数法显示的位数可能不同):
a.输入21.3或2.1e+001;
b.输入+21.290或2.129E+001;
#include<stdio.h>
int main()
{
float x1,x2;
printf("请输入一个小数:");
scanf("%f",&x1);
fflush (stdin);
printf("%.1e\n",x1);
printf("请输入一个正的小数:");
scanf("%f",&x2);
printf("%.3E\n",x2);
}
4.编写一个程序,提示用户输入身高(单位:英寸)和姓名,然后以下面的格式显示用户刚输入的信息:
Dabney, you are 6.208 feet tall
使用float类型,并用/作为除号。如果你愿意,可以要求用户以厘米为单位输入身高,并以米为单位显示出来。
#include<stdio.h>
int main()
{
float long1;
char name[30];
printf("请输入身高(cm):");
scanf("%f",&long1);
fflush(stdin);
printf("请输入姓名:");
scanf("%s",name);
printf("%s, you are %.2f (m) feet tall!",name,long1/100);
return 0;
}
5.编写一个程序
提示用户输入以兆位每秒(Mb/s)为单位的下载速度和以兆字节(MB)为单位的文件大小。程序中应计算文件的下载时间。注意,这里1字节等于8位。使用float类型,并用/作为除号。该程序要以下面的格式打印 3 个变量的值(下载速度、文件大小和下载时间),显示小数点后面两位数字:
At 18.12 megabits per second, a file of 2.20 megabytes downloads in 0.97 seconds.
#include<stdio.h>
int main()
{
float dl_v,fl_num;
printf("请输入文件下载速度(mb/s):");
scanf("%f",&dl_v);
fflush(stdin);
printf("文件的大小(mb):");
scanf("%f",&fl_num);
printf("At %.2f megabits per second, a file of %.2f megabytes\n",dl_v,fl_num);
printf("downloads in %.2f seconds.\n",fl_num/dl_v);
return 0;
}
6.编写一个程序,先提示用户输入名,然后提示用户输入姓。在一行打印用户输入的名和姓,下一行分别打印名和姓的字母数。字母数要与相应名和姓的结尾对齐,如下所示:
Melissa Honeybee
7 8
接下来,再打印相同的信息,但是字母个数与相应名和姓的开头对齐,如下所示:
Melissa Honeybee
7 8
#include<stdio.h>
#include<string.h>
int main()
{
char surname[30];
char name[30];
int size_surname,size_name;
printf("please,enter surname:");
scanf("%s",surname);
size_surname=strlen(surname);
fflush(stdin);
printf("please,enter name:");
scanf("%s",name);
size_name=strlen(name);
printf("%s %s\n",surname,name);
// printf("surname=%d , name=%d\n",size_surname,size_name);
printf("%*d%*d\n",size_surname,size_surname,1+size_name,size_name);
printf("%s %s\n",surname,name);
printf("%d%*d\n",size_surname,1+size_surname,size_name);
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(void)
{
double x1=1.0/3.0;
float x2=1.0/3.0;
printf("显示六位:%.6f %.6f\n",x1,x2);
printf("显示12位:%.12f %.12f\n",x1,x2);
printf("显示16位:%.16f %.16f\n",x1,x2);
printf("%f %f\n", FLT_DIG, DBL_DIG);
return 0;
// FLT_DIG, DBL_DIG
// 这两个宏在float.h头文件下面,用来说明double、float两种数据类型有效数字的位数,
// 注意不是小数点后面的有效位数,而是所有位数。
}
8.编写一个程序
提示用户输入旅行的里程和消耗的汽油量。然后计算并显示消耗每加仑汽油行驶的英里数,显示小数点后面一位数字。接下来,使用1加仑大约3.785升,1英里大约为1.609千米,把单位是英里/加仑的值转换为升/100公里(欧洲通用的燃料消耗表示法),并显示结果,显示小数点后面 1 位数字。注意,美国采用的方案测量消耗单位燃料的行程(值越大越好),而欧洲则采用单位距离消耗的燃料测量方案(值越低越好)。使用#define 创建符号常量或使用 const 限定符创建变量来表示两个转换系数。
#include<stdio.h>
#define Jia_Lun 3.785
int main()
{
const float ying_li=1.609;
float li,you;
printf("用户输入旅行的里程(单位千米):");
scanf("%f",&li);
fflush(stdin);
printf("消耗的汽油量(单位升):");
scanf("%f",&you);
printf("每加仑汽油行驶的英里数位:%.1f。\n",(you*Jia_Lun)/(li*ying_li));
return 0;
}