C 数据类型

C 是强类型语言,每个变量都必须先声明类型才能用。C 的数据类型主要分四大类:基本类型(整数、浮点、字符)、枚举类型(enum)、void 类型(无值)、派生类型(指针、数组、结构体、联合)。本篇聚焦最常用的基本类型——这是写任何 C 程序的基石。

一、整数类型

整数是 C 里用得最多的类型。C 提供了不同大小的整数,以适配不同的存储需求:

#include <stdio.h>
#include <limits.h>      // 各类型取值范围

int main(void) {
    // 整数类型(从小到大)
    short s = 100;             // 短整型,通常 2 字节
    int i = 1000;              // 整型,通常 4 字节(最常用)
    long l = 100000L;          // 长整型,32/64 位(末尾 L)
    long long ll = 1000000LL;  // 更长整型(C99),至少 8 字节

    // signed / unsigned
    unsigned int u = 4000000000U;  // 无符号,最高位不当符号位
    // 0 ~ 4294967295(对 32 位 int)

    printf("int 范围: %d ~ %d\n", INT_MIN, INT_MAX);
    printf("unsigned int 最大: %u\n", UINT_MAX);
    printf("long 最大: %ld\n", LONG_MAX);
    return 0;
}

各整数类型占的字节数(典型值,Linux/macOS 64 位系统):

signed / unsigned:每个整数类型都有有符号(signed,默认)和无符号(unsigned)两个版本。unsigned 把符号位也用来存数据,所以能存两倍的正数,但不能存负数。

二、浮点类型

浮点数用来表示带小数的数,采用 IEEE 754 标准存储:

#include <stdio.h>
#include <float.h>       // 浮点数取值范围/精度

int main(void) {
    // 浮点类型
    float f = 3.14f;          // 单精度,通常 4 字节(末尾 f)
    double d = 3.141592653589;  // 双精度,通常 8 字节(默认)
    long double ld = 3.14159265358979323846L;  // 扩展精度(末尾 L)

    // 注意:浮点数有精度损失!
    printf("%.20f\n", 0.1 + 0.2);  // 0.30000000000000004484...
    // 浮点数不要直接用 == 比较!

    printf("float 精度位数: %d\n", FLT_DIG);     // 通常 6
    printf("double 精度位数: %d\n", DBL_DIG);    // 通常 15
    printf("float 最大: %e\n", FLT_MAX);
    return 0;
}

重要警告:浮点数有精度损失(0.1 + 0.2 不等于 0.3!),所以永远不要用 == 直接比较两个浮点数,应该判断差的绝对值是否小于某个阈值(如 1e-9)。

三、字符类型 char

C 里没有真正的"字符"类型——char 本质上就是一个字节的小整数。它存的字符其实是 ASCII 码值:

#include <stdio.h>
#include <ctype.h>       // 字符处理函数

int main(void) {
    char grade = 'A';          // 单字符用单引号
    char newline = '\n';        // 转义字符也是 char
    char num = '7';

    // char 本质是小整数(0~127 是 ASCII)
    printf("%c 的 ASCII 码是 %d\n", grade, grade);  // A 的 ASCII 是 65
    printf("%c 的 ASCII 码是 %d\n", '0', '0');      // 0 的 ASCII 是 48

    // 字符运算
    char lower = grade + 32;   // 大写转小写(A=65, a=97)
    printf("小写: %c\n", lower);  // a

    // ctype.h 提供的字符判断函数
    printf("isalpha('A') = %d\n", isalpha('A'));    // 非0(真)
    printf("isdigit('7') = %d\n", isdigit('7'));    // 非0(真)
    printf("toupper('a') = %c\n", toupper('a'));    // A
    return 0;
}

几个要点:

四、sizeof 运算符

sizeof 是 C 里一个特殊的运算符(不是函数),它返回某类型或变量占的字节数。它在编译期就计算好,不占运行时间。

#include <stdio.h>
#include <limits.h>

