1.基本数据类型和范围
C语言的基本数据类型包括char
、int
、float
和double
。
-
char
:通常用于存储字符,其范围取决于编译器和系统,但通常为-128到127(以ASCII码为例)。 -
int
:用于存储整数,其范围取决于编译器和操作系统,通常为-32,768到32,767(以16位整数为例)。 -
float
和double
:用于存储浮点数,其范围非常大,但具体数值取决于IEEE 754标准。
2.计算机如何存储浮点数的
C++中,浮点数的存储分为两种,float(单精度)和double(双精度)。
float占4字节,32位,double占8字节,64位,二者在内存中的存储形式如下:
无论是单精度还是双精度在存储中都分为三个部分:
1. 符号位(Sign) : 0代表正,1代表为负 2. 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储 3. 尾数部分(Mantissa):尾数部分 存储规定:
按照国际标准IEEE 754,任意一个进制浮点数V可以表现成下面的形式:
V = (-1)^S * M * 2^E
其中 (-1)^S 表示有效数字,当S为0时,V为正数,S为1时,V是负数。 M表示有效数字,范围规定:大于1,小于2。 2^E表示的是指数位。 要将一个浮点数写成进制形式:一定需要S、M、E的值。
例如: 5.5 进制形式:
先将整数部分与小数部分分开换算: 整数: 101 小数: 0.1 小数点后面 2^-1 = 1/2 = 0.5
由此可以看出:V = (-1)^S * M * 2^E
5.5 --> 101.1 —> 1.011 * 2^2 所以 S = 0,M = 1.011,E = 2;
因为以上知道,
1.M是有效数字,范围:1<=M<2,在计算机内存保存M时,默认这个数第一位总是1,可以被舍去(在读取的时候,再把第一位加上),只保 留小数点后面的;如:当保留 101.1 时:只保存了 011,为了更方便理解.
2.E 是一个无符号数字(unsigned int)。 因为在8位的时候,其有范围0 ~ 255;而在11位的时候,其范围是 0 ~ 2047,但是由科学计数法知道,这时可以有负数的,(当存入 0.5 时,E = -1),所以在IEEE 754规定:存入内存时E的真实值需要加上一个中间值,对于8位来说其中间值是 127 ,对于11位来说其中间值是 1024. 对于指数E从内存中取出分为三种: 1.当E全为0时:也就是说指数E的真实值等于 -127,有效数字M不再加上1,而是0.xxxxxx这样的小数,这样做的目的是为了表示这样几乎很小很小的数,接近与0。 2.当E全为1时:也就是说这样的数表示无穷大(正负取决于S)。 3.当E有1有0时:即指数E的计算值减去127(或1024)得到真实值,然后由规定正数部分必须为1,(即M取出后加1)。
3.全局变量、局部变量、模块变量在内存空间中如何存放?
全局变量在全局空间分配。
局部变量在栈空间分配。
模块变量(通常指静态变量)也在全局空间分配,但其作用范围限制在定义它的模块内
4.内存详细分区
内存(在计算机科学中通常指的是主存储器或随机存取存储器,即RAM)在逻辑上可以被划分为几个不同的区域或段,这些区域或段用于不同的目的和存储不同的数据类型。然而,具体的分区方式可能因操作系统、处理器架构和应用程序的不同而有所差异。以下是一些常见的内存分区:
-
代码区(Code or Text Segment)
-
存储程序执行的机器代码(即编译后的二进制代码)。
-
通常是只读的,以防止程序意外修改自己的代码。
-
-
数据区(Data Segment)
-
通常分为两个子区:初始化数据区(Initialized Data)和未初始化数据区(Uninitialized Data 或 BSS)。
-
初始化数据区:存储程序中已明确赋值的全局变量和静态变量。
-
未初始化数据区(BSS):存储未明确赋值的全局变量和静态变量,这些变量在程序开始执行前被初始化为零(或空)。
-
-
堆区(Heap)
-
用于动态内存分配。当程序员使用如
malloc
、calloc
、new
等函数在运行时请求内存时,这些内存通常从堆中分配。 -
程序员负责在不再需要这些内存时释放它们(使用如
free
、delete
等函数),否则可能会导致内存泄漏。
-
-
栈区(Stack)
-
用于存储局部变量和函数调用的信息(如返回地址、参数等)。
-
由编译器自动分配和释放,其操作方式遵循“后进先出”(LIFO)的原则。
-
-
静态存储区
-
通常与数据区重叠,但强调存储的是静态数据(即全局变量和静态变量)。
-
这些变量的生命周期是整个程序的执行期间。
-
-
映射文件或共享内存区
-
在某些操作系统中,可以将文件或其他对象的内存映射到进程的地址空间中。
-
共享内存允许多个进程访问同一块物理内存,从而实现进程间的通信和数据共享。
-
-
其他内存区
-
根据特定的操作系统、处理器架构或应用程序需求,还可能存在其他特定的内存区,如内存映射的I/O设备、图形缓冲区等。
-
5.内存的最小存储单位和最小计量单位分别是什么?
-
内存的最小存储单位是二进制位(bit)。
-
内存的最小计量单位是字节(byte),通常8位组成1字节。
6.函数指针与递归
-
什么是函数指针?
-
函数指针是指向函数的指针变量,可以用来调用函数或传递函数作为参数。
-
-
解释递归函数
-
递归函数是一个可以调用自身的函数。递归函数是一个可以调用自身的函数。在递归函数中,函数会反复调用自己,直到满足终止条件为止。
-
7.什么是结构体?
-
结构体是一种用户自定义的数据类型,它可以包含多个不同类型的变量,这些变量可以通过一个结构名来访问。
8.C语言中如何进行字符串操作?
-
C语言提供了一系列字符串库函数,如
strcpy()
、strcat()
、strlen()
等,用于复制、连接、计算字符串长度等操作。
9.结构体为什么要内存对齐?
-
结构体内存对齐是计算机内存管理中的一个重要概念,它对于程序的性能和正确性有着重要影响。以下是结构体内存对齐的主要原因:
-
性能优化: CPU访问内存时,通常会以一定的块大小(例如4字节、8字节等)为单位进行读取。如果结构体中的数据成员没有对齐到这样的块边界,CPU可能需要执行额外的操作(如两次内存访问)来读取一个成员,这称为“未对齐访问”。未对齐访问会降低CPU的访问效率,增加程序的执行时间。 内存对齐可以确保结构体中的数据成员都位于合适的块边界上,从而提高CPU的访问效率。
-
硬件限制: 某些硬件平台对内存访问有严格的对齐要求。如果结构体中的数据成员没有正确对齐,硬件可能会抛出异常或产生错误的结果。 通过内存对齐,可以确保结构体中的数据成员满足硬件平台的对齐要求,从而避免潜在的硬件问题。
-
可移植性: 不同的编译器和硬件平台可能对内存对齐有不同的要求。如果不进行内存对齐,同一个结构体在不同的编译器或硬件平台上可能会有不同的内存布局和大小。 通过使用标准的数据类型和编译器特定的对齐指令(如#pragma pack),可以确保结构体在不同平台上的内存布局和大小一致,从而提高程序的可移植性。
-
空间利用率: 虽然内存对齐可能会增加结构体所占用的总内存空间(因为编译器会在成员之间插入填充字节以确保对齐),但在某些情况下,这可以提高内存访问的效率并减少缓存未命中的可能性。 此外,合理的内存对齐还可以帮助减少内存碎片,提高内存管理的效率。
-
简化编译器实现: 对于编译器来说,处理未对齐的内存访问需要额外的逻辑和复杂