基础数据类型

本篇用 sizeof 实测整型、浮点、字符、字符串在内存中的真实尺寸,理解符号位、补码、溢出、类型转换以及可移植性整型的正确打开方式。


一、整型家族全景图

关键字 典型字节 位数 取值范围(有符号) 格式符
char 1 8 -128 ~ 127 %c / %d
short 2 16 -32 768 ~ 32 767 %hd
int 4 32 -2 147 483 648 ~ 2 147 483 647 %d
long 4/8 32/64 与系统字长相同 %ld
long long 8 64 ≈ -9.22×10¹⁸ ~ 9.22×10¹⁸ %lld

> Tips:C标准只规定相对大小sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long) ≤ sizeof(long long)


整型修饰符详解

修饰符作用

  • short:缩短整型变量尺寸,减少取值范围并节省内存,称为短整型
  • long:增长整型变量尺寸,增大取值范围并占用更多内存,称为长整型
  • long long:增长整型变量尺寸,增大取值范围并占用更多内存,称为长长整型
  • unsigned:去除整型变量的符号位,使得整型变量只能表达非负整数

系统字长与类型尺寸

系统字长:操作系统每一次能从内存中处理的数据长度

系统类型 字长 long 尺寸 long long 尺寸
32位系统 32位 4字节 8字节
64位系统 64位 8字节 8字节

标准整型定义

// 标准整型定义(推荐)
short int a;        // 短整型
long int b;         // 长整型  
long long int c;    // 长长整型

unsigned int e;     // 无符号整型

unsigned short int f;   // 无符号短整型
unsigned long int g;    // 无符号长整型
unsigned long long int h; // 无符号长长整型
// 简化写法(int可省略)
short a;            // 短整型
long b;             // 长整型
long long c;        // 长长整型

unsigned e;         // 无符号整型

unsigned short f;   // 无符号短整型
unsigned long g;    // 无符号长整型
unsigned long long h; // 无符号长长整型

//可移植类型(推荐)
#include <stdint.h>

int8_t   num1;      // 精确1字节有符号
int16_t  num2;      // 精确2字节有符号  
int32_t  num3;      // 精确4字节有符号
int64_t  num4;      // 精确8字节有符号

uint8_t  num5;      // 精确1字节无符号
uint16_t num6;      // 精确2字节无符号
uint32_t num7;      // 精确4字节无符号
uint64_t num8;      // 精确8字节无符号

使用建议

  • 嵌入式开发:优先使用 stdint.h 中的精确宽度类型
  • 内存敏感场景:使用 shortint8_t/uint8_t
  • 大数值处理:使用 long longint64_t/uint64_t
  • 无符号需求:明确使用 unsigned 修饰符或 uintX_t 类型

二、实测:我的电脑到底多大?

#include &lt;stdio.h&gt;

int main(void)
{
    printf("char  = %zu B\n", sizeof(char));
    printf("short = %zu B\n", sizeof(short));
    printf("int   = %zu B\n", sizeof(int));
    printf("long  = %zu B\n", sizeof(long));
    printf("long long = %zu B\n", sizeof(long long));
    return 0;
}

64位电脑示例

实验示例

三、符号位 & 补码演示

十进制 单字节补码 注释
5 0000 0101 正数=原码
-5 1111 1011 负数=原码取反+1,符号位不变
0 0000 0000 唯一零
-128 1000 0000 特殊最小值

补码好处:加法器无需区分正负,CPU纯加法搞定减法


四、溢出实验(单字节)

#include <stdio.h>
int main(void)
{
    signed char a = 127;
    a = a + 1;          // 127+1 = 128 → 超出最大值
    printf("127+1 = %d\n", a);   // 结果:-128
    return 0;
}

解释0111 1111 + 1 → 1000 0000 被解释成 -128

溢出实验

五、浮点型精度陷阱

类型 字节 有效十进制位 常量变体
float 4 6~7 3.14f
double 8 15~16 3.14
long double 16 18~19 3.14L
float f1; // 单精度
double f2; // 双精度
long double f3; // 长双精度
浮点型精度

注意点:嵌入式里若对精度敏感(PID、滤波),优先 double,除非MCU硬件浮点只支持 float。


六、字符 & 字符串本质

字符(单字节的整型)

计算机中存储的都是1和0,因此各种字符都必须被映射为某个数字才能存储到计算机中,这种映射关系形成的表称为 ASCII 码表

ACSCII码表

字符串

C语言中并没有具体的字符串类型,现在讲述的是字符串的体现形式。

// 字符串的定义方式有两种:指针和数组
// 【只读】
char *s1 = "abcd"; // 使用字符型指针来指向字符串(字符序列、数组)存储的地址空间(入口地址)
// 【可读写】
char s2[]= "abcd"; // 使用字符数组来存储字符串(字符序列、数组)

