C语言入门:数据类型分类:基本类型、构造类型、指针、空类型

引言:为什么需要数据类型?

C 语言是一种 “强类型语言”,所有数据必须先声明类型才能使用。数据类型的本质是规定数据的存储方式(占多少内存)、取值范围(能存哪些值)和操作方式(能做什么运算)。例如:

  • 整型(int)占 4 字节(常见环境),只能存 - 2147483648 到 2147483647 之间的整数;
  • 字符型(char)占 1 字节,存的是 ASCII 码(如 'A' 对应 65);
  • 指针(*)占 8 字节(64 位系统),存的是内存地址。
一、基本类型(Primitive Types)

基本类型是 C 语言内置的、不可再分解的类型,共 3 大类:整型、浮点型、字符型。

1.1 整型(Integer Types)

整型用于存储整数,根据取值范围符号分为不同子类型:

类型关键字存储大小(常见环境)取值范围说明
char1 字节-128 ~ 127(有符号)本质是小整型,常用来存字符
unsigned char1 字节0 ~ 255(无符号)纯数值,不存字符时使用
short(或short int2 字节-32768 ~ 32767短整型,节省内存
unsigned short2 字节0 ~ 65535无符号短整型
int4 字节-2147483648 ~ 2147483647最常用的整型
unsigned int4 字节0 ~ 4294967295无符号整型(只能存非负数)
long(或long int4/8 字节(取决于系统)-2^31 ~ 2^31-1(32 位)长整型,用于大整数
unsigned long4/8 字节0 ~ 2^32-1(32 位)无符号长整型

注意

  • C 标准未规定具体存储大小(只要求short ≤ int ≤ long),实际大小由编译器和系统决定(可用sizeof(int)查看);
  • 无符号类型(unsigned)只能存非负数,适合表示 “数量”(如年龄、次数);
  • char严格来说是整型的一种,但通常用于存储字符(通过 ASCII 码映射,如'A'=65,'0'=48)。
1.2 浮点型(Floating-Point Types)

浮点型用于存储小数(实数),通过 “科学计数法” 表示(如 123.45=1.2345×10²),分为单精度(float)和双精度(double):

类型关键字存储大小有效位数取值范围(近似)说明
float4 字节6~7 位±3.4×10^-38 ~ ±3.4×10^38单精度,适合一般计算
double8 字节15~17 位±1.7×10^-308 ~ ±1.7×10^308双精度,精度更高,更常用

注意

  • 浮点型有 “精度丢失” 问题(如0.1无法精确表示为二进制小数),比较两个浮点数时需用 “误差范围”(如|a - b| < 1e-6);
  • 浮点型变量声明时,float需加f后缀(如float x = 3.14f),否则默认是double类型。
1.3 字符型(Character Types)

字符型(char)本质是 1 字节的整型,用于存储 ASCII 字符(共 128 个,包括字母、数字、符号、控制符)。

示例代码

#include <stdio.h>

int main() {
    char c1 = 'A';    // 存储大写字母A(ASCII码65)
    char c2 = 65;     // 直接存ASCII码,等价于c1
    char c3 = '\n';   // 存储换行符(控制符)
    
    printf("c1: %c(ASCII码:%d)\n", c1, c1);  // 输出:c1: A(ASCII码:65)
    printf("c2: %c\n", c2);                    // 输出:c2: A
    printf("c3: 换行成功!");                    // 输出时c3会换行
    return 0;
}
二、构造类型(Derived Types)

构造类型是通过基本类型组合或扩展得到的类型,包括数组、结构体、联合体、枚举。

2.1 数组(Array)

数组是相同类型数据的连续存储集合,通过 “下标”(从 0 开始)访问元素。

定义方式
类型 数组名[元素个数];
(例:int scores[5]; 定义一个包含 5 个整型的数组)

关键特性

  • 内存连续:数组元素在内存中 “紧挨着” 存放(如int arr[3]占 12 字节,地址依次为 0x1000、0x1004、0x1008);
  • 下标越界:C 语言不检查下标是否越界(如arr[5]访问第 6 个元素会导致 “未定义行为”,可能崩溃);
  • 初始化:可在定义时赋值(如int arr[] = {1,2,3}; 自动确定长度为 3)。

示例代码

#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};  // 初始化数组
    int len = sizeof(arr) / sizeof(arr[0]);  // 计算数组长度(5)
    
    // 遍历数组
    for (int i = 0; i < len; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    return 0;
}
2.2 结构体(Struct)

结构体是不同类型数据的集合,用于表示一个 “复合对象”(如学生、日期)。

定义方式

struct 结构体名 {
    类型 成员1;
    类型 成员2;
    // ...其他成员
};

(例:struct Student { char name[20]; int age; float score; };

关键特性

  • 成员访问:通过.操作符(如Student s; s.age = 20;);
  • 内存对齐:结构体成员存储时会 “对齐” 到特定地址(如char占 1 字节,但后面的int可能从 4 的倍数地址开始),导致结构体总大小可能大于成员大小之和;
  • 结构体指针:通过->操作符访问成员(如struct Student *p = &s; p->age = 20;)。

示例代码

#include <stdio.h>
#include <string.h>

struct Student {
    char name[20];
    int age;
    float score;
};

int main() {
    struct Student s1;
    strcpy(s1.name, "张三");  // 字符串赋值需用strcpy
    s1.age = 20;
    s1.score = 90.5f;
    
    printf("姓名:%s,年龄:%d,分数:%.1f\n", s1.name, s1.age, s1.score);
    return 0;
}
2.3 联合体(Union)

联合体是多个成员共享同一块内存的类型,每次只能使用其中一个成员(最后赋值的成员有效)。

定义方式

union 联合体名 {
    类型 成员1;
    类型 成员2;
    // ...其他成员
};

关键特性

  • 内存共享:联合体的大小等于其最大成员的大小(如union U { char c; int i; }占 4 字节,ci共用这 4 字节);
  • 用途:节省内存(当多个成员不会同时使用时),或用于 “类型转换”(如通过联合体观察整型的字节组成)。

示例代码

#include <stdio.h>

union Data {
    int i;
    float f;
    char str[4];
};

int main() {
    union Data data;
    
    data.i = 10;          // 赋值整型成员
    printf("data.i: %d\n", data.i);  // 输出:10
    
    data.f = 22.5f;       // 覆盖之前的整型数据
    printf("data.f: %.1f\n", data.f);  // 输出:22.5
    
    strcpy(data.str, "ABC");  // 覆盖浮点数据
    printf("data.str: %s\n", data.str);  // 输出:ABC
    return 0;
}
2.4 枚举(Enum)

枚举是为一组整数常量命名的类型,用于提高代码可读性(如表示 “星期”“状态”)。

定义方式

enum 枚举名 {
    枚举常量1,
    枚举常量2,
    // ...其他常量
};

(枚举常量默认从 0 开始递增,也可手动赋值,如enum Color { RED=1, GREEN=2, BLUE=4 };

关键特性

  • 本质是整型:枚举变量存储的是整数(如enum Color c = RED;c的值是 1);
  • 用途:替代 “魔法数字”(如用RED代替 1,代码更易理解)。

示例代码

#include <stdio.h>

enum Week {
    MON=1, TUE, WED, THU, FRI, SAT, SUN  // MON=1,后续自动递增(TUE=2,...,SUN=7)
};

int main() {
    enum Week today = WED;
    printf("今天是星期%d\n", today);  // 输出:今天是星期3
    return 0;
}
三、指针(Pointer):指向内存地址的类型

指针是 C 语言的核心特性,用于直接操作内存。指针变量存储的是 “内存地址”(类似于 “门牌号”),通过 “解引用” 可以访问地址上的数据。

3.1 指针的基本操作
  • 声明指针类型* 指针名;(例:int* p; 声明一个指向整型的指针);
  • 取地址&变量名(例:int a=10; p=&a; 让p存储a的地址);
  • 解引用*指针名(例:*p表示p指向的地址上的数据,即a的值 10)。

示例代码

#include <stdio.h>

int main() {
    int a = 10;
    int* p = &a;  // p存储a的地址
    
    printf("a的值:%d\n", a);        // 输出:10
    printf("a的地址:%p\n", &a);     // 输出:0x7ffd...(具体地址)
    printf("p存储的地址:%p\n", p);   // 输出:与&a相同
    printf("*p的值:%d\n", *p);      // 输出:10(解引用)
    return 0;
}
3.2 指针与数组

数组名本质是 “指向首元素的指针”(如int arr[3]arr等价于&arr[0])。

示例代码

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30};
    int* p = arr;  // p指向数组首元素(arr[0])
    
    // 指针运算:p+1指向arr[1](地址+4字节,因int占4字节)
    printf("arr[0] = %d\n", *p);      // 输出:10
    printf("arr[1] = %d\n", *(p+1));  // 输出:20
    printf("arr[2] = %d\n", *(p+2));  // 输出:30
    return 0;
}
3.3 指针与结构体

通过指针访问结构体成员时,用->操作符(等价于(*指针).成员)。

示例代码

#include <stdio.h>

struct Student {
    char name[20];
    int age;
};

int main() {
    struct Student s = {"李四", 22};
    struct Student* p = &s;
    
    // 两种方式访问成员
    printf("姓名:%s\n", (*p).name);  // 等价于s.name
    printf("年龄:%d\n", p->age);      // 更简洁的写法
    return 0;
}
3.4 指针的高级应用
  • 动态内存分配:通过malloc/calloc函数在堆内存中分配空间(如int* arr = (int*)malloc(5*sizeof(int)););
  • 函数指针:指向函数的指针(如int (*func)(int, int); 可指向加法函数);
  • 指针数组与数组指针
    • 指针数组:int* arr[5];(数组元素是指针);
    • 数组指针:int (*arr)[5];(指针指向一个包含 5 个整型的数组)。
四、空类型(Void Type)

空类型(void)表示 “无类型”,用于以下场景:

4.1 无返回值的函数

函数声明为void表示没有返回值(如void print_hello() { printf("Hello"); })。

4.2 无类型指针(void*)

void*是 “万能指针”,可以指向任何类型的数据(但使用时需强制转换类型)。

示例代码

#include <stdio.h>

void print_value(void* ptr, char type) {
    if (type == 'i') {
        int* int_ptr = (int*)ptr;  // 强制转换为int*
        printf("整型值:%d\n", *int_ptr);
    } else if (type == 'f') {
        float* float_ptr = (float*)ptr;  // 强制转换为float*
        printf("浮点值:%.1f\n", *float_ptr);
    }
}

int main() {
    int a = 10;
    float b = 22.5f;
    
    print_value(&a, 'i');  // 输出:整型值:10
    print_value(&b, 'f');  // 输出:浮点值:22.5
    return 0;
}
4.3 无参数的函数

函数参数声明为void表示没有参数(如int func(void) { return 0; })。

五、总结:数据类型的核心作用

数据类型是 C 语言的 “基石”,它:

  1. 规定内存大小:不同类型占不同字节(如int占 4 字节,double占 8 字节);
  2. 限制操作方式:整型可以做取模运算(%),但浮点型不行;
  3. 提高可读性:用struct Student代替 “姓名 + 年龄 + 分数” 的零散变量,代码更清晰;
  4. 保证内存安全:通过类型检查避免 “错误数据操作”(如用字符型存大整数会溢出)。

形象生动的 “数据类型分类” 解释(用生活比喻帮你记住)

我们可以把 C 语言的数据类型想象成 “数据的家具风格”—— 不同的家具(数据)有不同的 “形状”“功能” 和 “摆放方式”,而 “数据类型” 就是给这些 “家具” 分类的规则。

1. 基本类型:最基础的 “单人家具”

特点:像 “单人椅”“小桌子” 一样,是最基础、不可再拆分的类型。
C 语言里的基本类型有 3 种核心 “款式”:

  • 整型(int):专门装 “整数” 的 “盒子”,比如年龄(20)、数量(5)。
    (类比:单人椅只能坐 1 个人,整型盒子只能装整数,装不了小数)
  • 浮点型(float/double):专门装 “小数” 的 “精密盒子”,比如身高(1.75)、价格(9.99)。
    (类比:带刻度的量杯,能装更细的数据)
  • 字符型(char):专门装 “单个字符” 的 “小格子”,比如字母('A')、符号('!')。
    (类比:邮票盒,每个格子只能放一张邮票,字符型只能存 1 个字符)
2. 构造类型:“组合家具”—— 用基本类型拼出来的新款式

特点:像 “沙发床”(沙发 + 床)、“衣柜书桌组合” 一样,用基本类型 “拼” 出来的新类型。
C 语言里的构造类型有 4 种 “组合方式”:

  • 数组(Array):“一排相同款式的单人椅”—— 把多个相同类型的基本数据排在一起。
    (例:int scores[5] 是 5 个整型组成的数组,像 5 个并排的整数盒子)
  • 结构体(struct):“多功能家具组合”—— 把不同类型的数据打包成一个整体。
    (例:描述 “学生” 时,用struct Student { char name[20]; int age; float score; },把姓名(字符数组)、年龄(整型)、分数(浮点型)装在一起)
  • 联合体(union):“共享空间的家具”—— 多个数据共用同一块空间,每次只能用其中一个。
    (类比:一个抽屉,今天放袜子,明天放手套,但不能同时放)
  • 枚举(enum):“给选项贴标签”—— 把有限的可选值列出来,比如 “星期” 只有周一到周日。
3. 指针:“地址标签”—— 指向数据的 “导航图”

特点:像 “快递单上的地址”—— 不直接存数据,而是存数据的 “位置”(内存地址)。
(例:你有一个盒子(变量int a=10),指针int *p存的是盒子的 “门牌号”(&a),通过指针可以 “找到” 盒子里的内容(*p就是 10))

4. 空类型(void):“万能工具”—— 没有具体类型的 “空白区”

特点:像 “万能接口”—— 不指定具体类型,用于通用场景。
(例:void main()表示主函数 “没有返回值”;void *是 “万能指针”,可以指向任何类型的数据)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值