C Primer Plus——第三章 数据和C
本章介绍C语言中数据的表示方式,包括变量与常量、基本数据类型、存储方式及类型相关的操作。
一、示例程序
先看一个使用不同数据类型的简单程序:
#include <stdio.h> int main(void) { int num = 100; // 整型 float pi = 3.14159f; // 单精度浮点型 char ch = 'A'; // 字符型 double large = 1.23e12; // 双精度浮点型 printf("整数: %d\n", num); printf("浮点数: %.5f\n", pi); printf("字符: %c (ASCII %d)\n", ch, ch); printf("科学计数: %e\n", large); return 0; }通过这个程序,可以直观看到几种基本数据类型的声明和使用方式。
二、变量与常量数据
- 变量:程序中用来存储数据的命名存储区域,其值在运行过程中可以改变。
- 必须先声明后使用,声明时确定类型和名称,可选初始化。
int count = 0;
- 常量数据:程序运行期间值不能改变的量。
- 字面常量:直接写出的数值,如
100、3.14、'A' - 符号常量:使用
#define或const定义,如#define PI 3.14159或const double PI = 3.14159;
- 字面常量:直接写出的数值,如
三、数据:数据类型关键字概览
C语言通过数据类型关键字来定义变量的类型,主要分为以下几类:
- 基本类型关键字:
int、long、short、unsigned、signed、char、float、double、_Bool - 复数与虚数类型(很少直接使用):
_Complex、_Imaginary - 类型修饰与扩展:
signed、unsigned、short、long可以和int、char组合产生变体。
这些关键字决定了数据在内存中的存储方式和占用空间。
3.1 计算机存储方式:整数与浮点数
- 位(bit):最小的存储单元,可以存储0或1。
- 字节(Byte):最常用的计算机存储单位,1字节 = 8位。
- 字(Word):计算机设计的自然存储单位,通常为16位、32位或64位,取决于体系结构。
3.2 整数存储
- 整数以二进制补码形式存储,能精确表示其范围内的每一个值。
3.3 浮点数存储
- 浮点数被分成符号位、指数部分和尾数部分来存储(遵循IEEE 754标准)。
- 浮点数在表示很大或很小的数时非常有优势,但不能精确表示所有实数,算术运算时可能产生舍入误差。
四、C语言基本数据类型
4.1int类型
- 有符号整数,通常占用一个机器字长(现今多为32位或64位)。
- 声明示例:
int age;或int year = 2026; - 打印:用
%d输出十进制整数。 - 八进制和十六进制输出:
%o:八进制,不带前缀。%x或%X:十六进制,不带前缀。- 若想显示前缀,使用
%#o(显示0)、%#x(显示0x)或%#X(显示0X)。
int num = 255; printf("十进制: %d\n", num); // 255 printf("八进制: %#o\n", num); // 0377 printf("十六进制: %#x\n", num); // 0xff4.2 其他整数类型
C语言提供多种整数变体以适应不同范围需求:
| 类型 | 含义 | 常见大小(位) | 打印格式(十进制) |
|---|---|---|---|
short int或short | 短整型 | 16 | %hd |
long int或long | 长整型 | 32/64 | %ld |
long long int | 长长整型 | 64 | %lld |
unsigned int | 无符号整型 | 与int相同 | %u |
unsigned short | 无符号短整型 | 16 | %hu |
unsigned long | 无符号长整型 | 32/64 | %lu |
unsigned long long | 无符号长长整型 | 64 | %llu |
使用注意事项:
- C标准只规定了各类型最小允许范围,具体大小由实现决定。可借助
sizeof获取字节数。 - 在
long和int占用空间相同的平台上,若希望代码可移植,应使用long来存储可能超过int范围的数。 - 长整数常量后缀:
L或l(如100L表示long类型);LL表示long long。也可用于八进制或十六进制常量。 - 无符号常量后缀:
U或u,可与L组合,如100UL表示unsigned long。 - 溢出问题:有符号整数溢出属于未定义行为;无符号整数溢出会回绕(取模)。
- 打印八进制或十六进制长整型:
%lo、%lx;short类型八进制用%ho。
long long big = 1234567890123LL; printf("%lld\n", big); unsigned long ul = 4000000000UL; printf("%lu\n", ul);4.3char类型
- 用于存储字符,但实际上存储的是字符对应的数值(ASCII码或扩展编码)。占用1字节(8位)。
- 字符常量:用单引号括起来,如
'A'、'9'、'$'。 - 字符是以数值形式存储的,
'A'在ASCII中对应整数65。C将字符常量视为int类型。 - 打印字符:用
%c输出字符本身,也可用%d输出其对应的整数编码。
char c = 'A'; printf("字符: %c, 编码: %d\n", c, c); // 输出: 字符: A, 编码: 65- 有符号/无符号:
char默认可能是有符号或无符号,由编译器决定。显式声明可用signed char(通常范围-128~127)和unsigned char(0~255)。 - 非打印字符:一些控制字符无法直接输入,使用转义序列表示。常用转义序列如下:
| 转义序列 | 含义 | ASCII值(十进制) |
|---|---|---|
\a | 警报(响铃) | 7 |
\b | 退格 | 8 |
\f | 换页 | 12 |
\n | 换行 | 10 |
\r | 回车 | 13 |
\t | 水平制表 | 9 |
\v | 垂直制表 | 11 |
\\ | 反斜杠 | 92 |
\' | 单引号 | 39 |
\" | 双引号 | 34 |
\0oo | 八进制值(oo为1~3位八进制数字) | — |
\xhh | 十六进制值(hh为1~2位十六进制数字) | — |
示例:
printf("第一行\n第二行\t缩进后\a警告声"); char single_quote = '\''; char backslash = '\\'; printf("%c %c", single_quote, backslash);- 关于
\a:“活跃位置”指显示设备上下一个字符将出现的位置;\a触发警报声,但不改变活跃位置(即不移动光标)。 - 字符串:用双引号括起来的字符序列,无需单引号。例如
"Hello, World!"。
4.4_Bool类型
- C99引入的布尔类型,只能存储值
0(假)或1(真)。若包含<stdbool.h>头文件,可直接使用bool、true、false。
_Bool flag = 1; #include <stdbool.h> bool done = false;- 任何非零值赋值给
_Bool变量都会被转换为1。
4.5 可移植类型(stdint.h 和 inttypes.h)
- 为了解决各平台整数大小不一致问题,C99引入了精确宽度整数类型,定义在
<stdint.h>中。它们是可选的(如果平台不支持则不会定义)。 - 常见类型:
- 精确宽度:
int32_t、uint16_t等。 - 最小宽度:
int_least8_t(至少8位)、int_least32_t等。 - 快速宽度:
int_fast16_t等,追求运算速度。 - 最大宽度:
intmax_t、uintmax_t。
- 精确宽度:
- 打印这些类型时,使用
<inttypes.h>中定义的字符串宏。例如PRId32是打印int32_t的格式说明符。
#include <inttypes.h> int32_t val = 1000; printf("%" PRId32 "\n", val); // 输出 10004.6 浮点型:float、double、long double
float:单精度,通常4字节,有效数字约6-7位。double:双精度,通常8字节,有效数字约15-16位。long double:扩展精度,依赖实现。- 浮点型常量:默认以
double类型存储。若希望指定为float,添加后缀f或F;long double加l或L。 - 科学计数法:
-1.56E+12表示 -1.56×10¹²,87e-3表示 0.087。 - 十六进制浮点常量(C99):
0xa.1fp10。a.1f(十六进制)对应十进制 10 + 1/16 + 15/256,乘以 2¹⁰ 即1024。
float f = 3.14f; double d = 2.718281828; long double ld = 1.23456789L; printf("%.10f %.10f %.10Lf\n", f, d, ld);4.7 复数与虚数类型
- C99引入了复数算术的支持(若编译器实现),包含类型
float _Complex、double _Complex、long double _Complex,以及可选的关键字_Imaginary。 - 在包含
<complex.h>后可用complex宏及I表示虚数单位。 - 实际编程中较少直接使用,多用于数值计算领域。
4.8 其他类型
void:无类型,主要用于函数返回、函数参数、通用指针。enum:枚举类型,定义命名整数常量。- 派生类型:数组、指针、结构体、联合体等。
4.9 类型大小:sizeof运算符
sizeof以字节为单位给出指定类型或变量的大小。- 语法:
sizeof(类型)或sizeof 变量,返回size_t类型(通常用%zu打印)。
printf("int: %zu 字节\n", sizeof(int)); printf("double: %zu 字节\n", sizeof(double)); int a; printf("a: %zu 字节\n", sizeof a);五、使用数据类型
类型转换与赋值
- 把浮点数赋给整数变量时,直接丢弃小数部分(不进行四舍五入),只保留整数部分。
int i = 3.99; // i 的值为 3- 不同类型数值在运算时会进行隐式类型转换,通常向更高级别提升(如
int→double)。 - 强制类型转换:
(类型)表达式,如(int)3.99结果为3。
六、参数匹配与陷阱
使用printf和scanf时,参数必须与格式字符串中的占位符严格匹配,否则会产生未定义行为:
- 类型不符(如用
%d打印long),可能导致输出错误或崩溃。 - 参数数量与占位符不一致也会出现问题。
- 编译器可能不会检查这些错误,需程序员格外小心。
七、转义序列示例与缓冲区刷新
7.1 程序运行情况
下面的程序展示了部分转义序列的效果:
#include <stdio.h> int main(void) { printf("AB\bC\n"); // 输出 A C? (退格将B擦除,可能显示为 AC) printf("12345678\rABCD\n");// 输出 ABCD5678? (回车回到行首,覆盖1234) printf("Alert sound\a\n"); // 发出警告声 return 0; }实际输出可能因终端而异。
7.2 刷新输出
printf默认将输出发送到缓冲区,然后根据一定条件将缓冲区内内容刷新(flush)到屏幕。- 缓冲区的刷新时机:
- 缓冲区满。
- 遇到换行符
\n(行缓冲模式下)。 - 程序正常结束。
- 需要输入时(如
scanf之前,输出会被刷新)。 - 显式调用
fflush(stdout)。
为了确保信息立即显示,可在必要处使用换行符或fflush。
八、关键概念
ASCII码(American Standard Code for Information Interchange):用数值表示字符的标准编码方案。标准ASCII使用7位编码,可表示128个字符(0~127)。例如'0'为48,'A'为65,'a'为97。C语言char类型通常基于ASCII或扩展编码(如Latin-1、UTF-8起始段)处理字符。
理解字符与数字的对应关系是掌握C语言输入输出和字符串处理的基础。
总结
- 数据以变量或常量的形式存储,变量需声明类型。
- 常用基本类型:
int、char、float、double,以及它们的修饰变体。 - 字符通过ASCII码存储,转义序列表示特殊字符。
sizeof用于确定类型大小;类型转换需注意精度损失。- 格式化输出时注意占位符与参数匹配,了解缓冲区刷新机制有助于控制输出时序。