// 注意,使用数组来定义字符串时,方括号[]里面的数字可以省略
// 不省略也可以,但必须必字符串实际占用的内存字节数要大,比如:
char s3[] = "apple";
定义方式 内存布局 可读写 备注
char c = 'A'; 单字节 0x41 本质是小整型
char *s1 = "abc"; 常量和区+'\0' 只读,修改段错误
char s2[] = "abc"; 栈数组+'\0' 可复制可修改
char s[] = "abc";     // 栈数组,大小=4(含'\0')
char *p  = "abc";     // 指针,指向只读段
printf("sizeof(s) = %zu\n", sizeof(s)); // 4
printf("sizeof(p) = %zu\n", sizeof(p)); // 8(64位指针)

【只读】指针方式:

指针方式图解

【可读写】数组方式:

数组方式图解

注意:
当一个数组中没有足够的空间存储\0结束符时,可能会造成访问越界的问题。

数组越界图
数组越界图

七、布尔型数据

概念

布尔型数据只有两种取值,非零为真,零为假

语法

#include <stdbool.h>  // 必须包含此头文件

bool a = 1;           // 逻辑真,此处1可以取其他任何非零数值
bool a = true;        // 使用true关键字

bool b = 0;           // 逻辑假
bool b = false;       // 使用false关键字

注意事项

  • 逻辑真除了 1 之外,其他任何非零数值都表示逻辑真,等价于 1
  • 使用布尔型 bool 定义变量时需要包含系统头文件 stdbool.h

使用示例

#include <stdio.h>
#include <stdbool.h>

int main(void)
{
    bool flag1 = 10;      // 非零为真,等价于 true
    bool flag2 = 0;       // 零为假,等价于 false
    bool flag3 = -5;      // 非零为真,等价于 true
    bool flag4 = true;    // 使用true关键字
    
    if (flag1) {
        printf("flag1 is true\n");  // 会执行
    }
    
    if (!flag2) {
        printf("flag2 is false\n"); // 会执行
    }
    
    printf("flag3 = %d\n", flag3);  // 输出:1
    printf("flag4 = %d\n", flag4);  // 输出:1
    
    return 0;
}

应用场景

布尔型数据常用于:

  • 逻辑判断(if语句条件)
  • 循环控制(while、for循环条件)
  • 函数返回值(表示操作成功/失败)
  • 状态标志(开关状态、就绪状态等)

关键点记忆

  • 包含 stdbool.h 头文件
  • truefalse 是关键字
  • 任何非零值都为真,只有零值为假
  • 实际存储为整数(通常1字节)

八、常量与变量

概念

  • 常量:不可改变的内存
  • 变量:可以改变的内存

举例

int a = 100;        // a是变量,而100是常量 
float f = 3.14;     // f是变量,而3.14是常量
char s[] = "abcd";  // s是变量,"abcd"是常量

常量的类型

常量举例 说明 类型
100 整型 int
100L 长整型 long
100LL 长长整型 long long
100ULL 无符号长长整型 unsigned long long
3.14 双精度浮点型 double
3.14L 长双精度浮点型 long double
'a' 字符型(单引号) char
"abcd" 字符指针(字符串、双引号) char *

关键区别

  • 变量:内存内容可以修改,有内存地址
  • 常量:值固定不变,编译时确定
  • 字符串常量:用双引号包围,自动添加\0结束符
  • 字符常量:用单引号包围,单字节

实际应用

const int MAX_SIZE = 100;    // 使用const定义只读变量
#define PI 3.14159          // 使用宏定义常量

int main(void)
{
    int var = 50;           // 变量,可修改
    const int read_only = 200; // 只读变量,不可修改
    
    var = 60;               // ✅ 正确
    // read_only = 300;     // ❌ 错误:不能修改常量
    
    return 0;
}

九、标准输入

概念

键盘是系统的标准输入设备,从键盘中输入数据被称为标准输入

注意

scanf函数会根据format所写的格式控制符,严格要求从键盘中获取对应类型的数据,并存入到指定的内存空间中

相关函数

#include <stdio.h>

int printf(const char *format, ...);
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
int a;
float f;
scanf("%d", &a);   // 从键盘输入一个整型,放入指定的内存地址 &a 中
scanf("%f", &f);   // 从键盘输入一个浮点数,放入指定的内存地址 &f 中

scanf("%d%f", &a, &f); // 从键盘依次输入一个整型和一个浮点型数据,用空白符隔开

char c;
char s[10];
scanf("%c", &c);  // 从键盘输入一个字符,放入指定的内存地址 &c 中
scanf("%s", s);   // 从键盘输入一个单词,放入指定的数组 s 中(注意不是&s)

fgets(s, 10, stdin); // 从键盘输入一行字符串,放入数组 s 中

重要说明

  • scanf需要传递变量的地址(使用&取地址符)
  • 数组名本身表示地址,所以字符数组不需要&
  • fgetsscanf更安全,可以指定最大读取长度
  • 多个输入数据之间用空格、Tab或回车分隔

