您的位置:首页 > 编程语言 > C语言/C++

C/C++语言面试题详细解答二

2015-05-20 09:13 567 查看
以下提到的题目全部来自C语言面试题大汇总.本文章将对其中的题目根据自己的理解进行详细的解答。存在不足或错误的地方敬请指正出来。接着第一篇讲的,我们继续。

RTOS

问:描述实时系统的基本特性

答:在特定时间内完成特定的任务,实时性与可靠性

这个问题对我来说非常非常的难,虽然说就只有简简单单的一句话,但是里面包含了N多的内容,不是我等小喽喽能够说得清的。在此我就简单的摘录以下两篇文章的内容吧。

什么是真正的实时操作系统实时 Linux 架构剖析以及wikipedia,RTOS上对于RTOS的解释。

RTOS的定义

A real-time operating system (RTOS) is an operating system (OS) intended to serve real-time application process data as it comes in, typically without buffering delays. Processing time requirements (including any OS delay) are measured in tenths of seconds or shorter.

上面是wikipedia的解释。

实时系统指系统的计算正确性不仅取决于计算的逻辑正确性,还取决于产生结果的时间。如果未满足系统的时间约束,则认为系统失效。

全局变量和局部变量

问:全局变量和局部变量在内存中是否有区别?如果有,是什么区别?

答:全局变量储存在静态数据库,局部变量在堆栈

更准确的来说应该是全局变量存储在静态存储区,局部变量在栈中。

详见进程内存布局

一个由 C/C++编译的程序占用的内存(memory)分为以下几个部分:

1. 程序代码区(.text) - 存放函数体的二进制代码 。

2. 文字常量区(.rodata) - 常量字符串就是放在这里的,程序结束后由系统释放(rodata—read only data)。

3. 全局区/静态区(static) - 全局变量 和 静态变量的存储是放在一块的。初始化的全局变量和静态变量在一块区域(.rwdata or .data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss), 程序结束后由系统释放。*在 C++中,已经不再严格区分bss和 data了,它们共享一块内存区域

4. 堆区(heap) - 一般由程序员分配释放(new/malloc/calloc delete/free),若程序员不释放,程序结束时可能由 OS 回收。

注意:它与数据结构中的堆是两回事,但分配方式倒类似于链表。

5. 栈区(stack) - 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

Java中是没有全局变量这个概念的

java中是没有全局变量这个概念的,java程序中不能像C++那样在类外定义全局变量,因为JAVA当初出现的初衷就是为了安全性和跨平台性,所以去掉了类似C、C++中的全局变量的概念。JAVA中不应该有所谓全局变量的概念,全局变量严重影响了封装和模块化,所以中需要所谓的全局变量,那一定是对程序的设计出了问题。以上是网上的观点,但就我目前来说,一个能在全局上使用的变量是有必要的。

java提供了public static关键字来实现一个全局的变量。如果程序只有一个包的话,那么将这个public static(被声明为static的变量不需要实例化对象即可直接使用类名来引用之) 修饰的变量放到程序初始化的地方去实现,就可以在这个程序的各个地方直接调用这个变量了。这虽然不叫全局变量,但实际使用中和全局变量的意思是一样的。当然你也可以完全不用全局变量,可以写一个类,定义一般变量,并为该类定义一些操作变量的方法,通过调用可以更改变量的这些方法来达到使用全局变量的目的。

为什么人们常说尽量少用全局变量?

1、不利于模块化的编程。

2、全局变量存储在静态存储区,该内存区域大小是有限制的。而全局变量的整个生命周期是从声明起到程序结束,每声明一个,该内存区域就减少一次,在程序运行结束前,操作系统是不会进行回收与释放。这样就容易造成内存溢出,产生不可预知的错误。

3、在多线程程序中,全局变量容易读写不同步的问题。

全局变量与局部变量哪个执行效率更高?

问这种问题其实是没有多大的意义的,因为这个问题涉及到编译器、平台、代码书写等问题。但是网上还真有这个问题的讨论,来看看大家都是怎么说的。

全局变量一开始编译之后是放在data段的,一条汇编指令就可以读取了。而局部变量是通过stack(一般cpu都放在内存中的)来实现读取的,速度相对较慢。不过现在一般好点的编译器可以对楼主的代码优化,将i放在register中。

当Cache命中的时候,CPU访问内存的效率是最高的

由于局部变量是存在栈中的,当一个函数占用的栈空间不是很大的时候,这部分内存很有可能全部命中cache,这时候CPU访问的效率是很高的。

相反,如果一个函数里既使用了全局变量又使用了局部变量,那么当这两段地址相差较大时,cpu cache需要来回切换,那么效率会下降。

所以不太好说全局还是局部变量的效率高

If the compiler can’t fit all locals into registers it then creates a “stack frame”. There is a slight performance hit for locals residing in a stack frame.

在这个讨论帖Keep a global variable or recreate a local variable in c?中给出了更加详细的答复。简要做一下翻译:

有位老兄叫做Pandoro他发帖说:他已经在Android用Java开发很久了,他的工作内容对效率极其的严格,因为他觉得对于Android来说,local variables就是相当于垃圾回收,这严重影响了效率,于是他想把多数的local variables变成global variabels。

后面有个叫dash-tom-bang就回复他说在C语言中关于全局变量和局部变量执行效率的区别:

