天天看点

第五 字符串和格式化输入/输出C语言学习栏目目录

C语言学习栏目目录

目录

1、前导程序

2、字符串简介

3 常量和C预处理器

这篇介绍下列内容:

函数:strlen()

关键字:const

字符串

如何创建、存储字符串 如何使用strlen()函数获取字符串的长度,用C预处理器指令#define和ANSIC的const修饰符创建符号常量。

    本篇重点介绍输入和输出。与程序交互和使用字符串可以编写个性化的 程序,本章将详细介绍C语言的两个输入/输出函数:printf()和scanf()。学会 使用这两个函数,不仅能与用户交互,还可根据个人喜好和任务要求格式化 输出。最后,简要介绍一个重要的工具——C预处理器指令,并学习如何定 义、使用符号常量。

1、前导程序

//  这次是用xcode编译,所以代码书写风格有点差异,但是运行结果是一样的
//  main.c
//  day05
//  function:演示与用户交互
//  Created by 陈文坚 on 2019/12/31.
//  Copyright © 2019 陈文坚. All rights reserved.
//a

#include <stdio.h>
#include <string.h>   // 提供strlen()函数的原型
#define PRICE 25.5    // 书本价格
int main(void)
{
    float sum;
    int num,size,letters;
    char name[40];    // name是一个可容纳40个字符的数组
    printf("你的名字是什么?\n");
    scanf("%s", name);          //在控制台输入你的名字,英文/拼音
    printf("%s, 你打算买几本书?\n", name);
    scanf("%d", &num);          //输入书本的数量
    size = sizeof name;
    letters = strlen(name);
    sum = num * PRICE;
    printf(" %s, 你购买书的总价是:%2.2f 元.\n", name, sum);
    printf("而且你名字的长度是: %d ,\n", letters);
    printf("并且它占用的空间是 %d(字节).\n", size);
    return 0;
}
           

运行程序,得到的结果如下:

你的名字是什么?

chen

chen, 你打算买几本书?

5

chen, 你购买书的总价是:127.50 元.

而且你名字的长度是: 4 ,

并且它占用的空间是 40(字节).

Program ended with exit code: 0
           

该程序包含以下新特性。

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

    对于BASIC的输入/输出而言,C的输入/输出看上去有些复杂。不过, 复杂换来的是程序的高效和方便控制输入/输出。而且,一旦熟悉用法后, 会发现它很简单。

2、字符串简介

     字符串(character string)是一个或多个字符的序列,如下所示:

"Zing went the strings of my heart!"
           

双引号不是字符串的一部分。双引号仅告知编译器它括起来的是字符串,正如单引号用于标识单个字符一样。

2.1 char类型数组和null字符

C语言没有专门用于储存字符串的变量类型,字符串都被储存在char类型的数组中。数组由连续的存储单元组成,字符串中的字符被储存在相邻的存储单元中,每个单元储存一个字符(如下图)

第五 字符串和格式化输入/输出C语言学习栏目目录

    上图中数组末尾位置的字符\0。这是空字符(null  character),C语言用它标记字符串的结束。空字符不是数字0,它是非打印字符,其ASCII码值是(或等价于)0。C中的字符串一定以空字符结束,这意味着数组的容量必须至少比待存储字符串中的字符数多1。因此,程序清单4.1中有40个存储单元的字符串,只能储存39个字符,剩下一个字节留给空字符。那么,什么是数组?可以把数组看作是一行连续的多个存储单元。用更正式的说法是,数组是同类型数据元素的有序序列。程序清单4.1通过以下声明创建了一个包含40个存储单元(或元素)的数组,每个单元储存一个

char类型的值:
char name[40];
           

name后面的方括号表明这是一个数组,方括号中的40表明该数组中的元素数量。char表明每个元素的类型

第五 字符串和格式化输入/输出C语言学习栏目目录

2.2 使用字符串

先来个程序:

/************************************************************************

程序功能:输入你的名字,并显示在控制台
函数:name[40]  char数组,存放名字   		

************************************************************************/
#include<stdio.h>
#define PRAISE "You are an extraordinary being."		//定义字符串常量

