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

发布于 2021-04-04  316 次阅读


章节要点

  • 本章重点介绍输入和输出。
  • 一个重要工具——C预处理器指令
  • 如何定义、使用符号常量。

    4.1 前导程序

//  talkback.c  --演示与用户交互
#include<stdio.h>
#include<string.h>
#define DENSITY 62.4
int main(void)
{
    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;
    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;
}

file
新特性:

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

    4.2 字符串简介

    字符串(character string)是一个或多个字符的序列。如下:
    "zing went the strings of my heart!"
    双引号告诉编译器它括起来的是字符串,如单引号一样。

    4.2.1 char类型数组和null字符

    file
    末尾的\0.这是空字符(null character),C语言用它标记字符串的结束。它不是数字0,它是非打印字符,其ASCII码值是(或等价于)0.C的字符串一定要以空字符结束,意味着数组容量必须至少比存储字符串中的字符多1.name[40]有40个存储单元,只能存储39个字符。
    数组可以看作为一行连续的多个存储单元,数组是同类型数据元素的有序序列。
    file

    使用字符串

    //  praise1.c   --使用不同类型的字符串
    #include<stdio.h>
    #define PRAISE "You are an extraordingnary being."
    int main(void)
    {
    char name[40];
    
    printf("What's your name?\n");
    scanf("%s",name);
    printf("Hello,%s.%s\n",name,PRAISE);
    
    return 0;
    }

    file
    (重要!)scanf()只读取了郭 安中的郭。它在遇到第1个空白(空格、制表符或者换行符)就不再读取了。
    根据%s转换说明,scanf()只会读取字符串中的一个单词。而不是仪征句话
    C语言中fgets()用于读取一般字符串。
    file

    4.2.3 strlen()函数

    //  praise1.c   --使用不同类型的字符串
    #include<stdio.h>
    #include<string.h>
    #define PRAISE "You are an extraordingnary being."
    int main(void)
    {
    char name[40];
    
    printf("What's your name?\n");
    scanf("%s",name);
    printf("Hello,%s.%s\n",name,PRAISE);
    printf("Yoour name of %d letters occupies %d memory cells.\n",strlen(name),sizeof name);
    printf("The phrase of praise has %d letters",strlen(PRAISE));
    print("and occupies %d memory cells.\n",sizeof PRAISE);
    return 0;
    }

    file
    ANSI C之前的编译器移除

    #include<string.h>

    string.h头文件包含多个与字符串相关的函数原型,包括strlen().
    一般而言,C把函数库中相关的函数归为一类,并为每类函数提供一个头文件。
    file
    对于PRAISE,用strlen()得出的也是字符串中的字符数(包括空格标点符号)。
    sizeof运算符给的数更大,因为它把末尾看不见的空字符也计算在内了,它一般计算双引号内的字符数。
    C99和C11标准专门为sizeof运算符的返回类型添加了%zd对于strlen同样适用。
    对于sizeof,运算对象是类型时,圆括号必不可少,特定量,可有可无。推荐所有情况都写圆括号。
    对于要处理字符串的程序。strlen()很有用。

    4.3 常量和C预处理器

    有时候,在程序中需要用到常量

    1. 常量名字比常量能代表的信息更多。
    2. 程序多出使用这个常量为了修改方便。
      方法一:声明一个变量,然后为变量赋值。

      float taxrate;
      taxrate = 0.015;

      这样做提供了一个符号名,但是taxrate是一个变量,程序可能会无意间改变它的值。
      因此------C预处理器。

      #define TAXRATE 0.015

      此时,编译程序时,所有的TAXRATE都会被替换成0.015.这一个过程被称为_编译时替换(compile-time substitution)_在运行程序时候,程序中所有的替换均已完成。这样定义的常量也称为明示常量(manifest constant)。
      (名称与数值之间没有等号哟~)

#define NAME value

(不加分号!一般常量名字大写)
file

//  pizza.c --在比萨饼程序中使用已定义的常量
#include<stdio.h>
#define PI 3.14159
int main()
{
    float area,circum,radius;

    printf(" What is the radius of your pizza?\n");
    scanf("%f",&radius);
    area = PI*radius*radius;
    cirum = PI*2*radius;
    printf("Your basic pizza parameters are as follows:\n");
    printf("circumference = %1.2f,area = %1.2f\n",circum,area);

    return 0;
}

