3. 基本数据类型:整型、浮点型、字符型、void 类型、常量与变量

大家好,我是你们的老朋友。今天我们来聊聊 C 语言的“地基”——基本数据类型。说实话,数据类型这东西,看着简单,但我在面试新人时,十个人里有八个会在隐式类型转换上栽跟头。咱们今天就把这块硬骨头啃透。

核心要点:数据类型决定了变量在内存中占多大空间、能存什么范围的值、以及能做什么运算。选错类型,轻则精度丢失,重则程序崩溃。

3.1 整型(int)—— 最常用的“计数器”

整型就是整数。C 语言里的整型家族其实挺庞大的,但日常开发中,int 是绝对的主力。我个人习惯,只要不是明确需要超大或超小范围,一律用 int

类型 关键字 典型大小(32位系统) 取值范围
基本整型 int 4 字节 -2,147,483,648 ~ 2,147,483,647
短整型 short 2 字节 -32,768 ~ 32,767
长整型 long 4 或 8 字节 取决于平台
无符号整型 unsigned int 4 字节 0 ~ 4,294,967,295

我的小技巧:在嵌入式项目中,我经常用 unsigned int 来表示数组下标或计数器。为什么?因为下标不可能为负,用无符号类型还能多出一倍的正数范围,何乐而不为?

// 整型声明与初始化
int count = 0;
short small_val = 100;
long big_val = 100000L;      // 注意后缀 L
unsigned int positive = 3000;

// 我踩过的坑:用 int 存时间戳,结果溢出导致程序跑飞
// 正确做法:用 long 或 long long
long timestamp = 1699000000L;

3.2 浮点型(float/double)—— 处理“小数”的利器

浮点型用来表示带小数点的数。说白了,计算机里没有真正的“实数”,只有近似值。你想想看,1/3 在十进制里是无限循环小数,在二进制里也一样。

类型 关键字 大小 有效位数 典型应用
单精度 float 4 字节 约 6-7 位 图形学、传感器数据
双精度 double 8 字节 约 15-16 位 科学计算、金融

警告:千万不要用 floatdouble 直接比较是否相等!我曾经在金融项目中用 if (a == b) 判断两个浮点数,结果因为精度问题,明明数学上相等的两个数,程序就是不进分支。正确的做法是判断它们的差值是否小于一个很小的阈值(比如 1e-6)。

// 浮点型示例
float pi = 3.14159f;       // 注意 f 后缀
double precise_pi = 3.141592653589793;

// 错误的比较方式
// if (pi == 3.14159) { ... }  // 可能永远不成立

// 正确的比较方式
#include <math.h>
if (fabs(pi - 3.14159) < 1e-6) {
    printf("近似相等\n");
}

3.3 字符型(char)—— 不只是“字符”

很多人以为 char 就是用来存字母的。其实在 C 语言里,char 本质上就是一个 1 字节的整数。它既可以存 'A',也可以存 65——因为 'A' 的 ASCII 码就是 65。

关键认知:char 是整型家族的一员,只是它通常被用来表示字符。你可以对 char 做加减乘除,就像对 int 一样。

// 字符型的使用
char grade = 'A';
char ascii_val = 65;       // 等价于 'A'

// 字符运算:小写转大写
char lower = 'b';
char upper = lower - 32;   // 结果是 'B'
printf("%c\n", upper);     // 输出 B

// 注意:char 默认是有符号还是无符号?取决于编译器!
// 我在跨平台开发时,明确使用 signed char 或 unsigned char
unsigned char byte = 200;  // 安全,范围 0~255

3.4 void 类型—— 特殊的“空”

void 类型很有意思。它表示“没有类型”。你不能声明一个 void 类型的变量——想想看,编译器都不知道该给它分配多少内存。

那 void 有什么用?主要有两个场景:

  • 函数返回值:如果函数不需要返回任何值,就用 void。比如 void print_hello()
  • 通用指针:void* 可以指向任何类型的数据。我在写通用数据结构(比如链表、栈)时,经常用 void* 来存储任意类型的数据。
// void 类型的使用
void say_hello() {
    printf("Hello!\n");
}

// void* 通用指针
void* ptr;
int a = 10;
ptr = &a;  // 可以指向 int
// 但使用前必须强制转换
int* int_ptr = (int*)ptr;
printf("%d\n", *int_ptr);

3.5 常量与变量—— 数据的两副面孔

变量就是可以改变的量,常量就是不能改变的量。这个区分在编程中至关重要。

3.5.1 变量

变量有三个要素:类型、名字、值。声明变量时,编译器会为它分配内存空间。

// 变量声明与初始化
int age = 25;           // 声明并初始化
double salary;          // 只声明,未初始化(危险!)
salary = 5000.0;        // 赋值

// 我建议:声明变量时尽量同时初始化
// 否则变量里存的是垃圾值,用起来会出大问题
int uninitialized;      // 不要这样!
printf("%d\n", uninitialized);  // 输出什么?鬼知道!

避坑指南:我曾经在维护一个老项目时,发现某个变量在声明后没有初始化,直接参与运算。结果程序在 Debug 模式下运行正常,Release 模式下就崩溃。查了两天才发现是未初始化变量导致的。从那以后,我写代码必初始化。

3.5.2 常量

常量有两种定义方式:#defineconst

方式 示例 特点
#define #define PI 3.14 预处理阶段替换,没有类型检查
const const double PI = 3.14; 编译阶段处理,有类型检查,更安全
// 常量定义
#define MAX_SIZE 100        // 宏常量
const int MIN_SIZE = 10;    // const 常量

// 我个人更推荐 const,因为:
// 1. 有类型检查,避免宏替换带来的意外
// 2. 调试时可以查看 const 变量的值
// 3. 作用域规则更清晰

// 宏常量的坑:
#define SQUARE(x) x * x
int result = SQUARE(2 + 3);  // 预期 25,实际是 2 + 3 * 2 + 3 = 11
// 正确写法:#define SQUARE(x) ((x) * (x))

3.6 知识体系总览

下面这张图,是我自己总结的数据类型知识结构。你把它记在脑子里,以后遇到类型相关的问题,就能快速定位。

C语言基本数据类型知识体系 基本数据类型 整型 (int) short / int / long unsigned / signed 浮点型 float (单精度) double (双精度) 字符型 (char) 本质是1字节整数 ASCII码存储 void 类型 函数返回值 / void* 通用指针 常量 vs 变量

3.7 本章小结

嗯,到这里,基本数据类型就讲完了。总结一下:

  • 整型:选对范围,注意有无符号。我建议日常用 int,需要大范围用 long long
  • 浮点型:精度是核心。别用 == 比较浮点数,用差值比较。
  • 字符型:记住它是整数,可以参与运算。跨平台时明确 signedunsigned
  • void 类型:不能声明变量,但 void* 是万能指针,写通用代码时很有用。
  • 常量与变量:变量要初始化,常量推荐用 const 而不是 #define

这些基础概念,就像盖楼用的砖头。砖头质量不好,楼盖得再高也会塌。下一章,我们会把这些砖头组合起来,看看运算符和表达式怎么玩。不过那是后话了,先把今天的内容消化掉。

最后送你一句话:写代码时,多花 10 秒钟选对数据类型,能省下未来 10 小时的调试时间。这是我在无数个加班夜里悟出来的道理。

公众号:蓝海资料掘金营,微信 deep3321