int main(void) 
{
	char name[40];
	printf("What's your name? \n");
	scanf("%s", name);						//输入名字
	printf("Hello, %s.%s\n", name, PRAISE); //打印名字与常量
	system("pause");
	return 0;
}
           

    %s告诉printf()打印一个字符串。%s出现了两次,因为程序要打印两个字符串:一个储存在name数组中;一个由PRAISE来表示。运行程序,其输出如下所示:

What's your name?
陈   大
Hello, 陈.You are an extraordinary being.
请按任意键继续. . .
           

你不用亲自把空字符放入字符串末尾,scanf()在读取输入时就已完成这项工作。也不用在字符串常量PRAISE末尾添加空字符。稍后我们会解释#define指令,现在先理解PRAISE后面用双引号括起来的文本是一个字符串。编译器会在末尾加上空字符。注意(这很重要)scanf()只读取了陈  大中的陈,它在遇到第1个空白(空格、制表符或换行符)时就不再读取输入。因此,scanf()在读到“陈”和”大“之间的空格时就停止了。一般而言,根据%s转换说明,scanf()只会读取字符串中的一个单词,而不是一整句。C语言还有其他的输入函数(如fgets()),用于读取一般字符串。后面章节将详细介绍这些函数。

字符串和字符

字符串常量"x"和字符常量'x'不同。区别之一在于'x'是基本类型(char),而"x"是派生类型(char数组);区别之二是"x"实际上由两个字符

组成:'x'和空字符\0(如下图)

第五 字符串和格式化输入/输出C语言学习栏目目录

2.3 strlen()函数

前面提到了 sizeof 运算符,它以字节为单位给出对象的大小。strlen()函数给出字符串中的字符长度。因为 1 字节储存一个字符,读者可能认为把两种方法应用于字符串得到的结果相同,但事实并非如此。在上面程序清单中添加几行代码,看看为什么会这样。

/************************************************************************

程序功能:
main: 输入你的名字,并显示在控制台
main1:输入你的名字,并显示在控制台,显示字符串长度
函数:name[40]  char数组,存放名字   		

************************************************************************/
#include<stdio.h>
#include <string.h>  //提供strlen()函数的原型/
#define PRAISE "You are an extraordinary being."		//定义字符串常量

int main(void)
{
	char name[40];
	printf("What's your name? \n");
	scanf("%s", name);						//输入名字
	printf("Hello, %s.%s\n", name, PRAISE); //打印名字与常量
	//显示名字的长度与name数组存储单元
	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;
}
           
#include <string.h>  
           

     string.h头文件包含多个与字符串相关的函数原型,包括strlen()。后续j将详细介绍该头文件(顺带一提,一些ANSI之前的UNIX系统用strings.h代替string.h,其中也包含了一些字符串函数的声明)。

     一般而言,C 把函数库中相关的函数归为一类,并为每类函数提供一个头文件。例如,printf()和scanf()都隶属标准输入和输出函数,使用stdio.h头文件。string.h头文件中包含了strlen()函数和其他一些与字符串相关的函数(如拷贝字符串的函数和字符串查找函数)。

     注意,程序清单使用了两种方法处理很长的printf()语句。第1种方法是将printf()语句分为两行(可以在参数之间断为两行,但是不要在双引号中的字符串中间断开);第 2 种方法是使用两个printf()语句打印一行内容,只在第2条printf()语句中使用换行符(\n)。运行该程序,其交互输出如下:

What's your name?
陈
Hello, 陈.You are an extraordinary being.
Your name of 2 letters occupies 40 memory cells.
The phrase of praise has 31 letters and occupies 32 memory cells.
请按任意键继续. . .
           

sizeof运算符报告,name数组有40个存储单元。但是,只有前2个单元用来储存"陈",所以strlen()得出的结果是2。name数组的第3个单元

储存空字符,strlen()并未将其计入。下图演示了这个概念。

第五 字符串和格式化输入/输出C语言学习栏目目录

    对于 PRAISE,用 strlen()得出的也是字符串中的字符数(包括空格和标点符号)。然而,sizeof运算符给出的数更大,因为它把字符串末尾不可见的空字符也计算在内。该程序并未明确告诉计算机要给字符串预留多少空间,所以它必须计算双引号内的字符数。C99 和 C11 标准专门为 sizeof 运算符的返回类型添加了%zd 转换说明,这对于strlen()同样适用。对于早期的C,还要知道sizeof和strlen()返回的实际类型(通常是unsigned或unsigned long)。另外,还要注意一点:上一章的 sizeof 使用了圆括号,但本例没有。圆括号的使用时机否取决于运算对象是类型还是特定量?运算对象是类型时,圆括号必不可少,但是对于特定量,可有可无。也就是说,对于类型,应写成sizeof(char)或sizeof(float);对于特定量,可写成sizeof name或sizeof 6.28。尽管如此,还是建议所有情况下都使用圆括号,如sizeof(6.28)。