In C, the performance difference depends on the hardware. Loading a global on a RISC processor is more instructions (because you have to load both halves of the address in separate instructions, versus an add to the stack pointer), and then you need to contend with cache issues. For the most part, you can count on your local variables being in the cache. Using globals will thrash the cache a bit and some functions may be very adversely affected.

If you have substantial performance variability while running your app, it is quite likely that your assertion about the performance impact of local variables is immaterial.

The “cost” of creating a local variable in C is zero; it’s just bumping a register (the stack pointer) to make space for the local. Then you initialize that variable via whatever means are appropriate. You should be able to know if that is expensive or not by casual inspection. When the function exits, the stack pointer is returned to its previous value, regardless of how many local variables you have.

If your definition of “local variables” is heap allocated objects, though, you will suffer from the cost of memory allocation. Memory allocation is very slow in my opinion, so whatever you can do to get away from malloc/free (and ‘new’ in Java), the better off you’ll be. (I make games, and we tend to use dlmalloc but even that is too slow for regular usage; 400ns per call adds up quick.)

在C中,全局变量和局部变量的性能取决于硬件。当把全局变量加载到RISC上需要更多的指令(因为需要指令去加载地址和栈指针(不知道这样理解对不对?)),还需要将其与cache联系起来。大多数情况下,你可以在cache中找到局部变量。使用全局变量在某种程度上会破坏cache并且某些功能将会面临危险。

当运行你的app时候如果性能产生比较大的变化,那么你上面说的关于局部变量会影响性能的推测是说不通的。

平衡二叉树

问:什么是平衡二叉树

答:左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1

看看大牛的解答吧:

轻松搞定面试中的二叉树题目轻松搞定面试中的链表题目

堆栈溢出

问:堆栈溢出一般是由什么原因导致的?

答:没有回收垃圾资源

堆溢出Heap overflow

堆溢出是缓冲区溢出的另一种形式,它发生在堆中。前面说过:堆区一般由程序员动态分配释放(new/malloc/calloc delete/free)。

1、若程序员释放了,但还是去读写那块区域,会出现不可预知的错误(堆错误)。

2、如果程序员动态申请10Bytes的大小,但是往里面写11Bytes的数据,那么就会造成越界(堆溢出)。

3、如果程序员一直申请大量的堆区,但是都没有释放,有可能耗尽堆空间。**

堆错误

程序员向堆中申请40个bytes大小的空间,之后free掉,pointer变成野指针,如果再对该堆区进行操作,那么会造成堆错误。

#include <stdio.h>
#include <malloc.h>

int main(void)
{
int *pointer = calloc(10, sizeof(int));     // 申请10*4=40个Bytes大小的堆区

free(pointer);                              // 释放申请的空间,pointer此时为野指针
pointer[0] = 1;                             // 再次去那个堆区读写数据

getchar();
return 0;
}


在VS2008运行错误如下:



其实上面的code还有个问题,就是在申请内存空间的时候,都要做个判断是否申请成功

int *pointer = calloc(10, sizeof(int));     // 申请10*4=40个Bytes大小的堆区
if(pointer != NULL)
{
// 执行你想做的
}
else
{
// 报告错误
}


堆溢出

heap_overflow.c

#include <stdio.h>
#include <malloc.h>

int main(void)
{
int i = 10;
int *pre_pointer = calloc(10, sizeof(int));     // 申请10*4=40个Bytes大小的堆区
int *next_pointer = calloc(10, sizeof(int));

if((pre_pointer != NULL) && (next_pointer != NULL))
{
printf("pre_pointer addr = %p, next_pointer addr = %p\n", pre_pointer, next_pointer);   // 分别打印两个指针的地址

// pre_pointer在其指向的堆区进行越界读写数据
for(i=10; i<15; i++)
{
pre_pointer[i] = i;
}

// 打印next_pointer的第一个元素
printf("next_pointer[0] = %d\n", next_pointer[0]);

// 释放内存
//free(pre_pointer);
//free(next_pointer);

return 0;
}
else
{
return -1;
}

}


如果我屏蔽掉释放内存的那两个语句,那么在centos下的执行正确结果如下:



从结果中我们可以看到两个指针的地址分别为:

pre_pointer addr = 0x9ef4008

next_pointer addr = 0x9ef4038

因此我们可以得出一下结论:

1,堆的分配所自下向上增长的;

2,next_pointer - pre_pointer = 0x30 = 48Bytes

为什么所48Bytes而不是40Bytes呢?我猜是由于进行了字节对齐。

3,next_pointer = &pre_pointer[12]

由于pre_pointer的越界影响到了next_pointer指向的区域,堆溢出,进而导致了next_pointer[0]为12而不是为0。

如果取消屏蔽那两个语句,执行错误的结果如下:



我猜想出现这个问题主要是因为我们队pre_pointer进行越界访问,导致堆区的结构被破坏,之后进行free(pre_pointer)操作的话直接导致堆错误。

Malloc内存泄露和内存越界问题的研究给出了越界操作的各种情况,并给出了解决办法,大家可以看一下。

还有以下几篇文章都是关于堆溢出的详细解释:

Use a heap overflow to write arbitrary data

w00w00 on Heap Overflows

Explain stack overflow and heap overflow in programming with example? [duplicate]

栈溢出Stack overflow

http://en.wikipedia.org/wiki/Stack_overflow

http://stackoverflow.com/questions/26158/how-does-a-stack-overflow-occur-and-how-do-you-prevent-it

http://www.ouah.org/lamheap.txt

http://stackoverflow.com/questions/214741/what-is-a-stack-overflow-error
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: