C语言手记2

变量的范围整型如何推导?

在C语言中,整型变量的取值范围由其数据类型 (如 intshortlong)以及是否有符号signedunsigned)决定。取值范围本质上是该类型所能表示的二进制位数的结果。

1. 基本推导原理

  • 无符号整型 :所有位都用于表示数值,范围是 02^n - 1,其中 n 是该类型占用的位数。

  • 有符号整型 :通常使用补码 表示,最高位为符号位,范围是 -2^(n-1)2^(n-1) - 1

例如,一个 unsigned int32 位,其范围就是 02^32 - 1(即 04294967295)。而 signed int 的范围则是 -2^312^31 - 1(即 -21474836482147483647)。

2. 不同平台的位数差异

C标准只规定了每种类型的最小范围(例如 int 至少占16位),但具体位数由编译器和硬件平台决定。常见情况:

  • 在16位系统上,int 通常占16位。

  • 在32/64位系统上,int 通常占32位。

要获取确切范围,可以查看 <limits.h> 头文件中定义的宏:

  • INT_MININT_MAXint 的最小、最大值。

  • UINT_MAXunsigned int 的最大值。

  • 类似地,SHRT_MINSHRT_MAXLONG_MINLONG_MAX 等。

3. 如何推导具体数值

如果您知道类型的位数,就可以直接计算:

  • 对于 n 位无符号整型:最小值 0,最大值 2^n - 1

  • 对于 n 位有符号整型(补码):最小值 -2^(n-1),最大值 2^(n-1) - 1

例如,题目中 RT地址 字段占 5位 ,且是作为无符号数处理,所以其范围是 02^5 - 1 = 31。这正是题目中取值范围 0~31 的来源。

4. 为什么题目中的字段范围是 0~31?

因为 5 位二进制可以表示的最大值是 11111(二进制)= 31(十进制)。当所有位都是 1 时,得到最大值 2^5 - 1 = 31。所以设计时用 5 位刚好可以表示 0 到 31 的地址空间。

类似地,数据字计数字段也是 5 位,因此其存储的值范围也是 0~31。但特殊规则将 0 映射为实际长度 32,1~31 映射为对应长度。

5. 推导练习

  • unsigned char 通常占 8 位,范围:0 ~ 2^8 - 1 = 0 ~ 255。

  • signed char 占 8 位,范围:-128 ~ 127(因为 -2^7 = -128, 2^7 - 1 = 127)。

  • unsigned short 通常占 16 位,范围:0 ~ 65535。

  • signed short 占 16 位,范围:-32768 ~ 32767。

通过理解位数与范围的关系,您可以轻松推导任何整型变量的取值范围。

整型变量的取值范围由两个因素决定:该类型占用的位数是否有符号。推导的基本公式如下:

1. 无符号整型

所有位都用于表示数值,范围是 02ⁿ - 1,其中 n 是位数。

推导示例

  • unsigned char 通常占 8 位 → 范围:0 ~ 2⁸ - 1 = 0 ~ 255

  • unsigned short 通常占 16 位 → 范围:0 ~ 2¹⁶ - 1 = 0 ~ 65535

  • unsigned int 通常占 32 位 → 范围:0 ~ 2³² - 1 = 0 ~ 4294967295

2. 有符号整型(使用补码表示)

最高位为符号位(0 正,1 负),其余 n-1 位表示数值。补码的特点是:

  • 正数:原码即本身,范围 0 ~ 2ⁿ⁻¹ - 1

  • 负数:用补码表示,范围 -2ⁿ⁻¹ ~ -1

因此有符号整型的范围是 -2ⁿ⁻¹2ⁿ⁻¹ - 1

推导示例

  • signed char 占 8 位 → 范围:-2⁷ ~ 2⁷ - 1 = -128 ~ 127

  • signed short 占 16 位 → 范围:-2¹⁵ ~ 2¹⁵ - 1 = -32768 ~ 32767

  • signed int 占 32 位 → 范围:-2³¹ ~ 2³¹ - 1 = -2147483648 ~ 2147483647

3. 为什么负数比正数多一个?

因为 0 被归入正数范围,所以正数部分从 0 到 2ⁿ⁻¹ - 1 共 2ⁿ⁻¹ 个数,而负数从 -1 到 -2ⁿ⁻¹ 也是 2ⁿ⁻¹ 个数,总数正好 2ⁿ 个。