3 常量和C预处理器

    有时,在程序中要使用常量。例如,可以这样计算圆的周长:

circumference = 3.14159 * diameter
           

    这里,常量3.14159代表著名的常量pi(π)。在该例中,输入实际值便可使用这个常量。然而,这种情况使用符号常量(symbolic  constant)会更好。也就是说,使用下面的语句,计算机稍后会用实际值完成替换:

circumference = pi * diameter
           

     为什么使用符号常量更好?首先,常量名比数字表达的信息更多。请比较以下两条语句:

owed = 0.015 * housevalue;
owed = taxrate * housevalue;
           

    如果阅读一个很长的程序,第2条语句所表达的含义更清楚。另外,假设程序中的多处使用一个常量,有时需要改变它的值。毕竟,税率通常是浮动的。如果程序使用符号常量,则只需更改符号常量的定义,不用在程序中查找使用常量的地方,然后逐一修改。那么,如何创建符号常量?方法之一是声明一个变量,然后将该变量设置为所需的常量。可以这样写:

float taxrate;
taxrate = 0.015;
           

     这样做提供了一个符号名,但是taxrate是一个变量,程序可能会无意间改变它的值。C语言还提供了一个更好的方案——C预处理器。第2  章中介绍了预处理器如何使用#include包含其他文件的信息。预处理器也可用来定义常量。只需在程序顶部添加下面一行:

#define TAXRATE 0.015
           

    编译程序时,程序中所有的TAXRATE都会被替换成0.015。这一过程被称为编译时替换(compile-time substitution)。在运行程序时,程序中所有的替换均已完成)。通常,这样定义的常量也称为明示常量(manifest constant)

    请注意格式,首先是#define,接着是符号常量名(TAXRATE),然后是符号常量的值(0.015)(注意,其中并没有=符号)。所以,其通用格式如下:

#define NAME value
           

     实际应用时,用选定的符号常量名和合适的值来替换NAME和value。注意,末尾不用加分号,因为这是一种由预处理器处理的替换机制。为什么TAXRATE 要用大写?用大写表示符号常量是 C 语言一贯的传统。这样,在程序中看到全大写的名称就立刻明白这是一个符号常量,而非变量。大写常量只是为了提高程序的可读性,即使全用小写来表示符号常量,程序也能照常运行。尽管如此,初学者还是应该养成大写常量的好习惯。另外,还有一个不常用的命名约定,即在名称前带c_或k_前缀来表示常量(如,c_level或k_line)。符号常量的命名规则与变量相同。可以使用大小写字母、数字和下划线字符,首字符不能为数字。

第五 字符串和格式化输入/输出C语言学习栏目目录

来个程序:

/************************************************************************
功能:演示常量使用
************************************************************************/
#include <stdio.h>
#define PI 3.14159		//定义常量

int main(void)
{
	float area, circum, radius;
	printf("你的圆饼半径是多少?\n");
	scanf("%f", &radius);	//输入半径
	area = PI * radius * radius;	//计算面积
	circum = 2.0 * PI *radius;	//计算周长
	printf("你的圆饼参数是:\n");
	printf("circumference = %1.2f, area = %1.2f\n", circum, area);
	system("pause");
	return 0;
}
           

printf()语句中的%1.2f表明,结果被四舍五入为两位小数输出。下面是一个输出示例:

你的圆饼半径是多少?
2
你的圆饼参数是:
circumference = 12.57, area = 12.57
请按任意键继续. . .

           

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

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

记住,符号常量名后面的内容被用来替换符号常量。不要犯这样的常见错误:

/* 错误的格式 */
#define TOES = 20
如果这样做,替换TOES的是= 20,而不是20。这种情况下,下面的语句:
digits = fingers + TOES;
将被转换成错误的语句:
digits = fingers + = 20;
           

源代码下载