数浮点数(小数)字符字符串。描述整数使用整型类型描述浮点数(小数)使用浮点型类型描述字符使用字符类型。使用类型可以归纳相似的数据编译器提前知晓数据的类型才能操作数据。在C语言中数据类型有两大类如图结构数据类型├── 内置类型│ ├── 字符型│ │ └── char│ ├── 整型│ │ ├── short│ │ ├── int│ │ ├── long│ │ └── long long│ ├── 浮点型│ │ ├── float│ │ ├── double│ │ └── long double│ └── 布尔类型│ └── _Bool└── 自定义类型├── 数组├── 结构体-struct├── 枚举-enum└── 联合体-union数据类型笔者现在只学习到内置类型因此本章主要梳理内置类型下面会先引入了unsigned和signed这两个关键字以节省篇幅这两个关键字用来修饰字符型和整型类型会在后面详细介绍下文中的[ ]表示可省略。1.1 字符型字符character,在C语言中取char表示字符类型。[signed] char //有符号的char[unsigned] char // 无符号的char1.2 整型整数integer在C语言中取int表示整型类型。//短整型short [int][signed] short [int]unsigned short [int]//整型int[signed] intunsigned int//长整型long [int][signed] long [int]unsigned long [int]//更长整型long long [int][signed] long long [int]unsigned long long [int]1.3 浮点型floatdoublelong double1.4 布尔类型C语言中使用整数0表示假非0值表示真。在C99中特别加入了布尔类型用来专门区分真假。这样布尔值就有单独的一个类型了_Bool使用布尔类型需要包含头文件stdbool.h其变量取值是:true or false在stdbool.h头文件里存放着#define bool _Bool#define true 1#define false 0_Bool可以拿来使用_Bool flag true;if (flag)printf(“its true”);由于_Bool敲写起来有点麻烦因此现在也可以用bool:bool flag true;if (flag)printf(“its true”);1.5 数据类型的长度不同的数据类型能够创造出长度不同的变量而变量长度则影响变量能存储的数据多少。在本文1.7会详细阐述。1.6 sizeof操作符sizeof是关键字同时也是操作符可以用来计算sizeof中包含的操作数的类型长度其单位是字节1 字节 8 比特sizeof操作符的操作数可以是类型变量也可以是表达式。sizeof (/这里存放类型/)sizeof 表达式 //表达式的括号可以省略要注意sizeof其中的表达式不参与真实运算根据表达式的类型来得出大小这么说可能有点混乱别担心会在下文1.8详细解释。sizeof的计算结果的类型是size_t想打印这个类型需要使用占位符%zu#include stdio.hint main(void){int i 100;printf(“%zu\n”,sizeof(i));printf(“%zu\n”,sizeof i);//因为 i 是变量所以可以省略括号printf(“%zu\n”,sizeof (int));printf(“%zu\n”,sizeof (2 1));return 0;}那么sizeof的返回值是什么类型呢由于这里还没有涉及符号的概念会在下文2讲到符号的时候详细阐述。1.7 数据类型长度#include stdio.hint main(void){printf(“%zu\n”,sizeof(char));printf(“%zu\n”,sizeof(_Bool));printf(“%zu\n”,sizeof(short));printf(“%zu\n”,sizeof(int));printf(“%zu\n”,sizeof(long));printf(“%zu\n”,sizeof(long long));printf(“%zu\n”,sizeof(float));printf(“%zu\n”,sizeof(double));printf(“%zu\n”,sizeof(long double));return 0;}这里重申sizeof操作符可以用来计算sizeof中包含的操作数的类型长度其单位是字节1 字节 8 比特在Debian12.0 gcc x86_64环境下会输出1124884816在Windows下MinGw-w64环境会输出1124484816在Windows下使用VS2026 x64即MSVC环境下则会输出112448488这三组输出分别来自三种主流 C 编译环境差异来源于操作系统 ABI应用二进制接口和编译器实现策略的不同1.long 大小不同8 vs 4原因数据模型Data Model不同Linux / macOS中使用LP64 模型Long 64 位8 字节Pointer 64 位Int 32 位→ 称为 LP64Windows中使用LLP64 模型Long 32 位4 字节Long Long 64 位Pointer 64 位→ 称为 LLP64所有 Windows 编译器包括 MinGW 和 MSVC都遵循 LLP64因此 sizeof(long) 4。而 Linux/GCC 遵循 LP64故 sizeof(long) 8。这些就是跨平台整数类型不一致的最常见来源。2.long double 大小不同16 vs 8GCC含 MinGW行为在 x86/x86_64 上使用 x87 FPU 的 80 位扩展精度浮点格式实际有效数据10 字节80 位但为满足 16 字节对齐要求x86_64 ABI 规定sizeof(long double) 16MSVC 行为不支持 80 位扩展精度将 long double 直接等同于 double因此 sizeof(long double) sizeof(double) 8这意味着在 MSVC 下long double 不会提供比 double 更高的精度而 GCC 下会约 19 位十进制精度 vs 15~17)。因此跨平台开发要注意这些差异3. 其他类型为何一致charC 标准规定 sizeof(char) 1_BoolC99 标准要求最小可寻址单位通常为 1 字节所有现代编译器遵守short/int/long long/float/double在 32/64 位主流平台上这些类型的大小已高度统一尤其 int 几乎总是 32 位即使在 64 位系统long long 自 C99 起保证 ≥64 位各编译器均实现为 8 字节总结一下差异点 根本原因long 大小 操作系统 ABI 不同Linux 使用 LP64Windows 使用 LLP64long double 大小 编译器实现策略不同GCC 支持 x87 扩展精度MSVC 不支持并降级为 double而根据 C 标准C11 §5.2.4.2.1 / C17 同理int 至少 16 位long 至少 32 位并且必须满足csizeof(short) sizeof(int) sizeof(long) sizeof(long long)因此sizeof(long) sizeof(int) 永远成立实际常见情况Linux x86_64int4, long8 → 满足Windows x64int4, long4 → 相等也满足 ≥又根据 C 标准C11 §5.2.4.2.2 / Floating-point typesThe values of the floating types are specified in terms of precision and range, and:float ≤ double ≤ long double in both precision and storage size.虽然标准没有直接写 sizeof(long double) sizeof(double)但它规定了double 的精度 不低于 floatlong double 的精度 不低于 double而在所有现实实现中更高的精度必须通过更大的存储空间实现因此所有主流编译器和平台都保证sizeof(float) sizeof(double) sizeof(long double)实际常见情况平台 sizeof(double) sizeof(long double)所有平台 8 —Linux (GCC/x86_64) 8 16含填充或 12有效 80 位Windows (MSVC) 8 8long double 等同于 doubleWindows (MinGW-w64) 8 16即使 MSVC 中两者相等8 8仍然满足 故sizeof(short) sizeof(int) sizeof(long) sizeof(long long)sizeof(float) sizeof(double) sizeof(long double)是始终成立的也可简单记忆差异项sizeof(long)sizeof(int)sizeof(long double)sizeof(double)一些跨平台开发建议:避免依赖long的大小使用 stdint.h 中的固定宽度类型#include stdint.hint32_t a; // 明确 32 位int64_t b; // 明确 64 位不要假设 long double 有更高精度→ 若需高精度使用第三方库如 MPFR、Boost.Multiprecision→ 或明确使用 double 并接受其限制用 size_t 表示对象大小intptr_t 表示指针整数转换1.8sizeof操作符中的表达式不计算可以通过以下验证#include stdio.hint main(void){short a 10;int b 100;printf(“%zu\n”, sizeof(a b 1));printf(“s %d\n”, a);return 0;终端会输出2102. signed 和 unsigned以及数据类型的取值范围signed和unsigned是两个关键字用来修饰字符型和整型。signed表示此类型带有正负号unsigned表示此类型不带有正负号只能表示0或正整数。例如对int类型默认携带正负号前面的unsigned是可以省略不写的默认携带正负号即使加上去变成signed int也不会有什么错误只是稍显繁杂。即signed int a int a;对于不携带正负号的int就必须加上unsigned声明修饰变量unsigned int a;修饰unsigned可以让相同长度的内存存储更大的数字实际增加了一倍下面会对不同的编译环境进行比较不同编译器MSVC、MinGW、GCC x86_64下的行为:2.1整数变量将整数变量声明为 unsigned可以在相同内存长度下使最大可表示值翻倍。例如signed short int16 位范围为 -32768 ~ 32767最大值为 32767unsigned short int16 位范围为 0 ~ 65535最大值为 65535这一特性在 MSVCVisual Studio、MinGW、GCCx86_64 平台下完全一致因为这些编译器在 x86_64 架构上对 short 和 int 的大小定义是统一的。2.1.1各编译器 limits.h 中整数定义对比x86_64Microsoft Visual Studio (MSVC)#define SHRT_MIN (-32768)#define SHRT_MAX 32767#define USHRT_MAX 0xFFFF // 65535#define INT_MIN (-2147483647 - 1) // -2147483648#define INT_MAX 2147483647#define UINT_MAX 0xFFFFFFFFU // 42949672952. MinGW-w64Windows 上的 GCC#define SHRT_MIN (-32768)#define SHRT_MAX 32767#define USHRT_MAX 65535 // 或 0xFFFF#define INT_MIN (-2147483647 - 1)#define INT_MAX 2147483647#define UINT_MAX 4294967295U // 或 0xFFFFFFFFU3. GCCLinux / macOS, x86_64#define SHRT_MAX 32767#define USHRT_MAX 65535#define INT_MAX 2147483647#define UINT_MAX 4294967295U因此所有主流 x86_64 编译器均规定sizeof(short) 2sizeof(int) 4因此unsigned short 最大值恒为 65535unsigned int 恒为 4294967295差异在于long在MSVCLLP64中long是四个字节,而在MinGW/GCCLP64中long是八个字节这个差异在本文1.7已经详细讨论过。但是我们此处在讨论“unsigned 使最大值翻倍”此差异不影响 short 和 int因此忽略long即可但我们要知道差异。我们小结一下unsigned 确实让相同位宽的整型最大值翻倍从2n−1−12^{n-1} - 12n−1−1到2n−12^n - 12n−1。在 x86_64 平台上MSVC、MinGW、GCC 对 short 和 int 的定义完全一致。差异仅存在于 long 类型。所以我们可安全跨平台使用 unsigned short / unsigned int 获取更大的非负整数范围。2.2字符型变量字符类型char也可以被signed,unsigned修饰,也可以显式声明为signed char//范围在-128-127unsigned char//0-255要注意C语言规定char的符号性由实现定义由系统实现决定。因此char不等同于signed char他可能是signed char也可能是unsigned char。它在某些平台上等价于 signed char在另一些平台上则等价于 unsigned char。Microsoft Visual Studio (MSVC)#define SCHAR_MIN (-128) // signed char 最小值#define SCHAR_MAX 127 // signed char 最大值#define UCHAR_MAX 0xFF // unsigned char 最大值255MinGW-w64Windows 上的 GCC#define SCHAR_MIN (-128)#define SCHAR_MAX 127#define UCHAR_MAX 255 // 或 0xFFGCCLinux / macOS, x86_64#define SCHAR_MIN (-128)#define SCHAR_MAX 127#define UCHAR_MAX 255 // 或 0xFF这一点与 int 不同int 始终等同于signed int其符号性是明确且不可变的。这时候可能会有疑问为什么 char 也有正负之分char 本质是 1 字节8比特位的整数类型并非仅用于字符。因此它可以像其他整数一样分为signed char范围 -128 ~ 127unsigned char范围 0 ~ 255C 标准规定char 的默认符号性由编译器决定它可能是 signed也可能是 unsigned不等同于 signed char。因此处理字节或数值时应显式使用 signed char / unsigned char或更清晰的 int8_t / uint8_t。读者若还有困惑可以想象 char 是一个 8格的小盒子1字节 8位signed char有1格当“符号牌”最高位剩下7格存数字 → 能装 -128 ~ 127。unsigned char8格全用来存数 → 能装 0 ~ 255。同一个盒子用法不同。再简化来说我有存放的能力但我用不用是另一回事。希望可以让读者清晰理解。2.3 数据类型的取值范围C 提供 short、int、long、long long 等整型是因为它们取值范围不同可在不同场景选择最合适的类型兼顾内存和性能。要查看各类型的极限值请使用标准头文件中的常量整型范围定义在 limits.hSCHAR_MIN / SCHAR_MAXsigned charSHRT_MIN / SHRT_MAXshortINT_MIN / INT_MAXintLONG_MIN / LONG_MAXlongLLONG_MIN / LLONG_MAXlong longUCHAR_MAX、USHRT_MAX、UINT_MAX、ULONG_MAX、 ULLONG_MAX对应无符号类型的最大值为保证可移植性应使用这些常量而非硬编码数值。笔者在2.1和2.2广泛使用了limits.h里的数据。limits.h 文件中说明了整型类型的取值范围。float.h 文件中说明浮点型类型的取值范围。由于浮点数类型取值范围稍微复杂但原理类似因此不再本文进行讨论。这里已经讲清楚了符号的概念对于运算符sizeof的返回值其实C语言中值规定了它是无符号的整数没有指定具体的类型因此这个返回值的类型是系统自主决定的有可能是unsigned int,unsigned long,unsigned long long无法确切知晓其printf()中对应的占位符分别是%u,%lu,%llu。我们很难选择也不利于程序的可以移植性。因此C语言提供了一个类型别名size_t来统一表示sizeof的返回值类型让其对应当前系统的sizeof的返回值类型这个返回值的类型是什么我们不需要知道也不需要不关心交给编译器去做这是它的事情。我们只需要知道size_t和%zu,并且了解size_t是可以单独使用的就可以了。就像这样#include stdio.hint main(void){int a 10;printf(“%zu\n”, sizeof(a));printf(“%zu\n”, sizeof a);//a是变量的名字可以省略掉sizeof后边的()printf(“%zu\n”, sizeof(int));printf(“%zu\n”, sizeof(3 3.5));return 0;}3. 变量我们已经搞清楚了类型类型是用来创建变量的。C语言中把经常变化的值成为变量不变的量成为常量3.1 变量的创建变量命名的一般规则首字符必须是字母a–z、A–Z或下划线 _不能以数字开头。组成字符只能包含字母、数字0–9和下划线。区分大小写count 和 Count 是两个不同的变量。不能使用关键字如 int、if、for 等保留字不可作变量名。建议使用有意义的英文单词或缩写如 studentCount、max_value。避免单个字母除非在简单循环中如 i、j。保持风格一致推荐小写字母 下划线或驼峰命名法。int age;char name;double weight;创建变量的同时给予一个初始值叫做初始化int age 18;char name ‘X’;double weight 66.6;unsigned int height 180;3.2 变量的分类全局变量在大括号外定义的变量全局变量使用范围很广一般是整个项目都想使用的变量。局部变量在大括号内定义的变量就是局部变量局部变量的适用范围比较局限仅能在其所处的局部范围内使用。#include stdio.hint global 2026; //全局变量int main (void){int local 2000;//局部变量printf(“%d\n”,local);printf(“%d\n”,global);return 0;如果局部变量与全局名称相同则优先使用局部变量。这点与git的全局–global user.name 局部:user.name类似都是优先使用局部的。全局变量和局部变量在内存中存储的位置存在差异局部变量存放在内存的栈区全局变量存放在内存的静态区堆区则用来动态管理内存这里只是粗略划分就是简单提及。算术操作符在书写代码的时候难免会涉及计算因此C语言提供了一系列操作符用来方便运算其中有一组操作符叫做算术操作符 - * / %。这些操作符也叫做双目操作符因为会有两个操作数。有时候也会把算术操作符叫做算术运算符这只是翻译差异。4.1 和 -和-用来加减运算和-的两端就是他们的操作数#include stdio.hint main(void){int x 1 1;int y 2 - 1;printf(“%d\n”, x);printf(“%d\n”, y);return 0;}4.2 **用来乘法运算#include stdio.hint main(void){int num 5;printf(“%d\n”, num * num); // 输出 25return 0;4.3 //用来除法运算其两端如果是整数就会执行整数除法结果为整数#include stdio.hint main(void){float x 6 / 4;int y 6 / 4;printf(“%f\n”, x); // 输出 1.000000printf(“%d\n”, y); // 输出 1return 0;}上面代码中尽管变量 x 的类型是float 浮点数但是6 / 4 得到的结果是1.0 而不是1.5 。原因就在于 C 语言里面的整数除法是整除只返回整数部分会直接丢弃小数部分。如果想要得到浮点数的结果两个运算数里至少需要有一个浮点数。#include stdio.hint main(void){float x 6.0 / 4; // 或者写成 6 / 4.0printf(“%f\n”, x); // 输出 1.500000return 0;}保留6位小数是默认设置。要重视整数除法会丢弃小数部分#include stdio.hint main(void){int i 5;i ( i / 20) * 100;return 0;}经过运算,i会变成 0而不是25,因为i/20是整除会直接丢弃小数得到0。要想得到预期的结果把20改成20.0就可以了将整数除法编程浮点数除法。4.4 %%用来取模余运算会返回两个整数相除的余数值。取模运算%只可以用整数不可是浮点数。#include stdio.hint main(void){int x 6 % 4; // 2return 0;}对负数取模其结果的符号取决于第一个运算数的符号#include stdio.hint main(void){printf(“%d\n”, 11 % -5); // 1printf(“%d\n”,-11 % -5); // -1printf(“%d\n”,-11 % 5); // -1return 0;}5. 赋值操作符 在本文3.1里讲了创建变量的同时给予一个初始值叫做初始化。在在变量创建好后再给一个值就叫赋值。赋值操作符是一个可以随时给变量赋值的操作符5.1 连续赋值赋值操作符支持连续赋值int a 1; int b 2; int c 0; c b a3;//连续赋值虽然C语言支持这种格式但这种格式不便于理解因此还是建议拆开来写int a 1; int b 2; int c 0; b a3; c b;这样书写在调试的时候每次赋值的细节就很方便观察了。5.2 复合赋值符书写代码的过程中我们可能经常对一个数进行自增减操作int a 10; a a3; a a-2;更方便的写法int a 10; a 3; a - 2;复合赋值符可以方便程序员编写代码 -* ///下面这些以后学习 | ^单目操作符,–,,-有两个操作数的操作符是双目操作符单目操作符很显然就是只有一个操作数。,–,(正),-(负)就是单目操作符。6.1 和 –是自增的操作符分为前置 和后置 –是自减的操作符也分为前置 – 和后置 --。6.1.1 前置 int a 10;int b a;printf(“a%d b%d”,a,b);先1后使用6.1.2 后置 int a 10;int b a;printf(“a%d b%d”,a,b);先使用后1对于自增变量本身而言前后置没有区别但对于赋值的变量来讲就有先后之分了。6.1.2的代码也等价于int a 10;int b a;a a 1;printf(“a%d b%d\n”,a,b);6.1.3 前置–如果搞懂了前置那么理解前置–就毫无困难只是把1改成-1int a 10;int b --a;printf(“a%d b%d\n”,a,b);//输出9 9先-1后使用6.1.4 后置–int a 10;int b a–;printf(“a%d b%d\n”,a,b);//输出 9 10先使用后-16.2 和-要注意这里的和-是正负号是单目操作符运算符对于正负号没有影响完全可以忽略不写int a 10;等价于int a 10;运算符-则用来改变值的正负号负数前加入-得到正数正数前加入-得到负数int a 10;int b -a;int c -10;printf(“b%d c%d\n”,b,c);//b和c都是-10int a -10;int b -a;printf(“b%d\n”,b);b是107. 强制类型转换强制类型转换是特殊的操作符其语法很简单(这里写入类型)例如int a 1.11;//a是int类型1.11是double类型因此编译器会警告int a (int)1.11;//强制将double的类型转换为int类型这样强制转换只取整数部分强制转换类型要在特定情况或者尽量避免使用。否则会丢失数据scanf 和 printf 函数8.1 printf函数8.1.1 printf函数的基本用法printf()可以将参数文本输出到屏幕上其f表示format格式化定制输出文本的格式。#include stdio.hint main (void){printf(“Hello World!”);return 0;}运行上述代码会在屏幕输出Hello World!。printf()的换行符会在运行结束后停留在输出结束的地方不会自动换行需要使用\n来移动光标到下一行。#include stdio.hint main (void){printf(“Hello World!\n”);return 0;想要在文本内部换行也可以就像这样#include stdio.hint main(void){printf(“Hello\nWorld\n”);//等价printf(“Hello\n”);printf(“World\n”);return 0;}printf()是标准库函数定义在标准库的头函数stdio.h中因此使用此函数就必须在头源码文件头部引用这个头文件其中i是inputo是output。8.1.2 占位符printf()里面可以在输出文本里指定占位符占位符就是占位符的位置可以输入其他值代替占个位置。#include stdio.hint main(void){printf(“There are %d pens\n”,10)return 0}输出文本是There are %d pens\n,%d是占位符这个位置会被其他值代替。这个占位符的第一个字符一律是%第二个字符表示占位符的类型%d表示这个代替的值必须是一个整数。printf()函数的第二个参数就是替换占位符的值上文代码的例子就是整数10替换%d,代码执行的输出结果就是There are 10 pens%d是比较常用的占位符还有%s其表示带入的是一个字符串#include stdio.hint main(void){printf(“%s is coming now\n”, “Wang”);return 0;}这里的%s表示代入的是一个字符串因此printf()的第二个参数就必须是字符串。输出文本里也可以使用多个占位符。#include stdio.hint main(void){printf(“%s says it is %d o’clock\n”, “Li”, 10);return 0;}这里的输出文本%s says it is %d o’clock 有两个占位符第一个是字符串占位符%s 第二个是整数占位符%d 分别对应printf() 的第二个参数Li和第三个参数10。执行后的输出就是Li says it is 10 o’clock。printf()函数的参数与占位符是一一对应的关系有n个占位符printf()会有n1个参数如果参数个数少于对应的占位符printf()就会输出内存中的任意值。8.1.3 占位符列举%a十六进制浮点数字母输出为小写。%A十六进制浮点数字母输出为大写。%c字符。//char%d十进制整数有符号的10进制整数。// int%e使用科学计数法的浮点数指数部分的 e 为小写。%E使用科学计数法的浮点数指数部分的 E 为大写。%i整数基本等同于 %d 。%f小数包含 float 类型和 double 类型。//float: %f double : %lf%g6个有效数字的浮点数。整数部分一旦超过6位就会自动转为科学计数法指数部分的 e 为小写。%G等同于 %g 唯一的区别是指数部分的 E 为大写。%hd十进制 short int 类型。%ho八进制 short int 类型。%hx十六进制 short int 类型。%huunsigned short int 类型。%ld十进制 long int 类型。%lo八进制 long int 类型。%lx十六进制 long int 类型。%luunsigned long int 类型。%lld十进制 long long int 类型。%llo八进制 long long int 类型。%llx十六进制long long int类型。%lluunsigned long long int类型。%Le科学计数法表示的long double类型浮点数。%Lflong double类型浮点数。%n已输出的字符串数量。该占位符本身不输出只将值存储在指定变量之中。%o八进制整数。%p指针用来打印地址。%s字符串。%u无符号整数unsigned int。%x十六进制整数。%zusize_t类型。%%输出一个百分号。比较重要的已经加粗会查表即可用多了自然就熟悉了。8.1.4 输出格式printf()可以定制占位符的输出格式。8.1.4.1 限定格式printf()允许限定占位符的最小宽度:#include stdio.hint main(void){printf(“%5d\n”, 123); // 输出为 123return 0;}%5d表示这个占位符的宽度至少为5位。如果不满5位对应的值前面会添加空格。输出的值默认是右对齐因此输出内容前面会有空格如果希望改成左对齐在输出内容后面添加空格可以在占位符的% 的后面插入一个-号。#include stdio.hint main(void){printf(“%-5d\n”, 123); // 输出为 123 return 0;}对于小数而言上述的限定符会限制所有数字的最小显示宽度#include stdio.hint main(void){printf(“%12f\n”, 12.345);return 0;}%12f会限制输出的浮点数最少要维持12位的形式而由于小数的默认显示精度是小数点后6位因此输出的结果12.345000前会添加3个空格。8.1.4.2 输出显示正负号printf()在一般情况下不会在正数的前方显示仅对负数显示-如果想要正数也输出,就需要在占位符%后添加一个。#include stdio.hint main(void){printf(“%d\n”, 12); // 输出 12printf(“%d\n”, -12); // 输出 -12return 0;}这样就可以让输出的数值始终携带正负号了。8.1.4.3 限定小数位数输出小数的时候,有时候也想要限定小数点后的位数,就比如只想保留四位小数,占位符就可以写成%.4f#include stdio.hint main(void){printf(“The number is %.4f\n”, 0.5);return 0;//输出0.5000}这种写法可以和限定宽度占位符结合使用:#include stdio.hint main(void){printf(“The number is %8.4f\n”, 0.5);return 0;//输出 0.5000,前面会有两个空格}宽度和小数位数这两个限定值可以用*代替,转而通过printf()的参数传入:#include stdio.hint main(void){printf(“The number is %*.*f\n”,8,4,0.5);return 0;//输出 0.5000,前面会有两个空格}这样写法就便于改变参数了方便了不少。8.1.4.4 输出部分字符串输出字符串的占位符是:%s默认全部输出。若想要输出部分可以使用%.[m]s指定输出的长度这里的[m]是一个数字表示想要输出的长度这个数字是从前向后计算的因此这个部分就是开头的部分。#include stdio.hint main(void){printf(“%.5s\n”, “Hello world”);return 0;}输出结果就是Hello,前5位。8.2 scanf函数前面介绍了printf()函数用过输出结果而scanf()函数就是输入值给变量的:#include stdio.hint main(void){int a 1;printf(“请输入您的身高:”);scanf(“%d”,a);printf(“您的身高是:%d\n”,a);return 0;}8.2.1 scanf()的基本用法scanf() 函数用于读取键盘输入。当程序运行到这条语句时会暂停执行等待用户从键盘输入数据。当用户输入数据并按下回车键后scanf() 会处理这些输入并将它们存入指定的变量中。它的函数原型定义在 stdio.h 头文件内语法结构与 printf() 相似。int i 0;scanf(“%d”,i);scanf() 的第一个参数是格式字符串其中包含占位符用法和 printf() 的占位符大致相同。C语言中的数据都有特定的类型scanf() 必须提前知道输入的数据类型才能进行正确处理。scanf() 函数的其他参数是用来存放用户输入的变量格式字符串里有多少个占位符后面就需要对应多少个变量。对于上面的代码块scanf()的第一个参数 “%d” 提前规定了输入的应该是一个整数“%d” 就是占位符其中%是占位符的标志d 代表整数第二个参数 “i” 表示将用户输入的整数存入变量 i 中变量前方必须添加 符号指针变量除外来获取地址。因为 scanf() 传递的是内存地址程序需要通过变量 i 的地址将输入的值存入该地址对应的内存空间中。从键盘读取多个变量就像如此scanf(“%d%d%f%f”, a, b, c, d);格式字符串%d%d%f%f表示用户输入的前两个是整数后两个是浮点数比如10 -10 3.14 -1.0e4 。这四个值依次放入a、b、 c 、d 四个变量。scanf() 处理数值占位符时会自动过滤空白字符包括空格、制表符、换行符等等。因此用户输入的数据之间有一个或多个空格不影响scanf() 解读数据。即使用户使用回车键将输入分成几行同样不影响解读。10-103.141.0e4即使分成四行输入得到的结果与一行输入是完全一样的。每次按下回车键后scanf() 就会开始读取。如果第一行的内容匹配了第一个占位符那么下次按下回车键时就会按顺序从第二个占位符开始读取。scanf() 处理用户输入的原理是先将用户的输入存入输入缓冲区等到按下回车键后再按照占位符的要求对缓冲区内的内容进行读取。读取数据时会从上一次读取遗留的第一个字符开始直到读完缓冲区或者遇到第一个不符合条件的字符为止。#include stdio.hint main(void){int x;float y;// 输入 -13.45e12# 0scanf(“%d”, x);printf(“%d\n”, x);scanf(“%f”, y);printf(“%f\n”, y);return 0;}在上述示例中scanf() 读取用户输入时%d 占位符会忽略开头的空格从 - 开始读取到 . 处停止因为 . 不属于整数的有效字符因此读取到的值为 -13。第二次调用 scanf() 时会从上一次停止的位置继续读取。此时首字符是.由于对应的占位符是 %f程序会读取到 .45e12符合科学计数法的浮点数格式遇到 # 时停止# 不属于浮点数的有效字符。由于 scanf() 支持连续处理多个占位符上述示例也可以简写为以下形式#include stdio.hint main(void){int x;float y;// 输入 -13.45e12# 0scanf(“%d%f”, x, y);return 0;}相信你应该可以掌握了scanf()函数的基本用法了。8.2.2 scanf()函数的返回值scanf()函数的返回值是一个整数表示成功读取到的变量的个数。如果没有读取任何项或者匹配失败则返回0。如果在成功读取任何数据之前发生了读取错误或者遇到读取到文件结尾则返回常量 EOF (-1)。EOF - end of file 文件结束标志#include stdio.hint main(void){int a 0;int b 0;float f 0.0f;int r scanf(“%d %d %f”, a, b, f);printf(“a%d b%d f%f\n”, a, b, f);printf(“r %d\n”, r);return 0;}输入输出测试1 2 3.14a1 b2 f3.140000r3如果在输入2个数后按ctrlz提前介入输入的话1 2^Z^Z^Za1 b2 f0.000000r2在VS环境中按3次ctrlz 才结束了输入我们可以看到r是2表示正确读取了2个数值。如果一个数字都不输入直接按3次ctrlz ,输出的r是-1也就是EOF^Z^Z^Za0 b0 f0.000000r-18.2.3 占位符scanf() 常用的占位符如下与 printf() 的占位符基本一致。%c字符。%d整数。%ffloat 类型浮点数。%lfdouble 类型浮点数。%Lflong double 类型浮点数。%s字符串。%[]在方括号中指定一组匹配的字符比如 %[0-9]遇到不在集合之中的字符匹配将会停止。在 scanf() 的所有占位符中除 %c 外其余都会自动忽略开头的空白字符。%c 不忽略空白字符总是读取当前的第一个字符无论它是否为空格。若要强制跳过空白字符再读取可写成 scanf( %c, ch)即在 %c 前加一个空格表示跳过零个或多个空白字符。占位符 %s 的规则是从第一个非空白字符开始读取直到遇到空白字符空格、换行符、制表符等为止。由于 %s 不包含空白字符无法读取多个单词除非连续使用多个 %s。这意味着 scanf() 不适合读取含空格的字符串如书名。此外scanf() 会在字符串变量末尾自动存储空字符 \0。scanf() 读取字符串到数组时不会检测长度是否越界可能导致溢出。为防止这种情况应指定最大读取长度写成 %[m]s其中 [m] 为整数表示最大长度超出部分将被丢弃。#include stdio.hint main(){char name[11];scanf(“%10s”, name);return 0;}上例中name 是长度为 11 的字符数组%10s 表示最多读取 10 个字符剩余字符被丢弃从而避免数组溢出。由于上述安全性问题如缓冲区溢出风险在 Visual Studio (VS) 开发环境中编译时编译器会标记 scanf() 函数为“不安全”并推荐使用微软特有的安全函数如 scanf_s。然而scanf_s 并非标准 C 语言的一部分使用它会导致代码无法在其他编译器如 GCC、Clang或操作系统如 Linux、macOS上编译这对跨平台开发是非常不利的。为了在 VS 中继续使用标准的 scanf() 函数并保持代码的跨平台兼容性通常的做法是在代码的最开头定义一个宏以关闭 VS 的这种安全警告。在源文件的第一行添加以下代码#define _CRT_SECURE_NO_WARNINGS 1