4. 实际获取范围的方法

C 标准库的 <limits.h> 头文件中定义了各类型的最大值和最小值宏,例如:

  • INT_MIN, INT_MAX 对应 int

  • UINT_MAX 对应 unsigned int

  • SHRT_MIN, SHRT_MAX 对应 short

  • LONG_MIN, LONG_MAX 对应 long

可以直接使用这些宏来获取当前平台的确切范围,避免手动推导(因为不同平台位数可能不同)。例如:

c

复制代码
printf("int 范围:%d 到 %d\n", INT_MIN, INT_MAX);

5. 应用到题目中的字段

题目中的 RT 地址、字地址、计数字段都是 5 位无符号数,所以范围是 0 ~ 2⁵ - 1 = 0 ~ 31。这就是为什么取值范围被限定在 0~31 的原因。

理解这些基本公式,你就可以轻松推导任何整型变量的取值范围。


内存是怎么存的?

在C语言中,变量在内存中的存储方式取决于数据类型字节序(endianness) 以及编译器的内存对齐规则。针对你正在编写的1553命令字程序,理解内存存储有助于掌握位运算的实际效果。


1. 基本存储单位:字节

内存的最小寻址单位是字节(byte),每个字节有唯一的地址。一个字节由8个二进制位(bit)组成,bit7是最高位(MSB),bit0是最低位(LSB)。

例如,unsigned char a = 0x5A; 在内存中占1个字节,二进制为 01011010,存储如下:

text

复制代码
地址:0x1000   值:01011010

2. 多字节数据的存储:字节序

当数据占多个字节(如 unsigned short 占2字节,int 占4字节)时,存在两种存储顺序:

  • 大端序(Big-endian):高位字节存于低地址,低位字节存于高地址。

  • 小端序(Little-endian):低位字节存于低地址,高位字节存于高地址。

例如,16位命令字 0xD500(二进制 1101 0101 0000 0000):

  • 高8位 = 0xD511010101

  • 低8位 = 0x0000000000

在不同字节序下的内存布局:

地址偏移 大端序 小端序
0 0xD5 0x00
1 0x00 0xD5

大多数x86/x64 PC采用小端序,而网络协议、某些嵌入式系统(如1553B总线本身)可能采用大端序。但注意:1553B命令字是一个逻辑上的16位字,其位定义(bit15~bit0)是固定的;在内存中如何存放由CPU决定,但位运算操作的是逻辑值,不受字节序影响------因为C语言的位运算是在寄存器或变量的逻辑值上进行的,编译器会根据平台自动处理字节序。


3. 变量在内存中的实际存储

当你声明一个变量 unsigned short cmd = 0xD500;,编译器会分配2字节的内存,并按照当前平台的字节序存储这两个字节。然后,当你使用位运算(如 cmd >> 8)时,CPU会从内存中取出这两字节,组合成16位逻辑值,再进行移位操作。因此,位运算的结果与字节序无关,只与变量的逻辑值有关。


4. 位运算如何影响内存中的位?

位运算直接操作变量的二进制位(逻辑位),而不是物理内存地址。例如:

  • cmd >> 11:将逻辑值的所有位右移11位,左边补0。

  • cmd & 0x1F:取逻辑值的低5位。

这些操作在CPU的寄存器中完成,最终结果再存回内存。因此,你无需关心内存中字节的实际排列,只需按照逻辑位定义进行运算即可。


5. 1553命令字的内存存储示例

假设你在小端序机器上运行程序,合成命令字得到 0x2D00(示例的正确值)。内存中:

  • 低地址字节:0x00

  • 高地址字节:0x2D

    当你输出时,printf("0x%04X", cmd); 会以逻辑值 0x2D00 显示,与实际内存布局无关。

在拆解函数中,从键盘读入 0xD500 后,变量 cmd 的逻辑值就是 0xD500,位运算提取的字段也是基于这个逻辑值。


6. 总结

  • 内存以字节为单位,多字节数据有字节序之分,但C语言的位运算基于逻辑值,不受字节序影响。

  • 你只需按照题目定义的逻辑位范围(bit15~bit0)进行移位和掩码操作,无需关心内存物理存储。

  • 理解这一点后,就可以专注于位运算的正确性,而不用担心不同机器上的结果差异。

