C Primer Plus 第6版 中文版(02)

第4章 字符串和格式化输入/输出

printf : print format, 格式化打印的意思。

#include <stdio.h>
#include <string.h> // 提供strlen()函数的原型
# define DENSITY 1000 //人体密度 1000kg/立方米

int main(void) {
    char name[50]; // 可以容纳50个字符的数组
    printf("你的名字?\n");
    scanf("%s", name); // 默认使用 ASCII编码

    float weight, volume;
    printf("%s,你的体重是多少公斤?\n", name);
    scanf("%f", &weight);
    volume = weight / DENSITY;

    int size, letters;
    size = sizeof(name);
    letters = strlen(name);

    printf("%s,你的体积是%.2f 立方米\n", name, volume);
    printf("你的名字在计算存储中对应%d个字符存储空间,我们至少需要用%d个字节存储你的名字", letters, size);

    return 0;
}

/*
你的名字?
Zhang San
Zhang,你的体重是多少公斤?
Zhang,你的体积是0.00 立方米
你的名字在计算存储中对应5个字符存储空间,我们至少需要用50个字节存储你的名字
*/

存在的问题:

  • clion中,命令行运行测试默认不能输入中文,解决方式:run,Modify Run Configuration,选中 Emulate terminal in the output console
  • scanf默认遇到空格会结束输入,解决方式:scanf("%[^\n]",....
    • scanf函数中,[用来扫描特定的字符集。如果它以^开头,表示扫描除了字符集以外的所有字符。
#include <stdio.h>
#include <string.h> // 提供strlen()函数的原型
# define DENSITY 1000 //人体密度 1000kg/立方米

int main(void) {
    char name[50]; // 可以容纳50个字符的数组
    printf("你的名字?\n");
    scanf("%[^\n]", name); // 接受非 \n 输入

    float weight, volume;
    printf("%s,你的体重是多少公斤?\n", name);
    scanf("%f", &weight);
    volume = weight / DENSITY;

    int size, letters;
    size = sizeof(name);
    letters = strlen(name);

    printf("%s,你的体积是%.4f 立方米\n", name, volume);
    printf("你的名字在计算存储中对应%d个字符存储空间,我们用%d个字节存储你的名字", letters, size);

    return 0;
}

/*
你的名字?
Zhang San
Zhang San,你的体重是多少公斤?
77.777
Zhang San,你的体积是0.0778 立方米
你的名字在计算存储中对应9个字符存储空间,我们用50个字节存储你的名字
*/

程序说明:

  • 用数组(array)储存字符串(characterstring)。在该程序中,用户输入的名被储存在数组中,该数组占用内存中50个连续的字节,每个字节储存一个字符值。
  • 使用%s 转换说明来处理字符串的输入和输出。注意,在 scanf()中,name 没有&前缀,而 weight有( &weight和name都是地址)。
  • 用C预处理器把字符常量DENSITY定义为1000。
  • 用C函数strlen()获取字符串的长度。

使用中文名字的结果:

#include <stdio.h>
#include <string.h> // 提供strlen()函数的原型
# define DENSITY 1000 //人体密度 1000kg/立方米

int main(void) {
    char name[50]; // 可以容纳50个字符的数组
    printf("你的名字?\n");
    scanf("%[^\n]", name); // 接受非 \n 输入

    float weight, volume;
    printf("%s,你的体重是多少公斤?\n", name);
    scanf("%f", &weight);
    volume = weight / DENSITY;

    int size, letters;
    size = sizeof(name);
    letters = strlen(name);

    printf("%s,你的体积是%.4f 立方米\n", name, volume);
    printf("你的名字在计算存储中对应%d个字符存储空间,我们用%d个字节存储你的名字", letters, size);

    return 0;
}

/*
你的名字?
张三
张三,你的体重是多少公斤?
75
张三,你的体积是0.0750 立方米
你的名字在计算存储中对应4个字符存储空间,我们用50个字节存储你的名字
*/

字符串

  • 字符串是一个或多个字符的序列,用双引号标识。

  • C语言将字符串存储在char数组中,用空字符 \0 标识字符串的结束,因此char数组的大小至少比要存储的字符数多1,预留一个给空字符

  • scanf会处理添加空字符到字符串结尾

常量和C预处理器

符号常量的定义,注其中并没有=符号,其通用格式如下:

#define NAME value

const 关键字: 用于限定变量只读, 比 #define 更灵活

limits.h 中的一些常量

明示常量 含义
CHAR_BIT char 类型的位数
CHAR_MAX char类型的最大值
CHAR_MIN char类型的最小值
SCHAR_MAX signed char类型的最大值
SCHAR_MIN signed char类型的最小值
UCHAR_MAX unsigned char类型的最大值
SHRT_MAX short类型的最大值
SHRT_MIN short类型的最小值
USHRT_MAX unsiqned short类型的最大值
INT_MAX int 类型的最大值
INT_MIN int类型的最小值
UINT_MAX unsigned int的最大值
LONG_MAX long 类型的最大值
LONG_MIN long 类型的最小值
ULONG_MAX unsigned long 类型的最大值
LLONG_MAX long long 类型的最大值
LLONG_MIN long long 类型的最小值
ULLONG_MAX unsigned long long类型的最大值
#include <stdio.h>
#include <limits.h>

int main(void) {
    printf("CHAR_BIT,char 类型的位数:%d\n",CHAR_BIT);
    printf("CHAR_MAX,char类型的最大值:%d\n",CHAR_MAX);
    printf("CHAR_MIN,char类型的最小值:%d\n",CHAR_MIN);
    printf("SCHAR_MAX,signed char类型的最大值:%d\n",SCHAR_MAX);
    printf("SCHAR_MIN,signed char类型的最小值:%d\n",SCHAR_MIN);
    printf("UCHAR_MAX,unsigned char类型的最大值:%d\n",UCHAR_MAX);
    printf("SHRT_MAX,short类型的最大值:%d\n",SHRT_MAX);
    printf("SHRT_MIN,short类型的最小值:%d\n",SHRT_MIN);
    printf("USHRT_MAX,unsiqned short类型的最大值:%d\n",USHRT_MAX);
    printf("INT_MAX,int 类型的最大值:%d\n",INT_MAX);
    printf("INT_MIN,int类型的最小值:%d\n",INT_MIN);
    printf("UINT_MAX,unsigned int的最大值:%llu\n",UINT_MAX);
    printf("LONG_MAX,long 类型的最大值:%d\n",LONG_MAX);
    printf("LONG_MIN,long 类型的最小值:%d\n",LONG_MIN);
    printf("ULONG_MAX,unsigned long 类型的最大值:%llu\n",ULONG_MAX);
    printf("LLONG_MAX,long long 类型的最大值:%lld\n",LLONG_MAX);
    printf("LLONG_MIN,long long 类型的最小值:%lld\n",LLONG_MIN);
    printf("ULLONG_MAX,unsigned long long类型的最大值:%llu\n",ULLONG_MAX);

    return 0;
}

/*
CHAR_BIT,char 类型的位数:8
CHAR_MAX,char类型的最大值:127
CHAR_MIN,char类型的最小值:-128
SCHAR_MAX,signed char类型的最大值:127
SCHAR_MIN,signed char类型的最小值:-128
UCHAR_MAX,unsigned char类型的最大值:255
SHRT_MAX,short类型的最大值:32767
SHRT_MIN,short类型的最小值:-32768
USHRT_MAX,unsiqned short类型的最大值:65535
INT_MAX,int 类型的最大值:2147483647
INT_MIN,int类型的最小值:-2147483648
UINT_MAX,unsigned int的最大值:4294967295
LONG_MAX,long 类型的最大值:2147483647
LONG_MIN,long 类型的最小值:-2147483648
ULONG_MAX,unsigned long 类型的最大值:4294967295
LLONG_MAX,long long 类型的最大值:9223372036854775807
LLONG_MIN,long long 类型的最小值:-9223372036854775808
ULLONG_MAX,unsigned long long类型的最大值:18446744073709551615
*/

float.h 中的一些常量:

明示常量 含义
FLT_MANT_DIG float 类型的尾数位数
FLT_DIG float类型的最少有效数字位数(十进制)
FLT_MIN_10_EXP 带全部有效数字的float类型的最小负指数(以10为底)
FLT_MAX_10_EXP float类型的最大正指数(以10为底)
FLT_MIN 保留全部精度的 float类型最小正数
FLT_MAX float 类型的最大正数
FLT_EPSILON 1.00和比1.00大的最小float 类型值之间的差值
#include <stdio.h>
#include <float.h>

int main(void) {
    printf("FLT_MANT_DIG,float 类型的尾数位数:%d\n", FLT_MANT_DIG);
    printf("FLT_DIG,float类型的最少有效数字位数(十进制):%d\n", FLT_DIG);
    printf("FLT_MIN_10_EXP,带全部有效数字的float类型的最小负指数(以10为底):%e\n", FLT_MIN_10_EXP);
    printf("FLT_MAX_10_EXP,float类型的最大正指数(以10为底):%e\n", FLT_MAX_10_EXP);
    printf("FLT_MIN,保留全部精度的 float类型最小正数:%e\n", FLT_MIN);
    printf("FLT_MAX,float 类型的最大正数:%e\n", FLT_MAX);
    printf("FLT_EPSILON,1.00和比1.00大的最小float 类型值之间的差值:%e\n", FLT_EPSILON);


    return 0;
}

/*
FLT_MANT_DIG,float 类型的尾数位数:24
FLT_DIG,float类型的最少有效数字位数(十进制):6
FLT_MIN_10_EXP,带全部有效数字的float类型的最小负指数(以10为底):2.121996e-314
FLT_MAX_10_EXP,float类型的最大正指数(以10为底):1.877449e-322
FLT_MIN,保留全部精度的 float类型最小正数:1.175494e-38
FLT_MAX,float 类型的最大正数:3.402823e+38
FLT_EPSILON,1.00和比1.00大的最小float 类型值之间的差值:1.192093e-07
*/

printf()

虽然 printf()是输出函数,scanf()是输入函数,但是它们的工作原理几乎相同。两个函数都使用格式字符串和参数列表。

printf()函数的格式:

printf(格式字符串,待打印项1,待打印项2....);

printf函数提供了数据转换说明,指定如何把相关的数据转换成可显示的形式。printf函数转换说明及其打印的输出结果见下表:

转换说明 输出
%a 浮点数、十六进制数和p记数法(C99/C11)
%A 浮点数、十六进制数和p记数法(C99/C11)
%C 单个字符
%d 有符号十进制整数
%e 浮点数,e记数法
%E 浮点数,e记数法
%f 浮点数,十进制记数法
%g 根据值的不同,自动选择%f或%e。%e格式用于指数小于-4或者大于或等于精度时
%G 根据值的不同,自动选择%f或%E。%E格式用于指数小于-4或者大于或等于精度时
%i 有符号十进制整数(与%d相同)
%o 无符号八进制整数
%p 指针
%S 字符串
%u 无符号十进制整数
%x 无符号十六进制整数,使用十六进制数0f
%X 无符号十六进制整数,使用十六进制数0F
%% 打印一个百分号

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

  • 表1和表2列出可作为修饰符的合法字符如果要插入多个字符,其书写顺序应该与表1中列出的顺序相同

表1 printf()的修饰符

修饰符 含义
标记 表 2 描述了5种标记(+、-、空格、#和0),可以不使用标记或使用多个标记示例:“%-10d"
数字 最小字段宽度:如果该字段不能容纳待打印的数字或字符串,系统会使用更宽的字段示例:"%4d"
.数字 对于%e、%E和%f转换,表示小数点右边数字的位数
对于%g和%G转换,表示有效数字最大位数
对于%s转换,表示待打印字符的最大数量
对于整型转换,表示待打印数字的最小位数
如有必要,使用前导0来达到这个位数
只使用.表示其后跟随一个0,所以%.f和%.0f 相同
示例:"%5.2f"打印一个浮点数,字段宽度为5字符,其中小数点后有两位数字
h 和整型转换说明一起使用,表示short int或unsigned short int 类型的值
示例:"%hu"、"%hx"、”%6.4hd"
hh 和整型转换说明一起使用,表示signed char或unsigned char类型的值
示例:"%hhu"、"%hhx"、"%6.4hhd"
j 和整型转换说明一起使用,表示intmax_t或uintmax_t类型的值。这些类型定义在stdint.h中
示例:"%jd"、"%8jx"
l 和整型转换说明一起使用,表示long int或unsigned long int 类型的值
示例:"%ld"、"%8lu"
ll 和整型转换说明一起使用,表示long long int或unsigned long long int 类型的值(C99)
示例:"%lld”、“%8llu"
L 和浮点转换说明一起使用,表示long double 类型的值
示例:"%Ld”、“%10.4Le"
t 和整型转换说明一起使用,表示ptrdiff_t类型的值。ptrdiff_t是两个指针差值的类型(C99)
示例:"%td”、"%12ti"
z 和整型转换说明一起使用,表示size_t类型的值。size_t是sizeof返回的类型(C99)
示例:"%zd""%12zd

表2 printf()中的标记

标记 含义
待打印项左对齐。即,从字段的左侧开始打印该项
示例:"%-20s"
+ 有符号值若为正,则在值前面显示加号;若为负,则在值前面显示减号
示例:"%+6.2f"
空格 有符号值若为正,则在值前面显示前导空格(不显示任何符号);若为负,则在值前面显示减号
+标记覆盖一个空格
示例:"%6.2f"
h 和整型转换说明一起使用,表示short int或unsigned short int 类型的值
示例:"%hu"、"%hx"、”%6.4hd"
# 把结果转换为另一种形式。如果是%o格式,则以0开始:如果是%x或%X格式,则以0x或 0X开始:对于所有的浮点格式,#保证了即使后面没有任何数字,也打印一个小数点字符。对于%g和%G格式,#防止结果后面的0被删除
示例:"%#o”、"%#8.0f”、"%+#10.3e"
0 对于数值格式,用前导0代替空格填充字段宽度。对于整数格式,如果出现-标记或指定精度,则忽略该标记

sizeof 与 类型可移植性

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

float 参数的转换

对于浮点类型,有用于 double 和 long double 类型的转换说明,却没有 float 类型的。这是因为在K&RC中,表达式或参数中的 float 类型值会被自动转换成 double 类型。一般而言,ANSIC不会把 float 自动转换成 double。然而,为保护大量假设 float类型的参数被自动转换成 double的现有程序,printf()函数中所有float 类型的参数(对未使用显式原型的所有C函数都有效)仍自动转换成 double 类型。因此,无论是K&RC还是ANSIC,都没有显示 float 类型值专用的转换说明。

打印宽度示例:

#include <stdio.h>
#define PAGES   959  // 长度为3

int main(void) {
    printf("*%d*\n",PAGES);
    printf("*%2d*\n",PAGES); // 实际长度超过2
    printf("*%10d*\n",PAGES);// 实际长度不到10, 默认右对齐
    printf("*%-10d*\n",PAGES);// 左对齐

    return 0;
}

/*
*959*
*959*
*       959*
*959       *
*/

浮点数打印示例:

#include <stdio.h>

int main(void) {
const double PI = 3.14159265358979323846;
    printf("*%f*\n", PI);  //默认最多打印6位小数
    printf("%e*\n", PI);
    printf("*%4.2f*\n", PI);
    printf("*%3.1f*\n", PI);
/*
*3.141593*
3.141593e+00*
*3.14*
*3.1*
*/
    printf("********************************\n");
    printf("*%10.3f*\n", PI); //宽度10
    printf("*%-10.3f*\n", PI); //宽度10,左对齐
    printf("*%-10.3e*\n", PI);
    printf("*%-10.3E*\n", PI);
/*
*     3.142*
*3.142     *
*3.142e+00 *
*3.142E+00 *
*/
    printf("********************************\n");
    printf("*%+4.3f*\n", PI); //正负号
    printf("*%+4.3f*\n", -PI); //正负号
/*
*+3.142*
*-3.142*
*/
    printf("********************************\n");
    printf("*%010.3f*\n", PI);
    printf("*%010.3e*\n", PI);
/*
*000003.142*
*03.142e+00*
*/
    return 0;
}

另一个示例:

#include <stdio.h>

int main(void) {
    printf("%x %X %#X\n",31,31,31); //大小写,前缀
    printf("% d\n",4); //前导空格演示,正负号对齐
    printf("% d\n",-4); //前导空格演示,正负号对齐
    printf("*%5d*%5.3d*%05d*%05.3d*\n",6,6,6,6); //
    return 0;
}
/*
1f 1F 0X1F
 4
-4
*    6*  006*00006*  006*
*/

字符串示例:

#include <stdio.h>
#define HELLO "Hello World!"

int main(void) {
    printf("%2s\n", HELLO); // 会突破2的长度限制
    printf("%20s\n", HELLO); // 默认左对齐
    printf("%20.2s\n", HELLO); // 不会突破2的长度限制
    printf("%-20.2s\n", HELLO);//左对齐
    return 0;
}
/*
Hello World!
        Hello World!
                  He
He
*/

printf中的转换说明只是把原始的二进制值转换成指定的形式显示出来,不会改变原来的值。

#include <stdio.h>
#define PAGES 336
#define WORDS 65618

int main(void) {
    short num =PAGES;
    short num2=65;
    printf("print num as short and unsigned short: %hd %hu \n",num,num);
    printf("print -num as short and unsigned short: %hd %hu \n",-num,-num); // 试图用hu无符号格式打印负数
    printf("print num as int and char: %d %c \n",num,num); // num 已经超过char的取值范围
    printf("print num2 as int and char: %d %c \n",num2,num2); // OK
    printf("print WORDS 65618 as int short,unsigned short , and char: %d %hd %hu %c \n",WORDS,WORDS,WORDS,WORDS);
    return 0;
}

/*
print num as short and unsigned short: 336 336
print -num as short and unsigned short: -336 65200
print num as int and char: 336 P
print num2 as int and char: 65 A
print WORDS 65618 as int short and char: 65618 82 82 R
*/

printf 会依次对每个变量进行转换:

#include <stdio.h>

int main(void) {
    float n1 = 3;
    double n2 = 3;

    long n3 = 2000000000;
    long n4 = 1234567890;

    printf("%.1e %.1e %.1e %.1e\n", n1, n2, n3, n4); // e只处理浮点数
    // 3.0e+00 3.0e+00 9.9e-315 2.8e-312
    printf("%ld  %ld \n", n3, n4);
    // 2000000000  1234567890
    printf("%ld  %ld %ld %ld\n", n1, n2, n3, n4); 
    // 0  0 2000000000 1234567890
    return 0;
}

scanf()

  • scanf()把输入的字符串转换成整数、浮点数、字符或字符串

  • printf()正好与printf相反,把整数、浮点数、字符和字符串转换成显示在屏幕上的文本。

  • scanf()和printf()类似,也使用格式字符串和参数列表。scanf()中的格式字符串表明字符输入流的目标数据类型。

  • 两个函数主要的区别在参数列表中。printf()函数使用变量、常量和表达式,而scanf(函数使用指向变量的指针。

    • 如果用scanf()读取基本变量类型的值,在变量名前加上一个&;

    • 如果用scanf()把字符串读入字符数组中,不要使用&。

#include <stdio.h>

int main(void) {
    int age;
    char name[40];
    printf("请输入你的年龄和名字: \n");
    scanf("%d",&age);
    scanf("%s",name);
    printf("%s,你的年龄为 %d\n", name,age);
    return 0;
}
/*
请输入你的年龄和名字:
18
Zhang San
Zhang,你的年龄为 18
*/

如果两个变量输入放一条sanf语句中,用空格隔开

#include <stdio.h>

int main(void) {
    int age;
    char name[40];
    printf("请输入你的年龄和名字: \n");
    scanf("%d %[^\n]",&age,name);
    // scanf("%s",name);
    printf("%s,你的年龄为 %d\n", name,age);
    return 0;
}
/*
请输入你的年龄和名字:
18 Zhang San
Zhang San,你的年龄为 18
*/

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

转换说明 含义
%c 把输入解释成字符
%d 把输入解释成有符号十进制整数
%e、%f、%g、%a 把输入解释成浮点数(C99标准新增了%a)
%E、%F、%G、%A 把输入解释成浮点数(C99标准新增了%A)
%i 把输入解释成有符号十进制整数
%o 把输入解释成有符号八进制整数
%p 把输入解释成指针(地址)
%s 把输入解释成字符串。从第1个非空白字符开始,到下一个空白字符之前的所有字符都是输入
%u 把输入解释成无符号十进制整数
%x、%X 把输入解释成有符号十六进制整数

可以在上表所列的转换说明中(百分号和转换字符之间)使用修饰符。如果要使用多个修饰符,必须按下表所列的顺序书写。

转换说明 含义
* 抑制赋值
示例:""%*d""
数字 最大字段宽度。输入达到最大字段宽度处,或第1次遇到空白字符时停止
示例:""%10s""
hh 把整数作为signed char 或unsigned char 类型读取
示例:"%hhd"、"%hhu"
ll 把整数作为long long或unsigned long long类型读取(C99)
示例:"%lld"、"%llu"
h、l或L "%hd"和"%hi"表明把对应的值储存为short int类型
"%ho"、"%hx"和"%hu"表明把对应的值储存为unsigned shot int类型
"%ld"和"%li"表明把对应的值储存为long类型
"%lo"、"%lx"和"%lu"表明把对应的值储存为unsigned long类型
"%le"、"%lf"和"%lg"表明把对应的值储存为double类型
在e、f和g前面使用L而不是L,表明把对应的值被储存为long double类型。
如果没有修饰符,d、i、o和x表明对应的值被储存为int类型,f和g表明把对应的值储存为float类型
j 在整型转换说明后面时,表明使用intmax_t或uintmax_t类型(C99)
示例:"%zd"、"%zo"
z 在整型转换说明后面时,表明使用sizeof的返回类型(C99)
t 在整型转换说明后面时,表明使用表示两个指针差值的类型(C99)
示例:"%td"、"%tx"

如上表所示,使用转换说明比较复杂,而且这些表中还省略了一些特性。省略的主要特性是,从高度格式化源中读取选定数据,如穿孔卡或其他数据记录。因为在本书中,scanf()主要作为与程序交互的便利工具,所以这里没有讨论更复杂的特性。

从 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~E。浮点转换说明要求 scanf ()识别小数点、e记数法(指数记数法)和新增的p记数法(六进制指数记数法)。

  • 如果使用%s转换说明,scanf()会读取除空白以外的所有字符。scanf()跳过空白开始读取第1个非空白字符,并保存非空白字符直到再次遇到空白。这意味着scanf()根据%s 转换说明读取一个单词,即不包含空白字符的字符串。如果使用字段宽度,scanf()在字段末尾或第1个空白字符处停止读取。无法利用字段宽度让只有一个%s的 scanf()读取多个单词。

  • 当scanf()把字符串放进指定数组中时,它会在字符序列的末尾加上’\0’,让数组中的内容成为一个C字符串。

  • 在C语言中scanf(并不是最常用的输入函数。这里重点介绍它是因为它能读取不同类型的数据。C语言还有其他的输入函数,如getchar()和fgets()。这两个函数更适合处理一些特殊情况,如读取单个字符或包含空格的字符串。

格式字符串中的普通字符

scanf()函数允许把普通字符放在格式字符串中。除空格字符外的普通字符必须与输入字符串严格匹配,例如,假设在两个转换说明中添加一个逗号:

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

scanf()函数将其解释成:用户将输入一个数字、一个逗号,然后再输入一个数字。也就是说,用户必须像下面这样进行输入两个整数:
88, 121
由于格式字符串中,%d后面紧跟逗号,所以必须在输入88后再输入一个逗号。但是,由于scanf()会跳过整数前面的空白,所以下面两种输入方式都可以:
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个非空白字符开始读取,输入中第一个并非必须是空字符

第一种情况:

#include <stdio.h>

int main(void) {
    char c_first;
    scanf(" %c", &c_first);
    printf("%c\n", c_first);
    return 0;
}
/*
a bcd
a
*/

第二种情况:

#include <stdio.h>

int main(void) {
    char c_first;
    scanf(" %c", &c_first);
    printf("%c\n", c_first);
    return 0;
}
/*
abc
a
*/

scanf()的返回值

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

#include <stdio.h>

int main(void) {
    int i,j,k,r;
    printf("输入三个整数:i,j,k\n ");
    r = scanf("%d,%d,%d",&i,&j,&k);
    printf("输入:%d %d %d\n",i,j,k);
    printf("scanf的返回值:%d\n",r);
    return 0;
}
/*
输入三个整数:i,j,k
2,3,4
输入:2 3 4
scanf的返回值:3
*/

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

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

  • printf()中如果不想预先指定字段宽度,希望通过程序来指定,那么可以用*修饰符代替字段宽度。但还是要用个参数告诉函数,字段宽度应该是多少。也就是说,如果转换说明是*d,那么参数列表中应包含*d对应的值。这个技巧也可用于浮点值指定精度和字段宽度。
  • scanf()中*的用法与此不同。把*放在和转换字符之间时,会使得scanf()跳过相应的输出项。在程序需要读取文件中特定列的内容时,这项跳过功能很有用。
#include <stdio.h>

int main(void) {
    int width = 10, precision = 3;
    int number = 1234;
    double pi = 3.141592653589793;

    printf("%%d:%d \n", number);
    printf("%%*d:%*d \n", width, number);
    printf("%%f:%f \n", pi);
    printf("%%*.*f:%*.*f\n", width, precision, pi);

    int i = 0, j = 0, k = 0;
    printf("请输入3个整数 i j k\n");
    scanf("%*d %*d %d",&k);
    printf("i=%d, j=%d, k=%d\n", i, j, k);

    return 0;
}

/*
%d:1234
%*d:      1234
%f:3.141593
%*.*f:     3.142
请输入3个整数 i j k
10 11 12
i=0, j=0, k=12
*/

C语言用char 类型表示单个字符,用字符串表示字符序列。字符常量是一种字符串形式,即用双引号把字符括起来:"Good luck,my friend"。可以把字符串储存在字符数组(由内存中相邻的字节组成)中。字符串,无论是表示成字符常量还是储存在字符数组中,都以一个叫做空字符\0的隐藏字符结尾。

正文完
 0
评论(没有评论)