字符串和格式化输入输出

4.1范例

#include <stdio.h>
#include <string.h>
#define DENSITY 62.4
//常量
int main() {
    float weight,volume;
    int size, letters;
    char 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);
//    size=char name[]括号中的赋值
    printf("name length=%d", size,"\n");
    letters = strlen(name);
//    letters长度等于name长度如yan=3,zhang=5,strlen()用于获取字符串长度
    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**转换说明来处理字符串的输入和输出,但是在scanf()中,name没有&前缀,而weight有,为啥?
  • C预处理器把字符常量DENSITY定义为62.4。
  • C函数strlen()获取字符串的长度

4.2 字符串简介

  • 字符串(character string)是一个或多个字符的序列。

4.2.1 char类型数组和null字符

  • C中没有专门用于存储字符串的变量类型,字符串都被存储在char类型的数组中数组由连续的存储单元组成,字符串中的字符被存储在相邻的存储单元中,每个单元存储一个字符
  • 空字符(null character)\0,C中用它标记字符串的结束。空字符不是数字0,是非打印字符,其ASCII码值是0。C中的字符串一定是以空字符结束,这意味着数组的容量必须至少比待存储字符串的字符数多1。
  • 数组是同类型数据元素的有序序列。
  • 字符串的创建:必须先创建一个数组,把字符串中的字符逐个放入数组,还要在末尾加上一个\0。这一切都是计算机自己完成的。

4.2.2 使用字符串

#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;
}
/* output
 * What's your name?Angela Plains
 * Hello, Angela. You are an extraordinary being
 */
  • 根据%s转换说明,scanf()只会读取字符串中的一个单词,而不是一整句话。(以空格,制表符或换行符为止)。

字符串和字符

  • 字符串常量“x”和字符常量‘x’不同。区别之一在于’x’是基本类型(char),而“x”是派生类型(char数组);区别之二是“x”实际上是由两个字符组成:‘x’和空字符\0。

4.2.3 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);
    printf("The phrase of praise has %zd letters",
            strlen(PRAISE));
    printf("and occupies %zd memory cells.\n",sizeof PRAISE);
    return 0;
}
/* output
 * What's your name? yan mao
 * Hello, yan. You are an extraordinary being.
 * Your name of 3 letters occupies 40 memory cells.
 * The phrase of praise has 31 letters and occupies 32 memory cells.
 */
  • C99和C11标准专门为sizeof运算符的返回类型添加%zd转换说明。
  • sizeof使用括号问题:何时使用圆括号取决于运算对象是类型还是特定量。运算对象是类型时,圆括号必不可少,但是对于特定量,圆括号可有可无。对于类型,应写成sizeof(char)或sizeof(float);对于特定量,可写成sizeof name或sizeof 6.28。

4.3 常量和C预处理器

  • 如何创建符号常量?

    1. 声明一个变量,然后将该变量设置为所需的常量,如:

      1. float taxrate;
        taxrate = 0.015;
        
    2. C预处理器

      1. #define NAME value
        
      2. 如:

        #define TAXRATE 0.015
        
        • 编译程序时,程序中的所有TAXRATE都会被替换成0.015。这一过程称为编译时替换compile-time substitution),这样定义的常量也称为明示常量manifest constant)。
        • 常量为大写。
  • define也可以定义字符和字符串常量,如:

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

4.3.1 const 限定符

  • C90标准新增了const关键字,用于限定一个变量为只读。

  • const int MONTHS = 12;
    
  • 计算中可以使用MONTHS,可以打印,但是不能更改MONTHS的值。

4.3.2 明示常量

  • C头文件limits.h和float.h分别提供了与整数类型和浮点类型大小限制相关的详细信息。如:

  • #define INT_MAX +32767
    #define INT_MIN -32768
    

limits.h明示常量

明示常量含义
CHAR_BITchar类型的位数
CHAR_MAXchar类型的最大值
CHAR_MINchar类型的最小值
SCHAR_MAXsigned char类型的最大值
SCHAR_MINsigned char类型的最小值
UCHAR_MAXunsigned char类型的最大值
ULLONG_MAXunsigned long long类型的最大值
SHRT_MAXshort类型的最大值
SHRT_MINshort类型的最小值
USHRT_MAXunsigned short类型的最大值
INT_MAXint类型的最大值
INT_MINint类型的最小值
UINT_MANunsigned int的最大值
LONG_MAXlong类型的最大值
LONG_MINlong类型的最小值
ULONG_MANunsigned long类型的最大值
LLONG_MAXlong long类型的最大值
LLONG_MINlong long类型的最小值

float.h明示常量