如果还有具体的存储细节疑问(例如数组、结构体的内存对齐),可以进一步提问。


32位,64位,操作系统是什么?

32位64位是指计算机处理器(CPU)一次能处理的数据位数,也指操作系统和软件所支持的指令集架构。简单来说,它们决定了计算机能够使用的内存大小和数据处理能力。

1. 基本概念

  • 32位系统 :CPU一次可以处理32位(4字节)的数据。内存寻址空间最大为 2³² = 4GB(实际可用可能略少)。

  • 64位系统 :CPU一次可以处理64位(8字节)的数据。内存寻址空间巨大(理论最大 2⁶⁴ ≈ 16EB,实际受硬件和操作系统限制,但远大于4GB)。

2. 操作系统与硬件的关系

  • 如果CPU是64位的,可以安装32位或64位的操作系统。

  • 如果CPU是32位的,只能安装32位的操作系统。

  • 64位操作系统通常可以运行32位软件(通过兼容模式),但32位操作系统无法运行64位软件。

3. 对用户的影响

  • 内存支持:32位系统最多只能使用约3.25~3.5GB内存(即使物理内存更大也无法充分利用)。64位系统可以支持4GB以上内存,甚至TB级别。

  • 软件兼容性:一些新软件(如大型游戏、专业设计软件)可能只提供64位版本,因为64位能处理更大数据量,性能更强。

  • 驱动要求:64位系统需要64位设备驱动,旧硬件可能没有64位驱动。

4. 如何查看自己的操作系统位数?

Windows

  • 右键"此电脑" → 属性 → 在"系统类型"中查看(例如"64位操作系统,基于x64的处理器")。

Linux

  • 在终端输入 uname -m:若输出 x86_64 表示64位,i386/i686 表示32位。

macOS

  • 较新版本都是64位。点击左上角苹果图标 → "关于本机" → "系统报告"中查看。

5. 编程角度的意义

  • 在C语言中,int 类型通常为32位(4字节),但指针的大小会因系统而异:32位系统指针为4字节,64位系统指针为8字节。这就是为什么之前讨论的变量范围依赖于位数。

  • 如果你在64位系统上编程,默认编译出的可执行文件可能是64位的,但也可以编译成32位(需要安装相应工具链)。


int a=1,在内存中存放的顺序?大小端是什么?,为什么会有大小端,window和Linux存放的异同?

1. 什么是字节序?

字节序是指多字节数据在内存中存储时,其各个字节的排列顺序 。对于 int a = 1,它占4字节(假设int为32位),十六进制表示为 0x00000001。这4个字节从高到低分别是:0x00(最高位字节)、0x000x000x01(最低位字节)。

字节序分为两种:

  • 大端序(Big-endian) :将高位字节 存放在低地址 ,低位字节存放在高地址。

    内存布局:低地址 → 高地址:0x00 0x00 0x00 0x01

  • 小端序(Little-endian) :将低位字节 存放在低地址 ,高位字节存放在高地址。

    内存布局:低地址 → 高地址:0x01 0x00 0x00 0x00


2. int a = 1 的存储示例

假设 a 的起始内存地址为 0x1000

地址 大端序存储 小端序存储
0x1000 0x00 0x01
0x1001 0x00 0x00
0x1002 0x00 0x00
0x1003 0x01 0x00

可以看到,在小端序中,最低有效字节 0x01 存放在最低地址,所以读取该地址时直接看到的就是 1;而大端序则把最高有效字节放在最低地址。


3. 为什么会有大小端之分?

字节序的产生源于计算机硬件设计的历史差异,主要有两个原因:

  • 便利性:不同CPU制造商在设计芯片时,为了优化内部数据通路或简化电路,选择了不同的存储顺序。例如,Intel的x86架构采用小端序,而早期的Motorola 68000系列采用大端序。

  • 兼容性 :某些网络协议(如TCP/IP)规定使用大端序(称为网络字节序),以确保不同架构的设备能正确通信。因此,编程时常使用 htonlntohl 等函数进行转换。

