第一部分:基础概念
1.添加static之后作用:(1)函数内,static+变量=常量;(2)函数外,模块内,static+变量=全局变量;(3) static+函数,限制在本地模块的本地范围内使用。
2.引用与指针的不同:引用不可指向空,引用不可改,指针都可以。
3.堆栈溢出的原因:(1)申请的动态内存没释放;(2)递归调用的层数太高。
4.switch的参数类型不能是小数。
5.程序的内存分配:(1)栈区;(2)堆区;(3)全局区;(4)文字常量区;(5)代码区。
6.全局变量与局部变量的在内存中区别是?全局变量是在全局区,局部变量在栈区。
7.全局变量、局部变量与函数添加static前后的区别是什么?
一、static修饰全局变量
- 正常全局变量就是静态存储,加上static还是静态存储,区别在作用域。正常全局变量的作用域是整个源文件,使用extern表示,加上static修饰作用域限制在一个源程序中,static全局变量只能初始化一次。
二、static修饰局部变量
- 两者的主要区别在于存储方式。局部变量本身是存储在栈区,加上static后存储在静态区,static局部变量只能被初始化一次。重复调用一个包含static修饰变量的函数,调用的用一个的变量。
三、static修饰函数
- 这两者的区别是作用域。static函数只能在本程序代码中调用。不能在其他的程序代码中调用,哪怕是在同一个源文件都不行。 static函数在内存中只有一份。,普通函数每次调用都会存一份。
【源程序是指实际的代码,而源文件是保存代码的那个文件,存放在静态区的变量的生命周期一般比较长,一般与整个源程序“同生死、共存亡”,所以它只需初始化一次。】
8.栈与堆的区别:
- 管理方式不同:栈直接由编译器管理,堆由程序员管理。
- 存放内存不同:上面图片有了。
- 生长方向不同:栈是向下压栈,堆是向上存放数据。
- 空间大小不同:栈占用空间较小,而堆占用的空间较大。
- 能否产生碎片:栈不会产生碎片,堆会产生,有内存泄漏的风险。
- 分配效率不同:栈快。【栈是由内存分配的,系统专门为其准备寄存器,同时有专门的出栈和入栈指令,因而效率比较高。而堆空间则是C库分配的,可能会存在碎片的原因导致内存不连续,因而效率比较低】
9.const的含义是什么?
用 const 定义的变量的值是不允许改变的,即不允许给它重新赋值,即使是赋相同的值也不可以。用const修饰变量时,一定要给变量初始化,否则之后就不能再进行赋值了。
对比直接使#define的优点:
1.预编译指令只是对值进行简单的替换,不能进行类型检查
2.保护被修饰的东西,提高程序健壮性。
3.编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,使用起来效率高。
【类型检查:类型检查指的是在编译时或运行时验证操作数或变量是否被赋予了正确类型的操作,这种检查可以确保程序中的数据类型使用是安全的,避免因类型错误导致的程序崩溃或意外行为。例如,
int a = 5; // 整型变量a
double b = 3.7; // 双精度浮点变量b
double c = a + b; // 将整型和双精度浮点型相加】
用法:修饰变量、修饰函数的参数、函数返回值。
【常量指针与指针常量。常量指针是指针指向的内容是常量。指针常量是指指针本身是个常量,不能在指向其他的地址。前者是*放在后面,后者*放前面。还有一种指向常量的常指针。】
10.volatile的含义是什么?
volatile属于C语言的关键字。开发者告诉编译器该变量是易变的,无非就是希望编译器去注意该变量的状态,时刻注意该变量是易变的,让编译器不再去优化被volatile修饰的变量的操作。如:自定义延时函数中使用 volatile 去声明 val 变量可以解决编译器优化带来的延时失效问题;
volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加 volatile;
2、多任务环境下各任务间共享的标志应该加 volatile;
3、存储器映射的硬件寄存器通常也要加 volatile 说明,因为每次对它的读写都可能不一样;
11.结构与联合的区别是:(1)联合所有成员共用一块地址空间。(2)联合一个成员赋值,其他的成员全部被覆盖。
12含参宏与函数的区别:带参宏处理时间在前,在编译时,无参数类型,不分配内存,运行速度快,程序长度拉长。
13.两个栈实现一个队列的思想:两个栈A、B初始都为空,【入队】将元素入栈A,【出队】B栈不空,栈A全部出栈后入栈B,全部在栈B出栈。
14.两个队列实现一个栈的思想:两个队列实现一个栈的思想是利用两个队列的特性,即先进先出(FIFO)和后进先出(LIFO)。我们可以将一个队列用作输入队列,另一个队列用作输出队列。当需要入栈时,将元素添加到输入队列;当需要出栈时,将输入队列的所有元素除了最后一个元素之外依次添加到输出队列,然后从输入队列中移除最后一个元素并将其作为栈顶元素返回。最后,交换输入队列和输出队列的角色,以便下次操作。
15.访问固定的内存位置:要求设置一绝对地址为 0x67a9 的整型变量的值为 0xaa66。代码为:
16.找错误:在中断服务子程序中找错误
17.动态内存分配
使用原因:有时候程序中需要的空间大小在程序运行时才知道。
内存碎片,碎片收集,变量的持续时间的理解。
常见的错误:(1)空指针解引用。(2)越界访问。(3)非动态内存使用free()释放。(4)使用free()只释放动态内存的一部分,就是申请的时候的指针移动到后面了。(5)动态内存多次释放,解决办法是:第一次释放内存之后,马上指针置为空。(6)内存泄漏,忘记使用free()释放内存了。
常见的函数与之间的区别:malloc()与free(),calloc()与malloc的区别是,前者把申请的空间初始化为0。realloc()有两种情况,分为原有空间后面足够大的话,最简单,直接在后面申请空间就行。如果不足够大,需要再另找一个合适大小的连续空间,这样原有的数据的内存地址也会发生变化。
函数的未定义行为如何理解:程序可能会表现得不可预测,甚至在不同的运行时刻或不同的机器上表现不同。
想象一下,你有一个回收站(
free
函数),这个回收站专门回收塑料瓶(动态分配的内存)。如果你把家里的家具(非动态分配的内存,如栈上的局部变量)扔进回收站,回收站的工作人员可能会:
- 立刻拒收(程序崩溃)。
- 勉强收下但造成混乱(静默失败,内存损坏)。
- 暂时收下但日后产生问题(表面正常,但未来可能出现错误)。
18.typedef与#define的区别?
typedeff是关键字,对已经存在的数据类型取别名。
在编译阶段处理,会进行类型检查,只能在定义的作用域内使用。
define是预处理指令(宏定义),只进行简单的字符替换,是否产生错误要在编译时才可知。
没有作用域限制,可以对类型/变量/常量等进行替换
注意:同时定义两个以上的变量,记得要使用typedef,使用#define只能对于第一个变量有效。
19.指针变量的定义与区别:
int *a[10]与int (*a)[10] 区别就是个数,一个是指针数组,个数为10个指针组成数组。另一个是一个指针,指向一个数组的指针。
int (*a)(int)指的是a是一个指向函数的指针,返回类型为int,参数类型是int。
20.两个.c文件使用相同名字的static变量在编译不会出错。全部放在全局区。