十、类型转换

概念

不一致但相互兼容的数据类型,在同一表达式中(在运算过程中)将会发生(默认、隐式)类型转换。

转换模式

  • 隐式转换:系统按照隐式规则(向上→往高精度转)自动进行的转换
  • 强制转换:用户显式自定义进行的转换

隐式规则

从小类型向大类型转换,目的是保证不丢失表达式中数据的精度

  • 隐式转换示例代码
char  a = 'a';
int   b = 12;
float c = 3.14;
float x = a + b - c; // 在该表达式中将发生隐式转换,所有操作数被提升为float
  • 强制转换:用户强行将某类型的数据转换为另一种类型,此过程可能丢失精度
char  a = 'a';
int   b = 12;
float c = 3.14;
float x = a + b - (int)c; // 在该表达式中a隐式自动转换为int,c被强制转为int

重要说明:不管是隐式转换,还是强制转换,变换的都是操作数在运算过程中的类型,是临时的,操作数本身的类型不会改变,也无法改变。


十一、可移植性整型

概念

不管放到什么系统,尺寸保持不变的整型数据,称为可移植性整型

关键:typedef

typedef int int32_t;   // 将类型 int 取个别名,称为 int32_t
typedef long int64_t;  // 将类型 long 取个别名,称为 int64_t

实现思路

a. 为所有的系统提供一组固定的、能反应数据尺寸的、统一的可移植性整型名称
b. 在不同的系统中,为这些可移植性整型提供对应的 typedef 语句

系统预定义的可移植性整型

  • 精确宽度类型
#include <stdint.h>

// 有符号精确宽度类型
int8_t    // 1字节有符号整数
int16_t   // 2字节有符号整数  
int32_t   // 4字节有符号整数
int64_t   // 8字节有符号整数

// 无符号精确宽度类型
uint8_t   // 1字节无符号整数
uint16_t  // 2字节无符号整数
uint32_t  // 4字节无符号整数
uint64_t  // 8字节无符号整数
  • 系统专用类型
#include <sys/types.h>

pid_t     // 进程ID类型
time_t    // 时间类型
size_t    // 尺寸类型(用于sizeof返回值)
ssize_t   // 有符号尺寸类型
off_t     // 文件偏移类型
  • 使用示例
#include <stdint.h>
#include <stdio.h>

int main(void)
{
    int32_t counter = 1000;        // 保证在任何平台都是4字节
    uint16_t port = 8080;          // 保证在任何平台都是2字节无符号
    uint8_t flags = 0xFF;          // 保证在任何平台都是1字节无符号
    
    printf("counter size: %zu bytes\n", sizeof(counter));  // 4
    printf("port size: %zu bytes\n", sizeof(port));        // 2
    printf("flags size: %zu bytes\n", sizeof(flags));      // 1
    
    return 0;
}

嵌入式开发优势

  • 通信协议:确保不同设备间数据格式一致
  • 寄存器操作:精确控制硬件寄存器位宽
  • 跨平台移植:代码在不同架构MCU间无需修改
  • 内存映射:精确控制外设寄存器访问

结论:在嵌入式开发中,始终使用可移植性整型,避免平台相关的尺寸问题

数据类型的本质

概念

各种不同的数据类型,本质上是用户与系统对某一块内存数据的解释方式的约定

推论

  • 类型转换,实际上是对先前定义时候的约定,做了一个临时的打破
  • 理论上,可以对任意的数据做任意的类型转换,但转换之后的数据解释不一定有意义

整型数据尺寸

概念

整型数据尺寸是指某种整型数据所占用内存空间的大小

C语言标准规定

C语言标准并未规定整型数据的具体大小,只规定了相互之间的"相对大小",比如:

  • short 不可比 int
  • long 不可比 int
  • long 型数据长度等于系统字长

系统字长

CPU一次处理的数据长度,称为字长。比如32位系统、64位系统。

典型尺寸

  • char:占用1个字节
  • short:占用2个字节
  • int:在16位系统中占用2个字节,在32位和64位系统中一般都占用4个字节
  • long:尺寸等于系统字长
  • long long:在32位系统中一般占用8个字节,在64位系统中一般占用8个字节

存在问题

  • 同样的代码,放在不同的系统中,可能会由于数据尺寸发生变化而无法正常运行
  • 因此,系统标准整型数据类型是不可移植的,这个问题在底层代码中尤为突出

解决方案

#include <stdint.h>

// 使用精确宽度类型保证跨平台一致性
int8_t   var1;  // 精确1字节
int16_t  var2;  // 精确2字节  
int32_t  var3;  // 精确4字节
int64_t  var4;  // 精确8字节
uint8_t  var5;  // 精确1字节无符号
uint32_t var6;  // 精确4字节无符号
posted @ 2025-10-11 14:13  林明杰  阅读(20)  评论(0)    收藏  举报