没有绝对的优劣 ,小端序在做数据类型的强制转换(如将int转为char)时更直观(低位地址就是低位字节),而大端序更符合人类阅读数字的习惯(高位在前)。


4. Windows和Linux存放的异同

关键点 :字节序是由CPU架构决定的,而不是由操作系统决定的。因此,Windows和Linux的字节序异同取决于它们所运行的硬件平台。

  • x86 / x86-64 架构 (如Intel、AMD处理器):

    无论运行Windows还是Linux,都是小端序。因为x86系列从诞生起就采用小端序。

  • ARM 架构

    ARM处理器是双端序(bi-endian) ,即可以通过硬件配置或操作系统设置切换为大端或小端。但大多数嵌入式设备和手机(运行Linux或Android)默认使用小端序,以兼容主流软件。

  • 其他架构

    一些老式服务器(如IBM Power)可能默认大端序,但现在很多也支持切换。

结论

  • 同一硬件平台 上(比如都运行在x86 PC上),Windows和Linux对 int a = 1 的存储完全相同(小端序)。

  • 如果Linux运行在非x86硬件上(如ARM路由器、MIPS开发板),则可能不同,但现代主流设备基本都统一为小端序以简化软件兼容性。

因此,操作系统本身并不决定字节序,硬件才是根本。编程时如果需要跨平台处理二进制数据(如文件、网络包),应使用明确的大小端转换函数,而不是依赖特定平台。


任何东西是如何变成编码的?

计算机只能处理二进制数据(0和1),所以现实世界中的任何信息(文字、图像、声音、视频等)都必须转换成二进制形式才能被计算机存储、处理和传输。这个转换过程就是编码

1. 基本思路

  • 首先为每种信息定义一个符号集(例如字母、数字、颜色、声音幅度等)。

  • 然后给每个符号分配一个唯一的数字编号(即代码点)。

  • 最后将这些数字用二进制表示,并按照一定的规则组织起来。

2. 常见信息的编码方式

数字

  • 整数:直接用二进制表示(如 5101),但需约定位数(如32位)。负数用补码表示。

  • 小数:采用浮点数标准(如 IEEE 754),将数字拆分为符号位、指数和尾数,存入固定长度的二进制中。

字符(文字)

  • ASCII:最早的字符编码,用7位二进制表示128个字符(英文字母、数字、符号)。

  • Unicode :统一全球所有文字的编码,为每个字符分配一个唯一的数字(如"中"的 Unicode 是 20013)。但实际存储需要进一步编码,如:

    • UTF-8:变长编码,英文字符占1字节(兼容ASCII),中文占3字节。

    • UTF-16:常用2字节或4字节表示一个字符。

图像

  • 位图(BMP):每个像素用RGB三个颜色分量表示,每个分量通常8位(0~255)。整张图就是像素阵列。

  • 压缩格式(JPEG、PNG):利用人眼特性,通过数学变换(如离散余弦变换)减少数据量,但解码后仍还原为像素。

音频

  • 采样:对连续的声音波形按一定频率(如44.1kHz)测量其振幅。

  • 量化:将振幅用二进制数表示(如16位,范围-32768~32767)。得到的就是PCM(脉冲编码调制)数据。

  • 压缩(MP3、AAC):去除人耳不敏感的部分,进一步缩小数据量。

视频

  • 由连续图像帧(每秒几十张)加上音频组成。

  • 利用帧间相似性进行压缩(如H.264),只记录变化的部分,极大减小体积。

3. 为什么需要统一标准?

如果编码方式不统一,不同设备之间就无法交换数据。比如,一个用ASCII编码的文件在中文系统上可能显示乱码,就是因为系统使用了不同的编码方式(如GBK)去解释。因此,国际标准(如Unicode)确保了跨平台的一致性。

4. 总结

编码就是将信息映射为二进制数字的过程,它必须遵循事先约定好的规则(标准)。计算机通过这些规则,将二进制数据还原成我们能理解的信息(解码)。从文本到多媒体,每种信息都有适合的编码方式,平衡了表示精度、存储空间和处理效率。


数组的结构,类型和维度

在C语言中,数组 是一种数据结构,用于存储固定数量相同类型 的元素。这些元素在内存中连续存放,可以通过索引(下标)快速访问。

