经典面试题目C语言

题一,堆和栈的区别是?


题二,Volatile与Register的区别是?


题三,ARM里的大端格式和小端格式分别是什么意思?


题一答案:


(1)存储内容不同


栈:在函数调用时,栈中存放的是函数中(最底下是函数调用后的下一条指令)的各个参数(局部变量)。


堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员分配。


(2)管理方式上不同


栈:由系统自动分配并释放空间。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间,当对应的生存周期结束后栈空间被自动释放。


堆:需要程序员指定大小手动申请和手动释放,在C语言中使用malloc函数申请,使用free函数释放。


(3)空间大小不同


栈:获取空间较小。在Windows下一般大小是1M或2M,当剩余栈空间不足时,分配失败overflow。


堆:获得空间根据系统的有效虚拟内存有关,比较灵活、大。


(4)能否产生碎片不同


栈:不会产生碎片,空间连续。


堆:采用的是链表的存储方式,会产生碎片。


(5)生长方向不同


栈: 向低地址扩展的数据结构,是一块连续的内存区域。


堆: 向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表空闲内存地址来存储的,自然不连续,而链表的遍历方向是由低地址向高地址。


(6)分配方式不同


栈:有2种分配方式:静态分配和动态分配,静态由编译器完成,例如局部变量;动态由malloc函数实现,由编译器进行释放。


堆: 都是动态分配的,没有静态分配的堆。


(7)分配效率不同


栈:由系统自动分配,速度较快。但程序员无法控制。


堆:由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。


题二答案:


a.volatile

volatile是易变的,不稳定的意思,volatile是关键字,是一种类型修饰符,用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其他线程等,遇到这个关键字声明的变量,编译器对访问该变量的代码不在进行优化,从而可以提供对特殊地址的稳定访问。那么什么是编译器优化呢?


为了提高运行效率,攻城师们费尽心机地把代码优化,优化程序运行时存取速度。一般,分为硬件优化和软件优化。硬件优化,流水线工作,详细可以参考《计算机组成原理》。软件优化,一部分是程序猿们做的代码优化(前提你得有优化的思路和能力),还有一部分就是我们的编译器优化了。


现代的编译器经过那么多年的发展,已经比较成熟,它会把多余的变量忽略掉,让代码的运行效率更高。默认情况下,编译器都会对代码进行优化,会把一些变量在寄存器里存取,而不是在内存里存取,如此一来,CPU在自己家里拿东西当然比从内存那里拿东西要快得多。举个小栗子:

int i = 5;

int a = i;

……

int b = i;


编译器发现两次从i读数据的代码之间,并没有对i进行过操作,它会自动把上次读的数据放在b中,而不是重新从i里面读取。


而volatile关键字告诉编译器该变量是随时可能发生变化的,每次使用它的时候必须从内存中取出它的值,因而编译器生成的汇编代码会从原内存地址中读取数据使用,而不是从寄存器或者缓存中读取,从而保证了对特殊地址的稳定访问。


简言之,状态要经常变化的,为了防止我们编译优化而导致的存取的数据不同步的问题,这时我们就需要用到volatile。那具体到什么场景下需要用到volatile关键字呢?


1、并行设备的硬件寄存器(如:状态寄存器);


2、一个中断服务子程序中会访问到的非自动变量();


3、多线程应用中被几个任务共享的变量;


上面提到了非自动变量,这里进一步对几种变量做一番解释:


自动变量:是在函数内部定义和使用的变量,它是局部变量。


非自动变量:有两种,一种是全局变量,一种是静态变量。


全局变量:在函数外面定义的变量,只能定义一次,不能有重复的定义,不然就会发生错误,而其他的文件要想使用这个变量,需要extern来声明这个变量(也可省略,因为默认就是extern),这个声明叫做引用声明。


若不想被其他文件访问,则用static关键字声明为静态变量。静态变量与自动变量的本质区别是,静态变量并不像自动变量那样使用堆栈机制来使用内存。


而是为静态变量分配固定的内存,在程序运行的整个过程中,它都会被保持,而不会被销毁。这就是说静态变量的持续性是程序运行的整个周期。这有利于我们共享一些数据。