int main(void) {
    // sizeof 返回某类型/变量占的字节数
    printf("char:   %zu\n", sizeof(char));       // 1(恒为 1)
    printf("short:  %zu\n", sizeof(short));      // 通常 2
    printf("int:    %zu\n", sizeof(int));        // 通常 4
    printf("long:   %zu\n", sizeof(long));       // Linux64=8, Win64=4
    printf("float:  %zu\n", sizeof(float));      // 通常 4
    printf("double: %zu\n", sizeof(double));     // 通常 8
    printf("指针:    %zu\n", sizeof(int*));      // 32位系统=4, 64位=8

    // sizeof 是运算符,不是函数
    // 编译期就计算好了,不占运行时间
    int arr[10];
    printf("数组大小: %zu\n", sizeof(arr));              // 40
    printf("元素个数: %zu\n", sizeof(arr)/sizeof(arr[0])); // 10

    // %zu 是 size_t 的占位符(C99)
    return 0;
}

用 sizeof 求数组元素个数是 C 里的经典技巧:sizeof(arr) / sizeof(arr[0])。注意 sizeof 返回的类型是 size_t(无符号),打印用 %zu(C99)。

五、类型转换

不同类型的值一起运算时,C 会自动做类型转换。转换分两种:

#include <stdio.h>

int main(void) {
    // 1. 隐式转换(自动):小类型 → 大类型,安全
    int i = 100;
    double d = i;            // int → double,自动
    printf("%f\n", d);        // 100.000000

    // 2. 隐式转换(自动):大类型 → 小类型,可能丢精度!
    double pi = 3.14159;
    int truncated = pi;      // double → int,直接截断小数
    printf("%d\n", truncated);  // 3

    // 3. 强制转换(显式):(目标类型)值
    int a = 10, b = 3;
    double result = (double)a / b;   // 先把 a 转成 double,再除
    printf("%f\n", result);          // 3.333333

    // 不转换的对照
    printf("%d\n", a / b);           // 3(整数除法,丢小数)

    // 4. 数据丢失示例
    long big = 3000000000L;
    int small = (int)big;     // 溢出,得到的是错的值
    printf("big=%ld, small=%d\n", big, small);
    return 0;
}

隐式转换规则(向上提升):charintlonglong longfloatdoublelong double。小类型会自动提升成大类型参与运算,不会丢精度;反过来(大变小)则可能丢数据。

强制转换语法:(目标类型)值。注意 C 的强制转换不像 C++ 那样有 static_cast 等多种,它就一种语法,所以要格外小心——编译器不会再帮你检查。

六、转义字符

有些字符无法直接写在源码里(如换行、引号),需要用转义字符(反斜杠开头)表示:

// C 常用转义字符
\n    换行          \t    制表符
\r    回车          \\    反斜杠 \
\'    单引号        \"    双引号
\0    空字符(NULL) \a    警报(响铃)
\b    退格          \f    换页
\x41  十六进制表示字符 'A'
\101  八进制表示字符 'A'

最常用的包括换行符(用于打印换行)、制表符(对齐输出)、字面反斜杠本身,以及字符串结束符 NUL(后续字符串篇会重点讲)。具体写法见下面的代码示例。

七、limits.h 与 float.h

不同平台上类型的取值范围可能不同,标准库 <limits.h><float.h> 提供了这些常量:

写跨平台代码时,不要假设 int 一定是 4 字节,用 sizeof 或这些宏来判断更稳妥。

八、stdint.h(C99 固定宽度类型)

因为 int/long 在不同平台大小不一致,C99 引入了固定宽度整数类型(<stdint.h>):

写需要精确控制大小的代码(网络协议、二进制文件)时,优先用这些类型,避免平台差异。

小结

记住:int 是最常用的整数(4 字节)、double 是默认浮点(8 字节)、char 是字符也是小整数。sizeof 在编译期算字节数。整数和浮点互相转换有规则,但浮点别用 == 比较。下一篇我们看 C 的运算符——如何操作这些数据。

← 上一篇 C 变量

下一篇 C 运算符

✈️💬