1. 数组的结构

  • 连续内存 :数组的所有元素在内存中是一片连续的存储区域。例如,int a[5]; 会占用 5 × sizeof(int) 字节的连续空间。

  • 元素索引 :每个元素通过下标访问,下标从 0 开始。a[0] 是第一个元素,a[4] 是最后一个。

  • 数组名 :在大多数上下文中,数组名代表数组首元素的地址(指针常量)。例如 a 等价于 &a[0]

2. 数组的类型

数组的类型由元素类型元素个数共同决定。例如:

  • int a[5]; → 类型是 int [5](含5个int的数组)

  • char b[10]; → 类型是 char [10]

注意:int [5]int [10] 是两种不同的类型,即使元素类型都是 int

3. 数组的维度

维度指数组的"层数":

  • 一维数组 :线性结构,如 int arr[10];

  • 二维数组 :可看作"数组的数组",如 int matrix[3][4]; 表示3行4列,内存中仍是连续存放(行优先)。

  • 多维数组 :依此类推,如三维 int cube[2][3][4];

声明语法:类型 数组名[维度1][维度2]...;

示例

c

复制代码
#include <stdio.h>

int main() {
    // 一维数组
    int arr[5] = {1, 2, 3, 4, 5};
    printf("arr[2] = %d\n", arr[2]);

    // 二维数组
    int mat[2][3] = {{1,2,3}, {4,5,6}};
    printf("mat[1][2] = %d\n", mat[1][2]);  // 输出 6

    // 数组的大小(总字节数)
    printf("sizeof(arr) = %zu\n", sizeof(arr));     // 5 * sizeof(int)
    printf("sizeof(mat) = %zu\n", sizeof(mat));     // 2*3 * sizeof(int)

    return 0;
}

4. 关键特性

  • 数组名不能自增 :因为它是常量指针,不能执行 arr++

  • 数组作为函数参数时会退化为指针 :所以函数内无法直接通过 sizeof 获取数组长度,需要额外传递长度参数。

  • 初始化方式:可以用花括号列表初始化,未指定的元素自动初始化为0(静态存储期)或不确定值(局部自动变量)。

理解数组的结构、类型和维度是掌握C语言内存操作的基础,后续的指针、字符串和动态内存分配都与此密切相关。


C语言常用关键字并介绍

1. 数据类型关键字

  • char:字符型,占1字节,用于存储单个字符或小整数。

  • short:短整型,通常占2字节。

  • int:整型,通常占4字节(依平台而定)。

  • long:长整型,通常占4或8字节。

  • float:单精度浮点型,占4字节。

  • double:双精度浮点型,占8字节。

  • void :无类型,常用于函数无返回值或通用指针(void*)。

  • signed:有符号修饰符(默认),可省略。

  • unsigned:无符号修饰符,表示非负数。

2. 控制语句关键字

  • ifelse:条件分支。

  • switchcasedefault:多分支选择。

  • forwhiledo:循环结构。

  • break :跳出当前循环或switch

  • continue:结束本次循环,开始下一次迭代。

  • return:从函数返回,可携带返回值。

  • goto:无条件跳转(应谨慎使用)。

3. 存储类关键字

  • auto:自动变量(默认,局部变量隐含auto),几乎不用显式写出。

  • register:建议编译器将变量存储在寄存器中(现代编译器优化已很少需要)。

  • static:静态变量,延长生命周期至程序结束,且限制作用域(文件内或函数内)。

  • extern:声明变量或函数在其他文件中定义,用于多文件编程。

  • const:定义只读变量(常量),其值不能被修改。

  • volatile:告诉编译器该变量可能被意外修改(如硬件寄存器),禁止优化。

4. 其他关键字

  • sizeof:运算符(也是关键字),用于获取类型或变量所占字节数。

  • typedef :为已有类型起别名,简化类型书写(如typedef int INT32;)。

  • enum:枚举类型,定义一组命名的整数常量。

  • struct:结构体类型,将多个不同类型的数据组合在一起。

  • union:联合体类型,所有成员共享同一段内存。

  • inline(C99):建议编译器将函数内联展开,减少调用开销。

5. C99/C11新增常用关键字

  • _Bool :布尔类型,需包含stdbool.h后可用bool

  • _Complex_Imaginary:复数类型(很少用)。

  • _Alignas_Alignof:对齐控制(C11)。

  • _Static_assert:静态断言(C11)。