file
%1.2f表明,结果被四舍五入为两位小数输出。

//#define指令还可定义字符和字符串常量。前者使用双引号,后者使用双引号。
#define BEEP '\a'
#define TEE 'T'
#define ESC '\033'
#define OOPS "Now you have done it!"
//错误格式
#define TOES = 20
//如果这样子,替换TOES的是= 20,而不是20

4.3.1 const限定符

C90标准新增(只读)

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

4.3.2 明示常量

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

//  defines.c       使用limit.h和float头文件中定义的明示常量
#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("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",ELT_MIN);
    printf("float precision = %d digits \n",ELT_DIG);
    printf("float epsilon = %e\n",ELT_EPSILOW);

    return 0;
}

4.4 printf()和scanf()

_I/O_函数

4.4.1 printf()函数

%d/%c:整数/字符;这些符号称为转换说明(conbersion specification),它们指定了如何把数据转换成可现实的形式。
file

4.4.2 使用printf()

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

file

file
注意:格式字符串中的转换说明一定要与后面的每个项相匹配。

//  %号的打印
printf("It's 90%% of my love for you");

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

file
注意:类型可移植性。stddef.h头文件(在包含stdio.h头文件时已包含其中)把size_t定义成系统使用sizeof返回的类型值,这被称为底层类型(underlying type)printf()使用z修饰符表表示打印相应的类型。同样C还定义了ptrdiff_t类型和t修饰符来表示系统使用两个地址差值的底层有符号整数类型。

  • float自动转换成double类型
    file

    1. 使用修饰符和标记
//  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;
}

file
第一个正常输出;第二个两个空格长度输出但是因为要3个数字,所以自动补齐到3,第四个10个字符,第5个10个但是结果位于最左侧。

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

file
如果修改const变量
file

//  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**%2.3d**%05d**%05.3d**\n",6,6,6,6);

    return 0;
}

file

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

    return 0;
}

file
.x告诉计算机只打印x个字符。

4.4.4 转换说明的意义

转换说明把以二进制格式存储在计算机中的值转换成一系列字符(字符串)以便于显示。
例如:76的存储格式为01001100.%d转换说明将其转换成字符7和6,并显示76;%x转换成16进制4c;%c转换说明把01001100转换成字符L。
转换不是替换。而是换成某种格式打印出来。

  1. 转换不匹配

    //  intconv.c   -- 一些不匹配的整型转换
    #include<stdio.h>
    #define PAGES 336
    #define WORDS 65618
    int main(void)
    {
    short num = PAGES;
    short munm = -PAGES;
    printf("num as short and unsiged short:%hd %hu\n",num,num);
    printf("-num as short and unsigned short:%hd %hu\n",munm,munm);
    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;
    }

    file
    别期望用%u转换说明能把数字和符号分开。
    犯傻了~~~
    file

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

    file
    file

    • 参数传递(留坑)
  2. printf()的返回值
    sqrt()函数,接受一个数作为参数,并返回该数的平方根。

    //  prntval.c   --printf()的返回值
    #include<stdio.h>
    int main(void)
    {
    int a = 212;
    int rv;
    rv = printf("%d is a pig\n",a);
    printf("%d\n",rv);
    
    return 0;
    }

    file
    注意计算针对所有的字符数,包括空格和不可见的换行符。

  3. 打印较长的字符串
    给字符串断行有三种方法

    //  longstrg.c  --打印较长的字符串
    #include<stdio.h>
    int main(void)
    {
    printf("My name is ");
    printf("Guo an\n");
    printf("My name is \
    xiaoan\n");
    printf("my name is "
    "Guoan2\n");
    
    return 0;
    }

    file

    4.4.5使用scanf()

    printf()函数使用变量、常量和表达式,而scanf()函数使用指向变量的指针。

    • 如果使用scanf()读取基本变量类型的值,在变量名前面加一个 &;
    • 如果scanf()把字符串读入字符数组中,不要使用&。
//  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;
}

file
输入空格输出不会键入空白字符。
唯一例外的是%c转换的说明。根据%c,scanf()会读取每个字符,包括空白。
对于float类型和double类型,printf()都使用%f,%e,%E,%g和%G转换说明。而scanf()只把它们用于float类型,对于double类型要使用l修饰符。
file
file


擦肩而过的概率