明示常量含义
FLT_MANT_DIGfloat类型的尾数位数
FLT_DIGfloat类型的最少有效数字位数(十进制)
FLT_MIN_10_EXP带全部有效数字的float类型的最小负指数(以10为底)
FLT_MAX_10_EXPfloat类型的最大正指数(以10为底)
FLT_MIN保留全部精度的float类型最小正数
FLT_MAXfloat类型的最大正数
FLT_EPSILON1.00和比1.00大的最小float类型值之间的差值

4.4 printf()和scanf()

  • 转换说明(conversion specification),指定了如何把数据转换成可显示的形式。

  • ANSI C标准为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
    %%打印一个百分号

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

  • 在%和转换字符之间插入修饰符可修饰基本的转换说明。如果要插入多个字符,其顺序应该和下表中的顺序相同。不是所有的组合都可行。表中有些字符是C99新增的,如果编译器不支持C99,则可能不支持表中的所有项。
  • printf()的修饰符表
修饰符含义
标记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 long类型的值
示例:“%Lf”,“%10.4Le”
t和整型转换说明一起使用,表示ptrdiff_t类型的值。ptrdiff_t是两个指针差值的类型(C99)
示例:“%td”,“%12ti”
z和整型转换说明一起使用,表示size_t类型的值。size_t是sizeof返回的类型(C99)
示例:“%zd”,“%12zd”
  • printf()中的标记
标记含义
-待打印项左对其。即,从字段的左侧开始打印该项
示例:“%-20s”
+有符号值若为正,则在值前面显示加号;若为负,则在值前面显示减号
示例:"%+6.2f"
空格有符号值若为正,则在值前面显示前导空格(不显示任何符号);若为负,则在值前面显示减号
示例:“% 6.2f”
#把结果转换为另一种形式。如果是%o类型,则以0开始;如果是%x或%X格式,则以0x或0X开始;
对于所有的浮点格式,#保证了即使后面没有任何数字,也打印一个小数点字符。对于%g和%G格式,#防止结果后面的0被删除
示例:“%#o”,“%#8.0f”,“%+#10.3e”
0对于数值格式,用前导0代替空格填充字段宽度。对于整数格式,如果出现-标记或指定精度,则忽略该标记
示例:“%010d”和"%08.3f"
  • 注意:表达式或参数中的float类型值会被自动转换成double类型。

4.4.4 转换说明的意义

  • 转换说明把以二进制格式存储在计算机中的值转换成一系列字符(字符串)以便于显示
  • 转换可能会误导读者认为原始值被替换成转换后的值。实际上,转换说明是翻译说明,%d的意思是“把给定的值翻译成十进制整数文本并打印出来”。

1. 转换不匹配

  • 转换说明应该与待打印值的类型相匹配。通常有多种选择。如,打印一个int类型的值,可以使用%d,%x或%o。打印double类型的值,可使用%f,%e或%g。
//转换不匹配类型

#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;
}
/*
 * Output
 * num as short and unsigned short: 336 336
 *-num as short and unsigned short: -336 65200
 * num as int and char: 336 P
 * WORDS as int,short, and char: 65618 82 R
 */
  • 第一行,num变量对应的转换说明%hd和%hu输出的结果都是336。
  • 第二行,mnum变量对应的转换说明%u(无符号)输出的结果为65200,并非336。这是因为有符号short int类型的值在我们的参考系统中的表示方式所致。首先,short int的大小是2字节;其次,系统使用二进制补码来表示有符号整数。这种方法,数字0-32767代表它们本身,而数字32768-65535则表示负数。其中,65535表示-1,65534表示-2,一次类推。因此,-336表示为65200(即65536-336)。所以被解释成有符号int时,65200代表-336;而被解释成无符号int时,65200则代表65200。
  • 第三行,在我们的系统中,short int是2字节,char是1字节。当printf()使用%c打印336时,它只会查看存储336的2字节中的后1字节。这种截断相当于用一个整数除以256,只保留其余数。在这种情况下,余数是80,对应的ASCII值是字符P。用专业术语来说,该数字被解释成“以256为模”(modulo 256),即该数字除以256后取其余数。
  • 第四行,打印比short int类型最大整数(32767)更大的整数(65618).计算机也进行了求模运算。在系统中,应把数字65618存储为4字节的int类型。用%hd转换说明打印时,printf()只使用最后2个字节。相当于65618除以65536的余数。这里,余数是82。

2,printf()的返回值

  • printf()函数的返回值,它返回打印字符的个数。如果有输出错误,printf()则返回一个负值(printf()的旧版本会返回不同的值)。
  • 计算针对所有字符数,包括空格和不可见的换行符。
//
// Created by ym on 2021/9/26.
//printf()返回值
#include <stdio.h>

int main(void) {
    int re, rv;
    re = printf("12345678");
    printf("\n");
    rv = printf("12345678\n");
    printf("re长度为:%d,rv的长度为:%d", re, rv);

}
/*output
 * 12345678
 * 12345678
 * re长度为:8,rv的长度为:9
 */

