文章目录
1. 字符串
1.1 字符串简介
字符串(character string)是一个或多个字符的序列。例如:“a”、“abc”、“123”、“Hello World!”。其中双引号不是字符串的一部分,双引号只是告诉编译器它括起来的是字符串。
C 语言中没有专门用来存储字符串的的变量类型,字符串都被存储在 char 类型的数组中。数组由连续的存储单元组成,字符串中的字符被存储在相邻的存储单元中,每个单元存储一个字符。
其中每一个存储单元一个字节,数组末尾位置的字符是 /0。这是空字符,C 语言用它标记字符串的结束。C 中字符串一定以空字符结尾,所以数组的容量至少比待存储的字符串中字符数多 1。
那么什么是数组?数组可以看做一行连续的多个存储单元,更正式的说法是,数组是同类型数据元素的有序序列。
1.2 strlen() 函数
上一篇博客提到了 sizeof 运算符,它以字节为单位给出对象的大小。而 strlen() 函数给出字符串中字符的长度。
#include <stdio.h>
#include <string.h>
#define S "Hello world!"
int main(void)
{
char name[40];
printf("What's your name?\n");
scanf("%s",name);
printf("Hello %s\n",name);
printf("Your name of %zd letters occupies %zd memory cells.\n",strlen(name),sizeof name);
printf("The phrase of S has %zd letters and occupies %zd momory cells.\n",strlen(S),sizeof S);
return 0;
}
上面程序中,name 数组有 40 个存储单元,但是只有 3 个单元用来存储 Tom,所以 strlen() 得出的结果是 3。name 数组的第 4 个单元存储空字符 \0,但是 strlen() 并不将它计入其中。对于字符串 S,用 strlen() 得出是字符串中字符的个数,而 sizeof 运算符给出的数比前者大 1,因为它要将字符串最后的空字符计算其中。
上面程序中,sizeof 圆括号的使用取决于运算对象是类型还是特定量。运算对象是类型时,圆括号必不可少,但是对于特定量,可有可无。尽管如此,还是推荐所有情况下都使用圆括号。
2. 常量
2.1 明示常量(符号常量)
预处理器也可以用来定义常量,只需要在程序顶部添加一行语句,语句语法格式为:
#define NAME value
实际应用时,用选定的符号常量名和合适的值替换 NAME 和 value。用大写表示常量符号是 C 语言一贯的传统。例如:#define NUM 1.5 ,编译程序时,程序中所有的 NUM 都会被替换成 1.5。这一过程被称为编译时替换。通常,这样定义的常量也称为 明示常量。#define 指令还可以定义字符和字符串常量。前者使用单引号,后者使用双引号。
C 头文件 limits.h 和float.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 类型最小值 | USHR_MAX | unsigned 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 类型最大值 |
明示常量 | 含义 | 明示常量 | 含义 |
---|---|---|---|
FLT_MANT_DIG | float 类型的位数位数 | FLT_DIG | float 类型的最少有效数字位数 |
FLT_MIN_10_EXP | 带全部有效数字的 float 类型的最小负指数 | FLT_MAX_10_EXP | float 类型的最大正指数 |
FLT_MIN | 保留全部精度的 float 类型最小正数 | FLT_MAX | float 类型最大正数 |
其中把明示常量名中的 FLT 分别替换成 DBL 和 LDBL,即可分别表示 double 和 long double 类型对应的明示常量。
2.2 const 限定符
C90 标准增加了const 关键字,用于限定一个变量为只读。注意,在 C 语言中,用 const 类型限定符声明的是变量,不是常量。
例如:下面的 NUM 成为一个只读值,即不可更改 NUM 的值。
const int NUM = 12;
3. printf 函数
printf() 和 scanf() 函数是输入/输出函数,简称 I/O 函数
3.1 printf 函数用法
printf(格式字符串,待打印项1,待打印项2,……)
举个例子:
printf("His name is %s,and his age is %d.\n",name,age)
待打印项可以是变量、常量、甚至是在打印之前先要计算的表达式。格式字符串应包含每个待打印项对应的转换说明。上面的例子中,格式化字符串是双引号括起来的内容,其中的 %s 和 %d ,这些符号被称为 转换说明,它们指定了如何把数据转换成可显示的形式。如果只打印短语或句子,就不需要使用任何转换说明。
3.2 printf 转换说明
转换说明 | 输出 | 转换说明 | 输出 |
---|---|---|---|
%a 和 %A | 浮点数、十六进制数和 p 计数法(C99/C11) | %c | 单个字符 |
%d | 有符号十进制整数 | %e 或 %E | 浮点数、e 计数法 |
%f | 浮点数、十进制计数法 | %g 或 %G | 根据值不同,自动选择 %f 或 %e。 |
%i | 有符号十进制整数(与 %d 相同) | %o | 无符号八进制整数 |
%p | 指针 | %s | 字符串 |
%u | 无符号十进制整数 | %x 或 %X | 无符号十六进制整数 |
%% | 一个百分号 |
3.3 printf 修饰符
在 % 和 转换字符之间插入修饰符可以修饰基本的转换说明。
修饰符 | 说明 |
---|---|
标记 | 一共有 5 种标记(-、+、空格、#、0),下面表格具体说明。可以不使用或使用多个标记。例:“%-10d” |
数字 | 表示最小字段宽度。如果该字段不能容纳待打印数字或字符串,系统会使用更宽的字段。例: “%4d” |
. 数字 | 表示精度。 对于 %e,%E 和 %f,表示小数点右边数字的位数 对于 &g 和 %G,表示有效数字的最大位数 对于%s,表示待打印字符的最大数量 对于 %d,表示待打印数字的最小位数,若有必要,使用前导 0 来达到这个位数 只使用 . 表示其后跟随一个 0,所以 . f 和 . 0f 相同 例:“%5.2f” |
h | 和整型转换说明一起使用,表示 short int 或 unsigned short int 类型的值。例:“%hu”、“%hx” |
hh | 和整型转换说明一起使用,表示 short char 或 unsigned char 类型的值。 例:“%hhu”、“%hhx” |
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” |
z | 和整型转换说明一起使用,表示 size_t 类型的值。size_t 是 sizeof 返回的类型。(C99)例:“%zd” |
标记 | 含义 |
---|---|
- | 表示待打印项左对齐。例:“%-20s” |
+ | 有符号值为正,则在前面显示加号;若为负,则在前面显示减号。 例:“%+6.2f” |
空格 | 有符号值为正,则在前面显示前导空格(不显示任何符号);若为负,则在前面显示减号。 + 标记覆盖一个空格。例:“%6.2f” |
# | 把结果转换为另一种形式。 如果是 %o 格式,则以 0 开始;若以 %x 或 %X 格式,则以 0x 或 0X 开始; 对于所有浮点格式,# 保证了即使后面没有任何数字,也打印一个小数点字符; 对于 %g 和 %G,# 防止结果后面的 0 被剔除。 例:“%#o”、“%#8.0f” |
0 | 对于数值格式,用前导 0 代替空格填充字段宽度。对于整数格式,如果出现 - 标记或指定精度,则忽略该标记。 |
printf() 函数练习:
#include <stdio.h>
#define NUM 123
#define PRASE "Hello World!"
int main(void)
{
// 字段宽度在打印整数时的效果
printf("*%d*\n", NUM);
printf("*%2d*\n", NUM);
printf("*%10d*\n", NUM);
printf("*%-10d*\n", NUM);
printf("-------------------------\n");
// 浮点型格式
const double MONEY = 9817.58;
printf("*%f*\n", MONEY);
printf("*%e*\n", MONEY);
printf("*%4.2f*\n", MONEY);
printf("*%3.1f*\n", MONEY);
printf("*%10.3f*\n", MONEY);
printf("*%10.3E*\n", MONEY);
printf("*%+4.2f*\n", MONEY);
printf("*%010.2f*\n", MONEY);
printf("-------------------------\n");
// 格式标记
printf("%x %X %#x\n", 25, 25, 25);
printf("**%d**% d**% d**\n", 58, 58, -58);
printf("**%5d**%5.3d**%05d**%05.3d**\n", 6, 6, 6, 6);
printf("-------------------------\n");
// 字符串格式
printf("[%2s]\n", PRASE);
printf("[%24s]\n", PRASE);
printf("[%24.5s]\n", PRASE);
printf("[%-24.5s]\n", PRASE);
return 0;
}
3.4 printf 返回值
printf() 函数也有一个返回值,它返回打印字符的个数,这里计算的是所有字符,包括空格和不可见换行符等。如果输出有误,则返回一个负值(printf() 的旧版会返回不同的值)。
3.5 输出较长字符串的三种方式
- 使用多个 printf() 函数。
- 用反斜杠(\)和 Enter 键组合来断行。注意的是下一行代码必须从最左面开始。
- 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;
}
4. scanf() 函数
4.1 scanf 函数用法
C 库中包含多个输入函数,scanf() 是最通用的一个,scanf() 和 printf() 类似,也使用格式字符串和参数列表。两个函数的主要区别在于参数列表中。printf() 函数使用变量、常量和表达式,而 scanf() 函数使用 指向变量的指针。指针会在后面详细介绍,这里只做了解,只需记住两条规则:
- 如果用 scanf() 读取基本变量类型的值,在变量名前加上一个 &;
- 如果用 scanf() 把字符串读入字符串数组中,不要使用 &。
#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;
}
scanf() 函数使用空白(换行符、制表符和空格)把输入分为多个字段。在依次把转换说明和字段匹配时跳过空白。唯一的例外是 %c 转换说明,根据 %c,scanf() 会读取每个字符,包括空白。
4.2 scanf 转换说明
转换说明 | 含义 | 转换说明 | 含义 |
---|---|---|---|
%c | 把输入解释成字符 | %d | 把输入解释成有符号十进制整数 |
%e,%f,%g,%a %E,%F,%G,%A |
把输入解释成浮点数 | %i | 把输入解释成有符号十进制整数 |
%o | 把输入解释成有符号八进制整数 | %p | 把输入解释成指针(地址) |
%s | 把输入解释成字符串。从第 1 个非空白字符开始, 到下一个空白字符之前的所有字符都是输入 |
%u | 把输入解释成无符号十进制整数 |
%x 或 %X | 把输入解释成有符号十六进制整数 |
4.3 scanf 修饰符
在 % 和 转换字符之间插入修饰符可以修饰基本的转换说明。
修饰符 | 说明 |
---|---|
* | 抑制赋值。例:“%*d” |
数字 | 表示最大字段宽度。输入达到最大字段宽度处,或第 1 次遇到空白符时停止。例: “%10s” |
hh | 把整数作为 signed char 或 unsigned char l类型读取。例:“%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 类型。例:“%jd”、“%jx” |
t | 和整型转换说明一起使用,表明使用两个指针差值的类型。(C99) 例:“%td” |
z | 和整型转换说明一起使用,表明使用 sizeof 的返回类型。(C99)例:“%zd” |
4.4 scanf 返回值
scanf() 函数返回成功读取的项数。如果没有读取任何项,且需要读取一个数字而用户输入一个非数值字符串,scanf() 便返回 0。当 scanf() 检测到 “文件末尾” 时,会返回 EOF(EOF 是 stdio.h 中定义的特殊值,通常用 #define 指令把 EOF 定义为 -1)。