C-数据类型

第一个C语言程序:

bash 复制代码
#include <stdio.h>
int main()
printf("Hello World!");
return 0;
}
#include <stdio.h表示保护尖括号里面的文件,stdio.h是一个文件,这个文件称为头文件。
• int main()表示主函数,int 表示返回值类型main 表示函数的名字。
{}表示函数体,函数的内容。
printf("Hello Wor1d!);作用:在终端上输出"Hellowor1d!"
return0表示这个函数结束,并且返回一个值

数据类型

bash 复制代码
什么是数据类型?
数据类型:首先是将现实中的客观事物进行抽象化的处理,在计算机里面本质上没有办法直接处理现实事物,但是计算机里面所有的数据都是由0/1组成,没有所谓的类型之分。数据类型主要是为了方便人们能够区分每个数据所表示的含义,就出现类型的概念。
注意:所以数据类型并不是给计算机来看的,而是给程序员来看。

C语言里面的数据类型

bash 复制代码
基本类型
构造类型
指针类型
空类型
bash 复制代码
基本类型:编译器内置的类型
○ 整型
短整型:short 2字节
整型:int 4字节
长整型:1ong 4/8字节(32位系统 4字节,64位系统 8字节)

。浮点型(实型)
单精度:float 4字节
双精度:double 8字节

。字符型
字符型:char 1字节
bash 复制代码
构造类型:由程序员自己创建的合法的数据类型
。 数组
。 结构体
。 共用体(联合体)
。 枚举

指针类型:指向合法类型空间的指针
• 空类型
o void
bash 复制代码
内存空间单位:比特(bit)<字节(Byte)<干字节(KB)<兆字节(MB)<干兆字节(GB)

每个基本类型都有:signed和unsigned形式。

注意:基本类型的空间大小是固定。

bash 复制代码
构造类型
数组
。是一组相同类型的元素的集合
结构体
。是一组不同类型的元素的集合
• 共用体(联合体)
。一组不同类型的元素共用一段空间
枚举
。列举某个范围内值
注意:构造类型的内存大小是不固定的,取决于程序怎么去定义。

指针类型
指针类型:一般来说指的是指针变量。
基本类型指针
构造类型指针
由void构成万用指针
注意:系统给到指针类型变量的大小是固定,具体大小取决于系统的位数。

空类型
空类型:无值类型
void 和函数组成
。无返回值
。无参数
void和指针组成
。 通用类型指针

常量

bash 复制代码
常量
常量:在程序运行过程中无法被改变的量(字面量)
1234...此类整数常量
'A''B'C'..此类字符常量
"123456""ABCDEF"...此类字符串常量

变量

bash 复制代码
变量
变量:在程序运行过程中可以被修改的量(可变化的量)
6 + 6 = 12
x + y = 12 // x 和 y 可以看成是程序中的变量
变量实际上是内存中具有特定属性的存储单元,它是用来存放数据的。这个存储单元存放的数据就是:变量的值,这个值在程序运行期间是可以被改变的。
bash 复制代码
变量名
系统会把内存一个字节,一个字节的分割成若干个,然后进行一个编号。这个编号就是内存地址(无符号的整数),每增加一个字节,地址就会+1,操作系统在访问存储单元的时候,就必须要知道内存地址。
变量名会关联内存地址,操作变量名其实就操作内存。

变量的定义
变量的定义:创建一个变量的名字,并指定该变量的类型,让系统在内存中为它分配一块内存空间使用,并且把变量名和空间的地址关联起来。

变量定义语法格式:变量的类型+变量的名字+初始值+分号 (注意:中间要有空格,初始值可以省略)
int x;//定义了一个名字为×的int类型变量
int y=10;//定义了一个名字为y的int类型变量,并且初始化赋值为10
变量的类型:只要是C中合法的类型都是可以。如:基本类型,构造类型..

变量名:给存储单元起了一个名字。就是说这个名字会和一个存储单元关联起来。
注意:变量在定义的时候可以为变量进行赋值,也可以不赋值。
bash 复制代码
变量名的命名规则
C语言中命名标识符(名字)的时候需要符合以数字,字母,下划线组成的规则,并且只能由下划线和字母开头。
123ABC X
_abc√
abc-1 X

编程规范
1.在变量命名的时候,一定要避免下划线开头,因为一般下划线开头的都是作用于编译器编译的符号。
2. 在给变量对象取名字的时候,尽量做到"见其名知其意"
int a;//可以是可以,但是过久了,就不知道是干嘛的了。
int sum;// 知道这个是用来存和的。
int zheshiyigeyonglaicuncuhedebianliang;// 尽量不要这样去写。
。如果变量意思太长可以用简写,或者用注释解释。
。 尽量使用驼峰命名
int minNumber; // 最小数
int MaxNumber; // 最大数
3. 要注意缩进
。缩进:可以让我们的代码层次和逻辑更加清晰,包含关系更加明确。
。缩进的方式:tab键,可以自动往后填充固定数量的空格。

变量的访问(读/写)

bash 复制代码
变量的访问(读/写)

变量的读值
从变量中取值(读),实际上就是通过变量名找到它所关联的存储单元,从存储单元里面读取值。这个时候变量名一般在赋值符号(=)的右边。用来表示变量的值,称为:右值

变量的写值
给变量赋值(写),实际上就是通过变量名找到它所关联的存储单元,把数据写入到个存储单元中去,覆盖原来的值。这个时候变量一般在赋值符号(=)的左边,称为:左值
int x;//定义了一个名字为×的int类型变量
//没有进行赋值,里面存在的值是什么取决于编译器的处理。如果处理了一般为0,否则的话一般为随机的值。

变量的写入操作
int x;
× = 10;// 将常量数据10 存储到×所绑定的内存存储单元中。

变量的读取操作
int x;
x = 10; // 写入操作
printf("%d\n",x);//系统会从×所绑定的内存单元中取出它存储的值,并且在中断上面打印出来。

常变量

bash 复制代码
常变量
定义变量的时候,给它使用一个const进行一个修饰说明。表示这个变量在初始化之后,就不能赋值了,称它:只读变量
//正常的变量定义
int x = 1;
x = 10;
// 常变量的定义
const int x;
× = 10;// 错误的,不能在初始化之后进行赋值修改
//正确的定义方式
const int y = 10; // 在定义的时候进行赋值

整型数据

bash 复制代码
整型数据
整型常量:

十进制常量数:
1 18 99 168 348 // 十进制常量,逢十进一

八进制常量数:
00777 0664 0111 // 八进制常量 逢八进一
0123 √ 0864 × 0108 ×

十六进制常量数:
0//用字母来表示从10开始到15的数//A B C D E F
0xCF 0×1F 0×00 // 逢十六进一

计算机在底层存储数据就是用二进制保存的
// bit 比特位:每一位数据只有两个值:0/1 逢2进1
2 --> 10
3 --> 11
xxxx xxxx -->1字节
128 64 32 16 8 4 2 1
bash 复制代码
整型变量:
整型数据在内存中,以二进制的补码形式进行存放。并且最高位为符号位:符号位为1表示有负数,符号位为0表示纯整数,其他位为数字位。

存储的过程
补码过程:其绝对值的源码取反+1,源码:就是这个整型数据的二进制码
int × = 10;//定义了名为×的int类型变量,初始化为10;
//转换为2进制的形式
10--> 0000 1010 -->源码
//取反:将二进制形式下的1变成0,0变成1
0000 1010 --> 1111 0101
// 对取反后的二进制数据+1
1111 0101 +1 --> 1111 0110 --> 补码

```bash
读取的过程
//对补码减一取反
1111 0110 -1 --> 1111 0101
// 取反
1111 0101 --> 0000 1010 --> 10

-1
(实际系统在操作的时候最高位不会跟着变动,这里是为了好计算)0000 0001-->-1的绝对值的源码1111 1110 +1 --> 1111 1111 --> -1的补码
//还原
1111 1111 -1 --> 1111 1110
// 取反
(1) 0000 0001 --> -1

正数在内存中保存,其补码就是其源码,负数保存的时候,其补码就是其绝对的值源码取反+1

有符号和无符号区别

bash 复制代码
有符号和无符号区别
CPU把数据进行计算的时候,需要把变量的数据从存储单元中取出来CPU内部的寄存器(运算寄存器)(32bits 4字节),当变量的数据小于寄存器的时候,有符号的数据拷贝到寄存器的时候,空出的高位,需要补符号位。无符号的数据,拷贝到寄存器,多出来位,补0.
//unsigned 表示定义的变量是一个无符号的变量类型:无符号字符型变量unsigned char c1 = 255;

//signed 表示定义的变量是一个有符号的变量类型:有符号的字符型变量signed char c2 = 255;

// 1字节:8个比特位 128 64 32 16 8 4 2 1: 1111 1111
// 寄存器:0000 0000 0000 0000 0000 0000 0000 0000 默认右侧是低地址

c1:255 --> 1111 1111 --> 取到寄存器里面 --> 0000 0000 0000 0000 0000 0000 1111 1111 --> 255

//c2 有符号的数据:有负数范围的
c2:255 --> 1111 1111 --> 取到寄存器里面 --> 1111 1111 1111 1111 1111 1111 1111 1111 --> -1

1111 1111 1111 1111 1111 1111 1111 1111:因为最高位为(符号位)1,所以这个数据是一个负数-1
1111 1111 1111 1111 1111 1111 1111 1110
取反
0000 0000 0000 0000 0000 0000 0000 0001 : 1 这个---是绝对值-1
有符号和无符号最大的区别:有无符号位
注意:不用unsigned和signed修饰的变量,默认都是signed类型
在一定范围之内,肯定有一个整数和一个负数的补码是一模一样的。
bash 复制代码
各个整型之间的赋值
长整型向短整型赋值:
long 1 = 65536;
short s = 1;// 把1赋值给s,把1的数据取出来写入到s里面//s的值是多少?0
65536 : 0001 0000 0000 0000 0000
short :0000 0000 0000 0000
注意:高精度向低精度数据赋值的时候只保留低字节数据,只保留能保留的比特位。
短整型向长整型赋值:I
short s = 65535:
long 1 = s; // 将s赋值给1
//1的值是多少?
65535 :  1111 1111 1111 1111

1ong : 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 1111 1111 1111
       1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111
注意:低精度向高精度赋值直接拷贝,如果为unsigned高字节补0,如果signed高字节补符号位。

C 语言基础与数据类型

整理日期:2026-08-28

内容来源:老师课堂笔记 + 课程录音转写

说明:本文已合并重复内容、修正明显转写错误,并对容易误解的表述补充了标准 C 语言说明。

1. 第一个 C 语言程序

c 复制代码
#include <stdio.h>

int main(void)
{
    printf("Hello World!\n");
    return 0;
}

1.1 逐行解释

  • #include <stdio.h>:预处理指令,把标准输入输出头文件 stdio.h 中的声明提供给当前源文件使用。printf 的函数声明就在其中。它不是"保护文件"。
  • int main(void):程序入口函数。main 是函数名,前面的 int 表示函数返回一个整数;void 表示不接收参数。
  • { ... }:函数体,包含函数执行的语句。
  • printf("Hello World!\n");:向标准输出打印字符串;\n 表示换行。
  • return 0;:结束 main 函数并向运行环境返回 0,通常表示程序正常结束。

注意:每条普通 C 语句末尾通常需要分号;字符串必须由成对的英文双引号包围。

2. 为什么需要数据类型

计算机底层最终处理的是二进制位,但程序需要区分"这些位代表什么",例如整数、字符、小数、地址等。数据类型主要有三项作用:

  1. 说明数据的含义以及允许进行的操作;
  2. 决定或约束对象所需的存储空间和对齐方式;
  3. 决定二进制数据应如何解释,例如有符号整数与无符号整数的解释方式不同。

因此,类型既帮助程序员表达意图,也为编译器进行内存布局、类型检查和生成机器指令提供依据。

3. C 语言数据类型总览

3.1 基本类型

基本类型由语言直接提供,主要包括:

  • 整数类型:charshortintlonglong long,以及对应的有符号和无符号形式;
  • 浮点类型:floatdoublelong double
  • 布尔类型:C99 的 _Bool,通常通过 <stdbool.h> 使用 bool
  • 空类型:void

3.2 构造(派生或复合)类型

  • 数组:一组相同元素类型的对象;
  • 结构体 struct:把多个可以不同类型的成员组织在一起;
  • 联合体 union:多个成员共享同一段存储空间,同一时刻通常只有一个成员的值有效;
  • 枚举 enum:定义一组具名的整数常量;
  • 函数类型:描述函数的返回类型和参数类型。

3.3 指针类型

指针保存某个对象或函数的地址。例如:

c 复制代码
int value = 10;
int *p = &value;

p 的类型是"指向 int 的指针"。void * 是通用对象指针,可以与其他对象指针类型相互转换,但不能直接解引用,必须先转换为指向具体类型的指针。

3.4 空类型 void

void 表示"没有值"或"不指定具体对象类型",常见用法有:

c 复制代码
void print_message(void);  // 无返回值、无参数
void *buffer;              // 通用对象指针

不能定义普通的 void 变量,因为 void 是不完整类型,没有可供对象占用的确定大小。

4. 存储单位与类型大小

4.1 常见存储单位

  • 位(bit):最小的二进制信息单位,取值为 0 或 1;
  • 字节(byte):C 语言中 sizeof(char) == 1,通常一个字节为 8 位,但标准只保证至少 8 位;
  • KiB:1024 Byte
  • MiB:1024 KiB
  • GiB:1024 MiB

日常语境里常把 KB、MB、GB 用作上述二进制单位,但严格区分时,KB/MB/GB 也可能表示十进制的 1000 倍关系。

4.2 常见实现中的大小

类型 常见大小 标准保证的最小范围或关系
char 1 字节 恰好 1 个 C 字节,至少 8 位
short 2 字节 至少 16 位
int 4 字节 至少 16 位
long Windows 常为 4 字节,64 位 Linux 常为 8 字节 至少 32 位
long long 8 字节 至少 64 位
float 常为 4 字节 实现相关
double 常为 8 字节 精度不低于 float
指针 32 位平台常为 4 字节,64 位平台常为 8 字节 实现相关,不由所指类型决定

关键修正:不能笼统地说"基本类型大小固定"。C 标准只规定最小范围和若干大小关系,实际大小由平台、编译器和 ABI 决定。应使用 sizeof 实测,并用 <limits.h><stdint.h><float.h> 查询范围和特性。

c 复制代码
printf("sizeof(int) = %zu\n", sizeof(int));

5. 常量、变量与常变量

5.1 常量

常量是在表达式中直接表示固定值的记号。例如:

c 复制代码
123          // 十进制整数常量
0123         // 八进制整数常量
0x7B         // 十六进制整数常量
3.14         // 浮点常量,默认类型通常为 double
3.14f        // float 类型的浮点常量
'A'          // 字符常量
"ABC"        // 字符串字面量

字符串字面量本质上是一个以空字符 \0 结尾的字符数组。程序不应尝试修改字符串字面量。

5.2 变量

变量是具有类型和名字的对象,其存储的值可以在允许的情况下发生变化。

c 复制代码
int count;        // 定义,未显式初始化
int total = 10;   // 定义并初始化
total = 20;       // 赋值

变量定义完成后,名字与对应对象关联。编译器会根据对象的存储期、作用域和优化情况决定其具体存放位置;不能简单认为每个变量始终对应一个固定的物理内存地址。

5.3 初始化与赋值

  • 初始化:在对象定义时给出初始值,如 int x = 10;
  • 赋值:对象已经存在后,用赋值运算符改变其值,如 x = 20;

二者语法相似,但发生的时机和语言语义不同。

5.4 未初始化变量

c 复制代码
int x;              // 若为自动存储期的局部变量,其值不确定
static int y;       // 静态存储期对象会被零初始化
int global_value;   // 文件作用域对象也会被零初始化

读取未初始化的自动局部变量通常会导致未定义行为,不能概括为"通常是随机值",更不能依赖它恰好为 0。

5.5 常变量(只读对象)

使用 const 限定的对象不能通过该对象名再次赋值:

c 复制代码
const int max_count = 10;
// max_count = 20;  // 错误

const 表示只读约束,不等于编译期常量,也不保证对象一定存放在只读内存中。在标准 C 中,const int n = 10; 通常不能用于所有要求"整数常量表达式"的场景;枚举常量或宏更适合这种用途。

6. 录音问题解答

问题:常量和常变量有什么区别?

二者最核心的区别是:常量是值或字面记号,常变量是具有名字、类型和存储身份的只读对象。

对比项 常量 常变量
示例 10'A'3.14 const int n = 10;
是否有变量名 通常没有 有,例如 n
是否是对象 字面常量通常不是可修改对象 是对象,通常占有存储空间
能否取地址 一般不能对普通字面常量取地址 可以,如 &n
能否再次赋值 不存在"给字面量赋值" 不能通过该只读左值赋值
是否一定是编译期常量 字面整数常量通常可参与常量表达式 标准 C 中不一定
c 复制代码
const int n = 10;
const int *p = &n;  // 可以取得常变量的地址

一句话记忆:10 是常量;const int n = 10; 中,n 是被 const 限定的只读变量。

录音中的 {把这个} 上下文不完整,属于语音转写残片,无法还原为明确问题,因此没有强行补写答案。

7. 标识符与命名规范

7.1 语法规则

C 标识符由字母、数字和下划线组成,并且不能以数字开头,也不能与关键字相同。

text 复制代码
123abc   // 非法:数字开头
_abc     // 语法合法
abc_1    // 合法
abc-1    // 非法:包含减号

7.2 工程建议

  • 名字应表达用途,例如 summin_valuemaxCount
  • 同一项目保持一种命名风格;
  • 避免过短且无意义的名称,循环计数器等局部场景除外;
  • 避免自行使用保留标识符:文件作用域下以下划线开头的名字,以及任何位置以下划线加大写字母或双下划线开头的名字;
  • 用一致的缩进表达代码层次,通常使用 4 个空格或项目统一的 Tab 规则。

8. 左值、右值与变量访问

入门阶段可以这样理解:

c 复制代码
int x = 10;
int y = x;
x = 20;
  • x = 20 中左侧的 x 指定要写入的对象;
  • int y = x 中右侧的 x 需要读取其值。

更准确地说,左值是指定某个对象的表达式;右值是教学中常用的非正式说法,通常指表达式求得的值。左值不一定可修改,例如 const int x 产生的是不可修改左值。

9. 整数常量与进制

9.1 不同进制的写法

c 复制代码
int decimal = 123;       // 十进制
int octal = 0123;        // 八进制,等于十进制 83
int hexadecimal = 0x7B; // 十六进制,等于十进制 123
  • 八进制使用数字 07,所以 0864 非法;
  • 十六进制使用 09AF(不区分大小写);
  • 标准 C23 之前没有标准的 0b 二进制整数字面量,部分编译器把它作为扩展支持;C23 已加入二进制前缀。

9.2 二进制位权

一个 8 位无符号数的位权从高到低为:

text 复制代码
128 64 32 16 8 4 2 1

例如十进制 10:

text 复制代码
10 = 8 + 2 = 0000 1010₂

8 位可表示 2^8 = 256 种不同状态。无符号解释时范围为 0~255。

10. 有符号整数、无符号整数与补码

10.1 有符号与无符号

c 复制代码
signed int a = -1;
unsigned int b = 1U;
  • 无符号整数的所有位都参与数值表示,范围为 02^N - 1
  • 有符号整数需要同时表示正数、负数和 0;现代 C 实现通常使用二进制补码;
  • shortintlonglong long,省略 signed 时默认是有符号类型;
  • 普通 charsigned charunsigned char 是三个不同类型,普通 char 是否有符号由实现决定。

10.2 补码示例

内存中存储的到底是不是补码?

更准确的说法是:内存只存放 0 和 1 组成的位模式,位模式本身没有"正负"或"补码"标签;程序按照数据类型解释它。

  • 对主流现代计算机中的有符号整数,负数通常采用二进制补码表示;
  • 正数的补码与普通二进制表示相同;
  • 无符号整数按照纯二进制数解释全部位;
  • 浮点数通常使用 IEEE 754 格式,不能套用整数补码;
  • 字符、指针、结构体等也不能简单地说成"都以补码存储"。

因此,"整数在内存中以补码存储"是方便入门的简称,更严谨的表述应是:在常见平台上,有符号整数的对象表示采用补码;内存实际保存的仍然只是一串位。

采用补码有几个重要好处:

  1. 加法和减法可以共用同一套加法电路,a - b 可以转化为 a + (-b)
  2. 0 只有一种表示,不会同时出现"正零"和"负零";
  3. 固定位宽下的溢出可以自然丢弃最高位进位;
  4. 负数扩展到更宽位数时,只需复制最高位,就能保持数值不变。
如何计算一个负数的补码

先明确位宽。补码一定是"在 N 位范围内"的表示,不说明位宽就不完整。以 8 位为例:

text 复制代码
+10:0000 1010
-10:1111 0110
 -1:1111 1111

-10 的 8 位补码,可以对 10 的二进制逐位取反再加 1:

text 复制代码
0000 1010
1111 0101  // 取反
1111 0110  // 加 1

也可以使用更容易核对的公式:

text 复制代码
-x 的 N 位补码所对应的无符号数 = 2^N - x

-10 的 8 位补码:
2^8 - 10 = 256 - 10 = 246
246 = 1111 0110₂

反过来,看到一个最高位为 1 的 N 位补码时,可以这样求它表示的有符号值:

text 复制代码
无符号值 - 2^N

1111 0110₂ = 246
246 - 256 = -10

所以可以掌握两套等价方法:

  • 编码负数:绝对值写成二进制,逐位取反,再加 1;
  • 快速计算:用 2^N - 绝对值
  • 解码负数:减 1 后取反,再加负号;或直接用"无符号值减 2^N"。

课堂速算法适合帮助理解固定宽度补码,但写 C 程序时要从类型转换和运算规则判断结果,不要把所有位模式变化都简单归结为"最高位是符号位"。对无符号类型,最高位也是数值位。

10.3 符号扩展与零扩展

为什么有符号数补符号位,无符号数补 0?

扩展的目的不是机械地补位,而是:位宽变大以后,尽量保持原来的数值不变。

下面假设把一个 8 位数扩展到 16 位。原来的 8 位模式都是:

text 复制代码
1111 1011

同一串位由不同类型解释,会表示不同的数:

  • 作为 8 位有符号补码,它表示 -5
  • 作为 8 位无符号数,它表示 251

情况一:原类型是有符号数,数值为 -5。

为了让 16 位结果仍然表示 -5,高位必须补原最高位 1

text 复制代码
8 位:          1111 1011  = -5
符号扩展到 16 位:1111 1111 1111 1011  = -5

验证:这个 16 位模式的无符号值是 65531,按照 16 位补码解释:

text 复制代码
65531 - 2^16 = 65531 - 65536 = -5

如果错误地给 -5 高位补 0:

text 复制代码
0000 0000 1111 1011 = 251

数值就从 -5 变成了 251

情况二:原类型是无符号数,数值为 251。

无符号数的所有原始位都是数值位。为了让扩展后的数仍为 251,新增高位必须补 0:

text 复制代码
8 位:          1111 1011  = 251
零扩展到 16 位:0000 0000 1111 1011  = 251

如果错误地补 1,16 位无符号结果会变成 65531,不再是 251。

这也回答了"这里不也应该考虑寄存器的补码形式吗":确实需要考虑,而符号扩展恰好就是为了保持补码负数的值。 寄存器本身仍只保存位,不自带"补码"属性;CPU 指令以及编译器生成指令时所依据的 C 类型,决定这些位按有符号还是无符号方式参与扩展、比较和某些运算。

可以用下表记忆:

原数据类型与数值 8 位模式 扩展方式 16 位结果 数值是否保持
有符号 -5 1111 1011 复制最高位,补 1 1111 1111 1111 1011 是,仍为 -5
无符号 251 1111 1011 高位补 0 0000 0000 1111 1011 是,仍为 251

因此,较窄的整数转换成较宽类型时:

  • 非负、能表示的数值保持不变;
  • 在典型补码机器的位级直观中,有符号负数会进行符号扩展;
  • 无符号数扩宽时相当于高位补 0。

"有符号就补 1"并不完整。准确说法是"复制原最高位":正数最高位为 0,所以补 0;负数最高位为 1,所以补 1。

但 C 语言层面的正确分析方法仍然是使用"整数提升"和"类型转换规则"。"把小变量搬进 32 位寄存器"只是便于理解的硬件模型,不是每条 C 语句必然发生的固定步骤;CPU 寄存器宽度和实际指令由架构、编译器及优化决定。

11. 不同整数类型之间的赋值

11.1 宽类型转窄类型

c 复制代码
long value = 65536;
short s = (short)value;

如果目标类型无法表示原值:

  • 转为无符号类型时,结果按目标类型范围进行模 2^N 归约;
  • 转为有符号类型时,结果由实现定义,不能仅凭标准断言"一定保留低位"。在常见二进制补码平台上经常表现为截取低位。

若假设 short 为 16 位,常见平台上 65536 的低 16 位全为 0,因此 s 常见结果为 0。

11.2 窄类型转宽类型

c 复制代码
short s = -1;
long value = s;

只要目标类型能够表示原值,转换后数值保持不变,所以 value-1。若想表达 65535,应写成:

c 复制代码
unsigned short s = 65535;
unsigned long value = s; // 数值仍为 65535

课堂示例 short s = 65535; 在 16 位 short 上已经超出 signed short 的范围,其结果由实现定义,不能先假设 s 中保存的是正数 65535。

12. signed charunsigned char 示例

c 复制代码
unsigned char c1 = 255;
signed char c2 = 255;
  • unsigned char 为 8 位,c1 可以表示 255;
  • signed char 为 8 位,它通常只能表示 -128~127,255 无法表示;转换结果由实现定义,常见补码平台上 c2 的值为 -1;
  • c1c2 传给 printf 时会发生整数提升,典型结果分别提升为 int 值 255 和 -1。

建议显式匹配格式:

c 复制代码
printf("c1 = %u\n", (unsigned)c1);
printf("c2 = %d\n", (int)c2);

13. 常见易错点速查

  1. #include 是预处理包含,不是"保护文件"。
  2. 标准输出函数名是 printf,字符串引号和右括号必须成对。
  3. 基本类型的具体字节数并非全部由 C 标准固定。
  4. 指针大小取决于平台和 ABI,不取决于它指向基本类型还是结构体。
  5. 未初始化自动局部变量的值不可靠,读取它通常是未定义行为。
  6. const 对象是只读对象,不一定是编译期常量。
  7. 普通 char 的符号性由实现决定。
  8. 向较窄有符号类型转换、或把超范围值赋给有符号类型时,不要想当然地认为结果一定是"截断低位"。
  9. 有符号整数溢出是未定义行为;无符号整数运算按模 2^N 回绕。
  10. 使用 sizeof<limits.h><stdint.h>,不要把某个平台的经验当作所有平台的规则。

14. 最小验证程序

c 复制代码
#include <limits.h>
#include <stdio.h>

int main(void)
{
    const int answer = 42;

    printf("answer = %d\n", answer);
    printf("CHAR_BIT = %d\n", CHAR_BIT);
    printf("sizeof(char) = %zu\n", sizeof(char));
    printf("sizeof(short) = %zu\n", sizeof(short));
    printf("sizeof(int) = %zu\n", sizeof(int));
    printf("sizeof(long) = %zu\n", sizeof(long));
    printf("sizeof(long long) = %zu\n", sizeof(long long));
    printf("sizeof(void *) = %zu\n", sizeof(void *));

    return 0;
}

15. 本节复习提纲

  • 能解释 #includemainprintfreturn 0 的作用;
  • 能说出基本类型、构造类型、指针类型和 void 的用途;
  • 明白类型大小依赖实现,应通过 sizeof 检查;
  • 能区分常量、变量、初始化、赋值和 const 常变量;
  • 掌握标识符规则和基本命名规范;
  • 能读写十进制、八进制和十六进制整数常量;
  • 理解无符号范围、补码直观、整数提升与扩宽转换;
  • 知道窄化转换、超范围赋值和有符号溢出是高风险易错点。
相关推荐
君顾119 分钟前
从0到1课后辅导管理系统开发实战:需求文档、架构设计全复盘
java·开发语言
xx~t19 分钟前
嵌入式学习——进程与线程2
linux·c语言·学习·进程与线程
神仙别闹20 分钟前
基于C++ MFC RSA 实现加密程序
开发语言·c++·mfc
增量星球36 分钟前
一个 Python 脚本 + LLM 怎么替代向量检索?ProjQA 技能架构原理深度解析
开发语言·python·架构·embedding·skill
探数API小喇叭1 小时前
基站查询 API 怎么用?LAC、CELLID 参数详解与实战】
java·开发语言·api·基站定位
Mr.Lu ‍1 小时前
C++开发,使用openCV API对图片进行压缩
开发语言·c++·opencv
z落落1 小时前
C# WinForm NModbus 串口通信+WinForm Modbus4 串口通信
开发语言·单片机·c#
Mojitocean1 小时前
Win开发环境配置(持续更新)
开发语言·python
CoderIsArt1 小时前
C#环境中部署MQTT客户端
开发语言·c#