C标准库函数

C语言的"内置函数"通常指C标准库函数,它们不是语言核心的一部分,而是由编译器提供的标准库实现。使用这些函数需要包含相应的头文件。下面分类介绍常用的标准库函数。


1. 输入输出函数(<stdio.h>

  • printf():格式化输出到标准输出(屏幕)。

  • scanf():从标准输入读取格式化数据。

  • getchar() / putchar():读/写单个字符。

  • gets() / puts() :读/写一行字符串(gets不安全,已弃用)。

  • fopen()fclose():文件打开与关闭。

  • fprintf()fscanf():格式化文件读写。

  • fgetc()fputc():文件字符读写。

  • fgets()fputs():文件行读写。

  • fread()fwrite():二进制文件块读写。

2. 标准库通用函数(<stdlib.h>

  • malloc()calloc()realloc()free():动态内存分配与释放。

  • atoi()atol()atof():字符串转整数、长整数、浮点数。

  • rand()srand():生成伪随机数及设置种子。

  • exit()abort():程序终止。

  • system():执行系统命令。

  • qsort()bsearch():快速排序和二分查找。

  • abs()labs():整数绝对值。

3. 字符串处理函数(<string.h>

  • strlen() :计算字符串长度(不含 \0)。

  • strcpy()strncpy():字符串复制。

  • strcat()strncat():字符串连接。

  • strcmp()strncmp():字符串比较。

  • strchr()strrchr():在字符串中查找字符。

  • strstr():查找子串。

  • strtok():分割字符串。

  • memcpy()memmove()memset()memcmp():内存块操作。

4. 数学函数(<math.h>

  • sin()cos()tan():三角函数。

  • asin()acos()atan():反三角函数。

  • exp()log()log10():指数与对数。

  • pow():幂运算。

  • sqrt():平方根。

  • fabs():浮点数绝对值。

  • ceil()floor():向上取整、向下取整。

  • fmod():浮点数取余。

  • 编译时可能需要链接数学库 -lm

5. 字符分类与转换(<ctype.h>

  • isdigit()isalpha()isalnum():判断数字、字母、字母数字。

  • islower()isupper():判断大小写。

  • isspace():判断空白字符。

  • toupper()tolower():大小写转换。

6. 时间与日期(<time.h>

  • time():获取当前日历时间。

  • clock():获取程序运行的处理器时间。

  • difftime():计算时间差。

  • strftime():格式化时间字符串。

  • localtime()gmtime():将时间戳转换为本地时间或UTC时间。

7. 错误处理(<errno.h><assert.h>

  • errno:全局变量,记录错误码。

  • perror():输出错误信息。

  • assert():运行时断言,若条件为假则终止程序(调试用)。

8. 可变参数处理(<stdarg.h>

  • va_listva_start()va_arg()va_end() :实现可变参数函数(如 printf 内部使用)。

注意事项

  • 这些函数不是C语言的关键字,而是库函数,必须包含对应头文件才能使用。

  • 某些函数在不同平台或编译器下可能有行为差异,建议查阅标准文档或手册(如 man 3 printf)。

  • 使用动态内存函数(malloc等)需注意配对释放,避免内存泄漏。

相关推荐
乱世军军2 小时前
把 Python 3.13 降级到 3.11
开发语言·python
fy121632 小时前
GO 快速升级Go版本
开发语言·redis·golang
共享家95272 小时前
Java入门(String类)
java·开发语言
0xDevNull2 小时前
Spring Boot 循环依赖解决方案完全指南
java·开发语言·spring
bbq粉刷匠2 小时前
Java--多线程--单例模式
java·开发语言·单例模式
dfafadfadfafa2 小时前
嵌入式C++安全编码
开发语言·c++·算法
计算机安禾2 小时前
【C语言程序设计】第34篇:文件的概念与文件指针
c语言·开发语言·数据结构·c++·算法·visual studio code·visual studio
弦有三种苦难3 小时前
CCF-202412-T3缓存模拟90分
java·开发语言·spring
会编程的土豆3 小时前
【数据结构与算法】 二叉树做题
开发语言·数据结构·c++·算法