3,打印较长的字符串

  • 字符串断行的3种方式
    1. 使用多个printf()语句。
    2. 用反斜杠(\)和Enter键组合使用来断行。
    3. ANSI C引入的字符串连接。在两个用引号括起来的字符串之间用空白隔开,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");
    return 0;
}
/*output
 * Here's one way to print along string.
 * Here's another way to print a long string.
 * Here's the newest way to print along string.
 */

4.4.5 scanf()

  • C库包含了多个输入函数,scanf()是最通用的一个,因为它可以读取不同格式的数据。
  • scanf()把输入的字符串转换成整数,浮点数,字符和字符串。
  • scanf()中的格式字符串表明字符输入流的目标数据类型。
  • printf()函数使用变量,常量和表达式,而scanf()函数使用指向变量的指针。
  • 如果用scanf()读取基本变量类型的值,在变量名前加上一个&。
  • 如果用scanf()把字符串读入字符数组中,不要使用&。
//何时使用&

#include <stdio.h>
int main(void) {
    int age;                //变量
    float assets;           //变量
    char pet[30];           //字符数组,用于存储字符串
    
    printf("Enter you age, assets, and favorite pet.\n");
    scanf("%d %f",&age,&assets);       //此处要使用&
    scanf("%s",pet);                   //字符数组不使用&
    printf("%d $%.2f %s\n",age,assets,pet);
    return 0;
}
/*output
 * Enter you age, assets, and favorite pet.
 * 18
 * 123.45
 * abcdefg
 * 18 $123.45 abcdefg
 */
  • ANSI C中scanf()的转换说明
转换说明含义
%c把输入解释成字符
%d把输入解释成有符号十进制整数
%e,%f,%g,%a把输入解释成浮点数(C99标准新增了%a)
%E,%F,%G,%A把输入解释成浮点数(C99标准新增了%A)
%i把输入解释成有符号十进制整数
%o把输入解释成有符号八进制整数
%p把输入解释成指针(地址)
%s把输入解释成字符串。从第1个非空白字符开始,到下一个空白字符之前的所有字符都是输入
%u把输入解释成无符号十进制整数
%x,%X把输入解释成有符号十六进制整数
  • 转换说明中(百分号和转换字符之间)可以使用修饰符。如果要使用多个修饰符,必须按下表顺序书写。
  • scanf()转换说明中的修饰符
转换说明含义
*抑制赋值
示例:"%*d"
数字最大字段宽度。输入达到最大字段宽度处,或第一次遇到空白字符时停止
示例:“%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 short 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)
示例:“%jd”,“%ju”
z在整型转换说明后面时,表明使用sizeof的返回类型(C99)
示例:“%zd”,“%zo”
t在整型转换说明后面时,表明使用表示两个指针差值的类型(C99)
示例:“%td”,“%tx”

1, 格式字符串中的普通字符

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

    • scanf("%d,%d",&n,&m);
      
    • scanf()将其解释成:用户输入一个数字,一个逗号,然后再输入一个数字。

  • 除了%c,其他转换说明都会自动跳过待输入值前面所有的空白。对于%c,在格式字符串中添加一个空格字符会有所不同。如,如果在格式字符串中把空格放到%c的前面,scanf()便会跳过空格,从第一个非空白字符开始读取。也就是说,scanf("%c", &ch)从输入中的第1个字符开始读取,而scanf(" %c", &ch)则从第1个非空白字符开始读取。

2,scanf()的返回值

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

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

  • printf()和scanf()都可以使用*修饰符来修改转换说明的含义。

  • printf()中如果你不想预先指定字段宽度,希望通过程序来指定,可以用*修饰符代替字段宽度。但还是要用一个参数告诉函数,字段宽度应该是多少。也可用于浮点值指定精度和字段宽度。

  • //printf()中*的使用
    
    #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;
    }
    /*output
     * Enter a field width:
     * 6
     * The number is :   256:
     * Now enter a width and a precision:
     * 8
     * 3
     * Weight =  242.500
     * Done!
     */
    /***
     * 用户输入6,因此6是程序使用的字段宽度。
     * 接下来用户输入8和3,说明字段宽度是8,
     * 小数点后面显示3位数字
    */
    
  • scanf()中的 * 用法与此不同。把 * 放在%和转换字符之间时,会使得scanf()跳过相应的输入项。

  • //scanf()中*的使用
    
    #include <stdio.h>
    int main(void) {
        int n,m,q;
        printf("Please enter three integers:\n");
        scanf("%*d %*d %d",&n);
        printf("The last integer was %d\n",n);
        return 0;
    }
    /***
     * output
     * Please enter three integers:
     * 100 200 300
     * The last integer was 300
    */
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值