5. 【C语言】数据类型全家桶

📅 2026/7/5 14:57:52
5. 【C语言】数据类型全家桶
上一篇文章里我们学会了给数据起名字——变量名并且知道声明变量时要写类型。你有没有好奇过为什么 C 语言要提供int、float、double、char这么多种类型只用一个“万能数字”类型不行吗答案是不行。因为计算机的内存是有限的CPU 处理不同数据的效率也不一样。类型系统让你能在“省空间”和“高精度”之间自由权衡——它也是 C 语言高效、贴近硬件的核心原因之一。今天我们就来逐一拜访 C 语言的数据类型家族看看每个“抽屉”有多大能装什么该怎么选。到最后你会发现了解类型就是了解计算机如何存储和运算数据。一、整型家族存整数的各种型号整型顾名思义就是用来存放整数的类型。但整数也有大小之分——你不可能用同一个箱子去装“5”和“20 亿”C 语言为此准备了一整套大小不同的整型。1. 基本整型类型典型占用字节64位系统大致取值范围short2 字节-32,768 ~ 32,767int4 字节-2,147,483,648 ~ 2,147,483,647long4 或 8 字节Windows 64位是4Linux 64位是8同 int 或更大long long8 字节-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807其中short全称是short int简写shortlong是long intlong long是long long int。最常用的就是int。声明整型变量intstudent_count35;shorttemperature-10;longlongworld_population7800000000L;// L 后缀表示 long long 常量需要注意C 标准只规定了short≤int≤long≤long long的最小范围具体大小由编译器和平台决定。要想写出可移植的程序得用sizeof获取实际大小后面会讲或者使用固定宽度整型如int32_t在stdint.h里高阶阶段再说。2. 无符号整型把负数空间让给正数有时候一个整数根本没有负值比如年龄、编号、计数这时如果还保留负数范围就白白浪费了一半的容量。C 语言允许你在整型前面加上unsigned关键字把整个取值范围平移到非负数。类型典型字节取值范围unsigned short20 ~ 65,535unsigned int40 ~ 4,294,967,295unsigned long4/80 ~ 4,294,967,295 或更大unsigned long long80 ~ 18,446,744,073,709,551,615声明示例unsignedintage25;// 年龄不为负unsignedshortport65535;// 网络端口号注意unsigned和signed之间的赋值、比较要特别小心混合使用时极易产生“隐形 bug”。比如unsignedinta10;intb-5;if(ab)printf(a b\n);// 你可能觉得正确但实际 a b 为假为什么因为b会被隐式转换成unsigned int-5变成一个巨大的正数比如 4294967291自然大于 10。这种陷阱非常常见后面讲到类型转换时会专门再谈。3. 如何确定类型的大小引入sizeofsizeof是 C 语言的一个运算符用于返回某个类型或变量占用的字节数。它是一个编译时操作不会在运行时消耗额外时间。用法printf(int 占用 %zu 字节\n,sizeof(int));printf(long long 占用 %zu 字节\n,sizeof(longlong));intx;printf(变量 x 占用 %zu 字节\n,sizeofx);// 变量可以不加括号printf(表达式 3.14 的类型占用 %zu 字节\n,sizeof3.14);%zu是专门用来输出sizeof返回值类型为size_t的格式化符。我们用一个小程序来看看当前平台上各整型的大小#includestdio.hintmain(void){printf(short: %zu 字节\n,sizeof(short));printf(int: %zu 字节\n,sizeof(int));printf(long: %zu 字节\n,sizeof(long));printf(long long: %zu 字节\n,sizeof(longlong));printf(unsigned int: %zu 字节\n,sizeof(unsignedint));return0;}在 64 位 Windows/MinGW 和 64 位 Linux 上运行long的结果可能不一样亲自试试看。二、浮点型带小数点的世界处理小数比如计算圆周率、价格、温度就需要浮点型。C 语言提供了三种精度类型典型字节有效数字大致范围float4约 6-7 位十进制有效数字±3.4E38double8约 15-16 位有效数字±1.7E308long double10/12/16因平台而异至少和 double 一样更大直接看例子floatpi3.14159f;// 常量尾部加 f 表示 float 类型doublee2.718281828;// 不加 f 的浮点常量默认是 doublelongdoublehuge1.23e400L;// L 后缀表示 long double精度陷阱浮点数在计算机内是以二进制科学计数法存储的很多十进制小数无法精确表示就像十进制的 1/3 是 0.33333…。最常见的结果是0.1 0.2 ! 0.3。所以判断两个浮点数相等通常不直接用而是看它们的差是否在一个很小的误差范围内。三、字符型char不只是一个字母char用来存放一个字符但它在物理上是一个 1 字节8 位的整数。字符和整数在 C 语言里可以无缝转换。charchA;// ch 里面存的是 65A 的 ASCII 码printf(%c\n,ch);// 输出 Aprintf(%d\n,ch);// 输出 65常见 ASCII 码速记A是 65a是 970是 48。char也可以有无符号之分signed char范围 -128 ~ 127和unsigned char0 ~ 255。但单独的char是有符号还是无符号由编译器决定x86 上通常是有符号的。所以如果你要把char当纯数字用最好明确写出signed char或unsigned char。字符常量必须用单引号括起来比如A、6、\n。\n这种是转义字符我们在第三篇里见过几个这里再补充几个常用的\0空字符字符串结束标志ASCII 码 0\t制表符\\反斜杠自身\单引号四、查看类型的极值limits.h和float.h想知道你当前平台上int最大能存多少不用靠背表C 标准库已经给你准备好了。#includestdio.h#includelimits.h#includefloat.hintmain(void){printf(int 最小值: %d, 最大值: %d\n,INT_MIN,INT_MAX);printf(unsigned int 最大值: %u\n,UINT_MAX);printf(long long 最大值: %lld\n,LLONG_MAX);printf(float 有效数字位数: %d\n,FLT_DIG);printf(double 有效数字位数: %d\n,DBL_DIG);return0;}运行一下你就能看到自己平台上的真实边界值。当你需要控制数据不越界时这些宏非常有用。五、类型选择的经验法则什么时候用什么类型以下是一些简单建议一般整数就用int默认选择。需要保证非负的整数长度、索引、数量用unsigned int但要小心混合运算。节省大数组内存如果数值在 -128~127 内用signed char或char在 0~65535 内用unsigned short。需要极大的整数long long。普通小数double别用float因为精度较低且现代计算机速度差异不大。单个字符char但注意符号问题。这种选择不是绝对的随着你写的程序越来越多你会越来越有感觉。六、常见错误与陷阱1. 整型溢出OverflowunsignedintaUINT_MAX;// 4294967295aa1;// 变成 0环绕了printf(%u\n,a);// 输出 0无符号溢出会安静地“环绕”到 0有符号溢出是未定义行为可能崩溃可能得到奇怪的值这是 C 里最危险的坑之一。2. 浮点数精度丢失floatf0.1f;if(f*101.0){// 条件可能不成立printf(相等\n);}解决办法是用fabs(f*10 - 1.0) 1e-6之类的比较。3. 错误地将char用于算术运算chara100;charb50;charcab;// 150 可能超出 char 范围如果 char 是 signed150 就溢出了printf(%d\n,c);// 输出可能是 -106最好将char提升为int后再进行算术。4. 格式化符与类型不匹配intx10;printf(%lld\n,x);// 用 %lld 打 int未定义行为floaty3.14f;printf(%d\n,y);// 用 %d 打 float同样错误使用正确的格式化符后面有专门一篇详讲现阶段记住%d/%iint%uunsigned int%ldlong int%lldlong long int%ffloat/double%lf也是 double 但 printf 里%f就够了%cchar%zusize_tsizeof 返回值七、小结今天你认识了 C 语言的整个数据类型家族整型的大小差异与有无符号浮点型的精度取舍以及字符型的整型本质。你还学会了用sizeof探查平台上的真实大小用limits.h获取边界值。这些类型就是你与世界交流的词汇量。选对类型不仅能让你的程序更省内存还能避免大量诡异的 bug。接下来我们要把这些数据“说”给用户听也要“听”用户说了什么——下一篇格式化输入输出printf和scanf我们终于能真正和程序对话了。课后小练习写一个程序打印你平台上所有整型包括 unsigned的占用字节数和最大值用limits.h里的宏。看看long是 4 还是 8 字节。声明一个unsigned short变量并赋予最大值 65535然后加 1打印结果观察溢出行为。解释为什么float变量赋值0.1然后用printf(%.10f, f)输出看到的不是精确的 0.1小挑战写一个程序交换两个int变量的值但不要使用第三个变量。提示可以用加减法或异或运算。我们下期见获取本系列示例代码请访问 GitCode 仓库。