一、字符串简介1.1char类型数组和空字符null characterC语言中的字符串实际上是以空字符\0结尾的char数组。空字符\0是一个非打印字符其ASCII码值为0用于标记字符串的结束。数组容量存储字符串时数组的大小必须比待存储字符串的字符数多1以容纳结尾的\0。示例定义charstr[20];// 可以存储最多19个有效字符 \0charname[]Alice;// 自动分配6个字节5个字符 \01.2 使用字符串scanf()读取字符串时会自动在输入末尾添加\0。scanf()遇到空白字符空格、制表符、换行符即停止读取因此不能读取包含空格的字符串。fgets()可用于读取包含空格的字符串更安全。字符串与字符的区别字符串用双引号 括起来字符用单引号 括起来。sizeof运算符以字节为单位给出对象或类型的大小strlen()函数返回字符串中有效字符的个数不含\0。二、常量和 C 预处理器2.1const限定符const用于声明一个只读变量其值在初始化后不能被修改。比#define更灵活因为它具有类型和作用域规则。constdoublePI3.14159;2.2 明示常量#define使用#define在预处理阶段进行文本替换通常用于定义数值常量或宏。头文件limits.h和float.h提供了与整数类型和浮点类型大小限制相关的宏例如整数INT_MAX、INT_MIN、LONG_MAX等。浮点FLT_DIGfloat有效位数、DBL_DIGdouble有效位数等。2.3#define与const对比#define无类型检查预处理阶段替换不占用存储空间直接替换作用域全局const有类型检查编译阶段处理占用内存作为变量遵循块作用域规则三、printf()和scanf()这两个函数是C语言中最常用的I/O函数负责程序与用户之间的交互。3.1printf()函数概述转换说明格式字符串中的%开头的符号用于指示如何将数据转换为可显示形式。ANSI C 提供的转换说明%a%A%c%d%e%E%f%g%G%i%o%p%s%u%x%X%%3.2 使用printf()基本格式printf(格式字符串,待打印项1,待打印项2,...);待打印项可以是变量、常量或表达式格式字符串中必须为每个待打印项提供对应的转换说明。格式字符串是用双引号括起来的内容可以包含普通字符和转换说明。警告转换说明的类型必须与待打印项的类型严格匹配否则会导致未定义行为。若要打印百分号%使用%%进行转义。3.3printf()的转换说明修饰符在%和转换字符之间可以插入修饰符来调整输出格式修饰符说明-左对齐默认右对齐有符号数强制显示正号或负号-空格正数前显示空格负数显示负号#对%o、%x、%X添加前缀如0、0x对浮点数强制显示小数点0用前导零填充字段宽度与精度同时出现时0被忽略数字最小字段宽度.数字精度小数位数、字符串最大打印字符数等h与整数说明符配合表示short int或unsigned short inthh表示signed char或unsigned charj表示intmax_t或uintmax_t需inttypes.hl表示long int或unsigned long intll表示long long int或unsigned long long intL表示long doublet表示ptrdiff_t两个指针差值的类型z表示size_tsizeof返回的类型关于类型可移植性sizeof返回的类型在不同系统上可能是unsigned int、unsigned long或unsigned long long。直接使用%u、%lu等可能导致移植问题。C标准在stddef.h中定义了size_t作为底层类型并使用z修饰符来打印它例如%zu。类似地ptrdiff_t和t修饰符用于打印指针差值。关于float参数的转换在printf()中没有专门针对float的转换说明%f实际对应double。这是因为在旧版 CKR C中float参数在传递给函数时会被自动提升为double。ANSI C 虽然不再强制提升但为了兼容性printf()仍将float视为double处理。示例字段宽度和标记/* width.c -- 字段宽度示例 */#includestdio.h#definePAGES959intmain(void){printf(*%d*\n,PAGES);// 输出: *959*printf(*%2d*\n,PAGES);// 字段宽度2不够则自动扩展 → *959*printf(*%10d*\n,PAGES);// 右对齐宽度10 → * 959*printf(*%-10d*\n,PAGES);// 左对齐宽度10 → *959 *return0;}/* floats.c -- 浮点数修饰符组合 */#includestdio.hintmain(void){constdoubleRENT3852.99;printf(*%f*\n,RENT);// *3852.990000*printf(*%e*\n,RENT);// *3.852990e03*printf(*%4.2f*\n,RENT);// *3852.99*printf(*%3.1f*\n,RENT);// *3853.0*printf(*%10.3f*\n,RENT);// * 3852.990*printf(*%10.3E*\n,RENT);// * 3.853E03*printf(*%4.2f*\n,RENT);// *3852.99*printf(*%010.2f*\n,RENT);// *0003852.99*return0;}/* 其他修饰符示例 */#includestdio.hintmain(void){printf(%x %X %#x\n,31,31,31);// 1f 1F 0x1fprintf(**%d**% d**% d**\n,42,42,-42);// **42** 42**-42**printf(**%5d**%5.3d**%05d**%05.3d**\n,6,6,6,6);// ** 6** 006**00006**006** (0标记与精度同时出现时0被忽略)return0;}/* stringf.c -- 字符串格式 */#includestdio.h#defineBLURBAuthentic imitation!intmain(void){printf([%2s]\n,BLURB);// [Authentic imitation!]printf([%24s]\n,BLURB);// [ Authentic imitation!]printf([%24.5s]\n,BLURB);// [ Authe]printf([%-24.5s]\n,BLURB);// [Authe ]return0;}3.4 转换说明的意义转换说明将存储在内存中的二进制数据按照指定格式转换为字符序列显示。3.4.1 转换不匹配当转换说明与参数类型不匹配时可能导致错误输出甚至程序崩溃。/* intconv.c -- 整型转换不匹配示例 */#includestdio.h#definePAGES336#defineWORDS65618intmain(void){shortnumPAGES;shortmnum-PAGES;printf(num as short and unsigned short: %hd %hu\n,num,num);// num as short and unsigned short: 336 336printf(-num as short and unsigned short: %hd %hu\n,mnum,mnum);// -num as short and unsigned short: -336 65200// 若 short 为 2 字节且使用补码65200 表示 -336printf(num as int and char: %d %c\n,num,num);// num as int and char: 336 P (336 的低 8 位为 P)printf(WORDS as int, short, and char: %d %hd %c\n,WORDS,WORDS,WORDS);// WORDS as int, short, and char: 65618 82 R// 65618 0x10052截断为 16 位得 0x005282低 8 位为 0x52Rreturn0;}/* floatcnv.c -- 浮点型转换不匹配示例 */#includestdio.hintmain(void){floatn13.0;doublen23.0;longn32000000000;longn41234567890;printf(%.1e %.1e %.1e %.1e\n,n1,n2,n3,n4);// 输出无意义值因为 %e 期望 double (8 字节)但 n3/n4 是 long (4 字节)printf(%ld %ld\n,n3,n4);// 正确printf(%ld %ld %ld %ld\n,n1,n2,n3,n4);// 错误参数传递混乱return0;}3.4.2printf()的返回值返回成功打印的字符总数包括空格和不可见字符。若发生输出错误则返回一个负值。3.4.3 打印长字符串的多种方式/* longstrg.c -- 打印较长字符串 */#includestdio.hintmain(void){printf(Heres one way to print a );printf(long string.\n);// 分两段printf(Heres another way to print a \\longstring.\n);// 反斜杠续行printf(Heres the newest way to print a long string.\n);// ANSI C 字符串拼接return0;}/* 输出均为 Heres one way to print a long string. Heres another way to print a long string. Heres the newest way to print a long string. */3.5 使用scanf()scanf()用于从标准输入读取数据并根据格式字符串进行解析。基本格式scanf(格式字符串, 变量地址列表);读取基本类型变量时变量名前需加地址运算符如num。读取字符串%s时变量名本身就是地址数组名不需要。3.5.1 空白字符的处理scanf()将空白字符空格、制表符、换行视为字段分隔符读取时自动跳过。但%c转换说明不会跳过空白它会读取输入中的下一个字符包括空白。若想跳过空白可在%c前加一个空格 %c。3.5.2scanf()的转换说明转换说明含义%c读取单个字符%d读取十进制整数%e%f%g%a读取浮点数可指数形式%E%F%G%A同上大小写变体%i读取整数自动识别八进制/十六进制前缀%o读取八进制整数%p读取指针地址%s读取字符串不含空白%u读取无符号十进制整数%x%X读取十六进制整数3.5.3scanf()的修饰符修饰符作用*抑制赋值读取但不存储数字最大字段宽度hh读取为signed char/unsigned charll读取为long long/unsigned long longh、l、L分别对应short、long、long doublejzt与printf()中意义相同3.5.4scanf()的读取机制对于%d跳过空白读取数字或符号直到遇到非数字字符该字符被“放回”输入流供下一次读取使用。若使用字段宽度则在宽度结束或遇到空白时停止。若第一个非空白字符不匹配转换说明则scanf()停止解析并返回已成功读取的项数。%s会跳过前导空白读取连续非空白字符并在末尾自动添加\0。3.5.5 格式字符串中的普通字符除了空格格式字符串中的普通字符必须与输入严格匹配。例如scanf(%d,%d, a, b);要求输入必须包含逗号。注意除%c外其他转换说明会自动跳过输入值前的空白而%c不会但 %c会先跳过空白再读一个字符。3.5.6scanf()的返回值返回成功匹配并赋值的输入项数。若遇到文件末尾EOF则返回EOF通常为 -1。3.6printf()和scanf()的*修饰符*允许动态指定字段宽度或精度通过参数传递。/* varwid.c -- 变宽输出 */#includestdio.hintmain(void){unsignedwidth,precision;intnumber256;doubleweight242.5;printf(Enter a field width:\n);scanf(%d,width);printf(The number is :%*d:\n,width,number);// 输出宽度由 width 决定printf(Now enter a width and a precision:\n);scanf(%d %d,width,precision);printf(Weight %*.*f\n,width,precision,weight);// 宽度和精度均由参数指定printf(Done!\n);return0;}在scanf()中*用于跳过输入项不存储/* skiptwo.c -- 跳过前两个整数 */#includestdio.hintmain(void){intn;printf(Please enter three integers:\n);scanf(%*d %*d %d,n);// 跳过前两个只读取第三个printf(The last integer was %d\n,n);return0;}3.7printf()的使用提示要使输出对齐固定字段宽度非常有效。在多个转换说明之间插入空格或换行可避免数字粘连。若要将数字嵌入文本中通常指定一个小于或等于数字实际宽度的字段以避免多余空白。本地化某些地区使用逗号作为小数点分隔符但printf()和scanf()并未提供直接支持。可以通过setlocale()函数设置本地化但这属于高级主题。## 四、关键概念总结字符串本质以空字符\0结尾的char数组。字符串常量用双引号字符常量用单引号。常量定义#define用于预处理替换const用于声明只读变量后者具有类型检查优势。空白字符的作用在scanf()中除%c外空白被用作分隔符并被忽略而%c会读取所有字符包括空白。类型匹配printf()和scanf()的转换说明必须与参数类型严格匹配否则可能导致未定义行为。返回值printf()返回打印字符数scanf()返回成功读取的项数可利用它们进行错误检查。