引言

什么是类型系统?

类型系统(Type System)​ 是一组规则和机制,用于定义程序中可以使用的各种数据类型,以及这些类型如何被创建、使用和相互转换。

为什么要有类型系统?

  • 提高代码可读性和可维护性:明确的数据类型让代码更易理解。

  • 减少错误:类型系统可以在编译阶段捕捉很多潜在的错误,比如类型不匹配。

  • 帮助编译器优化:编译器知道数据类型后,可以进行更好的内存分配与指令优化。

  • 保障程序安全(相对地):良好的类型使用能避免很多内存错误和未定义行为。

事实上,我还真没见过没有类型系统的编程语言...

C语言的类型

C语言的类型大体可以分为以下几类

基本类型 构造类型 指针类型 空类型
字符型char 数组 * void
整型 结构&联合
浮点型 枚举类型

基本类型

整型

整型指的是整数类型,这种类型没有小数部分

类型 字节数 取值范围 备注
short 2 -2^15~2^15-1
unsigned short 2 0~2^16-1
int 4 -2^31~2^31-1 16位平台下通常是2字节
unsigned int 4 0~2^32-1 同int
long 4 -2^31~2^31-1 在部分平台也可能是8字节
unsigned long 4 0~2^32-1 同long
long long 8 -2^63~2^63-1 C99引入
unsigned long long 8 0~2^64-1 C99引入

注:实际大小需用 sizeof()确认,不同平台、不同编译器可能有细微调整。

C语言标准没有严格规定基本整型的字节长度,仅规定相对大小关系和最小取值范围。 其中short<=int<=long<=long long。


浮点型

浮点型(也称为实型)可以理解为小数,具有小数部分。

类型 名称 字节数 有效数字位数 取值范围
float 单精度浮点型 4 7 -3.4x10^-38~3.4x10^38
double 双精度浮点型 8 15 -1.7x10^-308~1.7x10^308
long double 长双精度浮点型 不定(不同平台差异大) 同左 同左

long double的字节数由其底层存储格式决定,常见情况如下 (作为了解)

平台/编译器 ​存储格式​ 理论位数 ​实际字节数(sizeof) ​说明
​x86/x86_64(GCC/Clang/Linux/macOS) ​80 位扩展精度(x87 FPU) 80 位 12 或 16 字节 实际存储 80 位(1 符号位 + 15 指数位 + 64 尾数位),但内存中按 16 字节对齐(如 GCC 通常返回 16)。
Windows(MSVC) ​等同于 double(64 位 IEEE 754) 64 位 8 字节 MSVC 中 long double与 double无区别,仅为兼容保留。
64 位系统(部分编译器,如 GCC PowerPC) ​128 位四精度(IEEE 754-2008) 128 位 16 字节 1 符号位 + 15 指数位 + 112 尾数位(含隐含位共 113 位)。
嵌入式系统​ 可能为 96 位或其他定制格式 96 位 12 字节 较少见,取决于硬件 FPU 支持。

实际占用需使用sizeof函数确认。

long double是 C 语言中精度最高的浮点类型,但其实现高度依赖平台,适合对精度有极致要求的场景。使用时需注意:

  • 用后缀 L标记字面量;
  • 用 %Lf进行 I/O;
  • 通过 宏了解当前平台特性;
  • 权衡精度、性能与可移植性。

在大多数日常场景中,double已足够,仅在必要时才考虑 long double。


字符型

字符型指的是字符类型的数据,包括有符号字符型(char)和无符号字符型(unsigned char)。

char类型的取值范围为-128~127,unsigned char类型的取值为0~255。

一个字符型数据占用一个字节,书写形式是用单引号括起来的单个字符,例如'1','e','?'; 事实上,char类型本质上也是整型,它实际上储存的是对应的字符的编码(ASCII码以及扩展的ASCII码)。因此,你可以讲char类型理解为只占用一个字节的int类型。

例如,下面的代码片段是合法的:

char a='A';
int b=a+1;

从数学角度来看,一个数字+一个字符没有意义(不要狡辩说是方程!),但在C语言中,实际的操作是将字符的编码拿来相加,因此,结果仍然有意义('A'的ASCII值(97)+1是'B'的ASCII值(98))。

  • 补充-转义字符:

有些特殊的控制字符无法直接写出,它们有特殊的写法:以反斜杠\为开头加上一个字符或一个数字序列。这类字符叫做‘转义字符’。

字符 ASCII值 含义
\a 7 终端响铃
\b 8 退格(Backspace)
\n 10 换行
\r 13 回车(Enter)
\\ 92 反斜杠\
\' 39 单引号
\" 34 双引号

构造类型

数组类型

数组是一组固定数量的、类型相同的变量,它们在内存中占据一块连续的存储空间,每个元素通过整数索引(从 0开始)唯一标识。

数组按维度可分为一维数组、多维数组(二维最常见,更高维可类推);按存储方式分为静态数组(栈/全局区)和动态数组(堆区);按元素类型可分为基本类型数组、结构体数组、指针数组等。

声明与初始化:

简单的一维数组定义int a[10]; //定义一个有10个元素的int数组

访问元素:数组通过下标为每个元素编号,访问时通过数组名[下标号]的格式来访问;

数组的下标从0开始,依次递增,即第i个元素的下标为i-1。

例如 a[2]访问的是数组a的第3个元素。

有关数组的详细内容,我们另行专门讨论。


结构类型与联合类型

数组可以用于聚合相同类型数据,但当需要聚合不同类型数据时,我们需要使用结构类型(struct)。

struct people
{
    char name[20];
    int age;
    int height;
}

这个结构体people一共有三个成员,name,age和height。

联合类型与之类似,但区别是联合类型只有最后一个写入的成员有效。

union data
{
    int err_code;
    float sensor_value;
}

在内存上,结构类型为每个成员都分配了各自的空间,因此一个结构体最终大小由所有成员大小之和决定;联合类型的各个成员共用内存空间,它的大小由其中最大的成员决定。


枚举类型

枚举类型是对整数序列的映射,将整数用易于理解的单词替换,可以用来提高程序的可读性。

#include<stdio.h>
//--这里是枚举类型的定义
enum week{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};
//--
int main()
{
    printf("%d",Monday); //输出Monday的值
    return 0;
}

week是枚举类型名,其中每个元素对应的值依次为0,1,2,3,4,5,6。

枚举值为整形常量,定义后无法修改!

空类型

void(无类型/空类型)通常用于对无返回值函数、无参函数、通用指针类型的声明。

#include<stdio.h>
void funa(int b){  //这是一个无返回值(return)函数
    printf("%d+%d=%d",b,b,b+b);
}
int funb(void){  //这是一个无参函数
    return 1;
}
void func(void){  //这个函数既无返回值也无参
    printf("Hello");
}

int main(){
    int a=1;
    void *p=&a;  //定义一个void指针p
}

指针类型

指针用于记录变量,函数等实体在内存中的地址,指针类型与其指向的对象有关。

int a=1;
int *p=&a;
*p+=a;