如果静态变量在函数内部定义,则它的作用域就是在这个函数内部,仅在这个函数内部使用它才有效,但是它不同于自动变量,自动变量离开函数后就会被销毁,而静态变量不会被销毁。他在函数的整个运行周期内都会存在。


b. register

这个关键字请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率。注意是尽可能,不是绝对。你想想,一个CPU 的寄存器也就那么几个或几十个,你要是定义了很多很多register 变量,它累死也可能不能全部把这些变量放入寄存器吧。


题三答案:

当前的存储器,多以byte为访问的最小单元,当一个逻辑上的地址必须分割为物理上的若干单元时就存在了先放谁后放谁的问题, 于是端(endian)的问题应运而生了, 对于不同的存储方法, 就有大端(big-endian)和小端(little- endian)两个描述。


字节排序按分为大端和小端,概念如下


大端(big endian): 低地址存放高有效字节


小端(little endian): 低字节存放低有效字节


现在主流的CPU, intel系列的是采用的little endian的格式存放数据,而motorola系列的CPU采用的是big endian,ARM则同时支持 big和little。


举个例子

int a = 0x12345678;

a是四字节的int类型变量,需要占四个字节空间,假设变量a的首地址是0x2000,那么数据存储在地址中的格式如下:



                         ---------end-------





题一,同步和异步有啥区别?

题二,TCP与UDP有啥区别?

题三,进程和线程有啥区别?


题一答案:


同步(Sync):所有的操作都做完,才返回给用户。这样用户在线等待的时间太长,给用户一种卡死了的感觉(比如系统迁移中,点击了迁移,界面就不动了,但是程序还在执行,卡死了的感觉)。


这种情况下,用户不能关闭界面,如果关闭,那么迁移程序也会中断,用户体验不好。


异步(Async):将用户请求放入消息队列,并反馈给用户,比如系统迁移程序已经启动,提示你可以关闭浏览器了。然后程序再慢慢地写入数据库。但是用户没有卡死的感觉,用户体验较好。


题二答案:


1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接


2、TCP提供可靠的服务。它通过校验和,丢包时的重传控制,序号标识,滑动窗口、确认应答,次序乱掉的分包进行顺序控制实现可靠传输。即通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达; UDP尽最大努力交付,即不保证可靠交付。


3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高要求的通信或广播通信场景。


4、每一条TCP连接只能是点到点的; UDP支持一对一,一对多,多对一和多对多的交互通信方式。


5、TCP对系统资源要求较多,UDP对系统资源要求较少。


UDP有时比TCP更有优势:


UDP以其简单、传输快的优势,在越来越多场景下取代了TCP, 如实时游戏。


(1)网速的提升给UDP的稳定性提供可靠网络保障,丢包率很低,如果使用应用层重传,能够确保传输的可靠性。


(2)TCP为了实现网络通信的可靠性,使用了复杂的拥塞控制算法,建立了繁琐的握手过程,由于TCP在内置的系统协议栈中,极难对其进行改进。


采用TCP,一旦发生丢包,TCP会将后续的包缓存起来,等前面的包重传并接收到后再继续发送,延时会越来越大。


基于UDP对实时性要求较为严格的情况下,采用自定义重传机制,能够把丢包产生的延迟降到最低,尽量减少网络问题造成的影响。


题三答案:


1. 进程是资源分配的最小单位。

2. 线程是程序执行的最小单位,也是处理器调度的基本单位,但进程不是,两者均可并发执行。


3. 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据,使用相同的地址空间,因此,CPU切换一个线程的花费远比进程小很多,同时创建一个线程的开销也比进程小很多。


4. 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也跟着死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。


5. 进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程。


6. 执行过程:每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。


优缺点:


线程执行开销小,但是不利于资源的管理和保护。线程适合在SMP机器(双CPU系统)上运行。


进程执行开销大,但是能够很好的进行资源管理和保护,可以跨机器迁移。


何时使用多进程,何时使用多线程?


对资源的管理和保护要求高,不限制开销和效率时,使用多进程。

要求效率高,频繁切换时,资源的保护管理要求不是很高时,使用多线程。








猜你喜欢

转载自www.cnblogs.com/LyndonMario/p/9326026.html