面试题收集

来源:互联网 发布:2003年nba总决赛数据 编辑:程序博客网 时间:2024/05/23 10:34

题目:输入一个链表的头结点,反转该链表,并返回反转后链表的头结点。链表结点定义如下:
struct ListNode
{
      int        m_nKey;
       ListNode* m_pNext;
};
分析:这是一道广为流传的微软面试题。由于这道题能够很好的反应出程序员思维是否严密,在微软之后已经有很多公司在面试时采用了这道题。
为了正确地反转一个链表,需要调整指针的指向。与指针操作相关代码总是容易出错的,因此最好在动手写程序之前作全面的分析。在面试的时候不急于动手而是一开始做仔细的分析和设计,将会给面试官留下很好的印象,因为在实际的软件开发中,设计的时间总是比写代码的时间长。与其很快地写出一段漏洞百出的代码,远不如用较多的时间写出一段健壮的代码。
为了将调整指针这个复杂的过程分析清楚,我们可以借助图形来直观地分析。假设下图中lmn是三个相邻的结点:
aßbßßl  mànà
假设经过若干操作,我们已经把结点l之前的指针调整完毕,这些结点的m_pNext指针都指向前面一个结点。现在我们遍历到结点m。当然,我们需要把调整结点的m_pNext指针让它指向结点l。但注意一旦调整了指针的指向,链表就断开了,如下图所示:
aßbßlßm  nà
因为已经没有指针指向结点n,我们没有办法再遍历到结点n了。因此为了避免链表断开,我们需要在调整mm_pNext之前要把n保存下来。
接下来我们试着找到反转后链表的头结点。不难分析出反转后链表的头结点是原始链表的尾位结点。什么结点是尾结点?就是m_pNext为空指针的结点。
基于上述分析,我们不难写出如下代码:
///////////////////////////////////////////////////////////////////////
// Reverse a list iteratively
// Input: pHead - the head of the original list
// Output: the head of the reversed head
///////////////////////////////////////////////////////////////////////
ListNode* ReverseIteratively(ListNode* pHead)
{
       ListNode* pReversedHead = NULL;
       ListNode* pNode = pHead;
       ListNode* pPrev = NULL;
      while(pNode != NULL)
       {
            // get the next node, and save it at pNext
             ListNode* pNext = pNode->m_pNext;
            // if the next node is null, the currect is the end of original 
            // list, and it's the head of the reversed list
            if(pNext == NULL)
                   pReversedHead = pNode;

            // reverse the linkage between nodes
             pNode->m_pNext = pPrev;

            // move forward on the the list
             pPrev = pNode;
             pNode = pNext;
       }

      return pReversedHead;
}
扩展:本题也可以递归实现。感兴趣的读者请自己编写递归代码。

 

分别给出BOOL,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var)

  解答:

   BOOL型变量:if(!var)

   int型变量: if(var==0)

   float型变量:

   const float EPSINON = 0.00001;

   if ((x >= - EPSINON) && (x <= EPSINON)

   指针变量:  if(var==NULL)

  剖析:

  考查对0值判断的“内功”,BOOL型变量的0判断完全可以写成if(var==0),而int型变量也可以写成if(!var),指针变量的判断也可以写成if(!var),上述写法虽然程序都能正确运行,但是未能清晰地表达程序的意思。一般的,如果想让if判断一个变量的“真”、“假”,应直接使用if(var)、if(!var),表明其为“逻辑”判断;如果用if判断一个数值型变量(short、int、long等),应该用if(var==0),表明是与0进行“数值”上的比较;而判断指针则适宜用if(var==NULL),这是一种很好的编程习惯。

  浮点型变量并不精确,所以不可将float变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。如果写成if (x == 0.0),则判为错,得0分。

 

看到一份不错的面试题,转过来大家看看,希望对那些正在找工作的XDJM们有所帮助。 
祝愿大家早日找到理想的工作 :) 

PS:很少在c/c++板块散分,顺便散点。 

转自:http://www.diybl.com/course/3_program/c++/cppsl/20081117/151331.html 

-------------------------------------------------------------------------- 
-------------------------------------------------------------------------- 

1.是不是一个父类写了一个virtual 函数,如果子类覆盖它的函数不加virtual ,也能实现多态? 

virtual修饰符会被隐形继承的。private 也被集成,只事派生类没有访问权限而已。virtual可加可不加。子类的空间里有父类的所有变量(static除外)。同一个函数只存在一个实体(inline除外)。子类覆盖它的函数不加virtual ,也能实现多态。在子类的空间里,有父类的私有变量。私有变量不能直接访问。 


-------------------------------------------------------------------------- 
2.输入一个字符串,将其逆序后输出。(使用C++,不建议用伪码) 

#include <iostream> 
using namespace std; 


void main() 

  char a[50];memset(a,0,sizeof(a)); 
  int i=0,j; 
  char t; 
  cin.getline(a,50,'/n'); 
  for(i=0,j=strlen(a)-1;i <strlen(a)/2;i++,j--) 
  { 
  t=a[i]; 
      a[i]=a[j]; 
  a[j]=t; 
  } 
  cout < <a < <endl;  


//第二种 

string str; 
cin>>str; 
str.replace; 
cout < <str; 


-------------------------------------------------------------------------- 
3.请简单描述Windows内存管理的方法。 

内存管理是操作系统中的重要部分,两三句话恐怕谁也说不清楚吧~~ 
我先说个大概,希望能够抛砖引玉吧 

当程序运行时需要从内存中读出这段程序的代码。代码的位置必须在物理内存中才能被运行,由于现在的操作系统中有非常多的程序运行着,内存中不能够完全放下,所以引出了虚拟内存的概念。把哪些不常用的程序片断就放入虚拟内存,当需要用到它的时候在load入主存(物理内存)中。这个就是内存管理所要做的事。内存管理还有另外一件事需要做:计算程序片段在主存中的物理位置,以便CPU调度。 

内存管理有块式管理,页式管理,段式和段页式管理。现在常用段页式管理 

块式管理:把主存分为一大块、一大块的,当所需的程序片断不在主存时就分配一块主存空间,把程 序片断load入主存,就算所需的程序片度只有几个字节也只能把这一块分配给它。这样会造成很大的浪费,平均浪费了50%的内存空间,但时易于管理。 

页式管理:把主存分为一页一页的,每一页的空间要比一块一块的空间小很多,显然这种方法的空间利用率要比块式管理高很多。 

段式管理:把主存分为一段一段的,每一段的空间又要比一页一页的空间小很多,这种方法在空间利用率上又比页式管理高很多,但是也有另外一个缺点。一个程序片断可能会被分为几十段,这样很多时间就会被浪费在计算每一段的物理地址上(计算机最耗时间的大家都知道是I/O吧)。 

段页式管理:结合了段式管理和页式管理的优点。把主存分为若干页,每一页又分为若干段。好处就很明显,不用我多说了吧。 

各种内存管理都有它自己的方法来计算出程序片断在主存中的物理地址,其实都很相似。 

这只是一个大概而已,不足以说明内存管理的皮毛。无论哪一本操作系统书上都有详细的讲解 


-------------------------------------------------------------------------- 
4. 
#include "stdafx.h" 
#define SQR(X) X*X 

int main(int argc, char* argv[]) 

int a = 10; 
int k = 2; 
int m = 1; 

a /= SQR(k+m)/SQR(k+m); 
printf("%d/n",a); 

return 0; 

这道题目的结果是什么啊? 

define 只是定义而已,在编择时只是简单代换X*X而已,并不经过算术法则的 

a /= (k+m)*(k+m)/(k+m)*(k+m); 
=>a /= (k+m)*1*(k+m); 
=>a = a/9; 
=>a = 1; 

PS:经过本人验证,虽然结果一样,但是应该不是这样运算的,应该是a/=k+m*k+m/k+m*k+m; 

因为SQR(k+m)/SQR(k+m)的值是7,而不是9。 

-------------------------------------------------------------------------- 
5. 
const 符号常量; 
(1)const char *p 
(2)char const *p 
(3)char * const p 
说明上面三种描述的区别; 


如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量; 
如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。 

(1)const char *p 

一个指向char类型的const对象指针,p不是常量,我们可以修改p的值,使其指向不同的char,但是不能改变它指向非char对象,如: 
const char *p; 
char c1='a'; 
char c2='b'; 
p=&c1;//ok 
p=&c2;//ok 
*p=c1;//error 

(2)char const *p 
(3)char * const p 

这两个好象是一样的,此时*p可以修改,而p不能修改。 

(4)const char * const p 
这种是地址及指向对象都不能修改。 

-------------------------------------------------------------------------- 
6.下面是C语言中两种if语句判断方式。请问哪种写法更好?为什么? 
int n; 
if (n == 10) // 第一种判断方式 
if (10 == n) // 第二种判断方式 

如果少了个=号,编译时就会报错,减少了出错的可能行,可以检测出是否少了= 

-------------------------------------------------------------------------- 
7.下面的代码有什么问题? 
void DoSomeThing(...) 

char* p; 
... 
p = malloc(1024);  // 分配1K的空间 
if (NULL == p) 
  return; 
... 
p = realloc(p, 2048); // 空间不够,重新分配到2K 
if (NULL == p) 
  return; 
... 


A: 
p = malloc(1024);    应该写成: p = (char *) malloc(1024); 
        没有释放p的空间,造成内存泄漏。 


-------------------------------------------------------------------------- 
8.下面的代码有什么问题?并请给出正确的写法。 
void DoSomeThing(char* p) 

char str[16]; 
int n; 
assert(NULL != p); 
sscanf(p, "%s%d", str, n); 
if (0 == strcmp(str, "something")) 

  ... 



A: 
sscanf(p, "%s%d", str, n);  这句该写成: sscanf(p, "%s%d", str, &n); 

-------------------------------------------------------------------------- 
9.下面代码有什么错误? 
Void test1() 

char string[10]; 
char *str1="0123456789"; 
strcpy(string, str1); 


数组越界 

-------------------------------------------------------------------------- 
10.下面代码有什么问题? 
Void test2() 

  char string[10], str1[10]; 
  for(i=0; i <10;i++) 
  { 
    str1[i] ='a'; 
  } 
  strcpy(string, str1); 


数组越界 

-------------------------------------------------------------------------- 
11.下面代码有什么问题? 
Void test3(char* str1) 

  char string[10]; 
  if(strlen(str1) <=10) 
  { 
    strcpy(string, str1); 
  } 


==数组越界 
==strcpy拷贝的结束标志是查找字符串中的/0 因此如果字符串中没有遇到/0的话 会一直复制,直到遇到/0,上面的123都因此产生越界的情况 

建议使用 strncpy 和 memcpy 

-------------------------------------------------------------------------- 
12.下面代码有什么问题? 

#define MAX_SRM 256 

DSN get_SRM_no() 

  static int SRM_no; //是不是这里没赋初值? 
  int I; 
  for(I=0;I <MAX_SRM;I++,SRM_no++) 
  { 
    SRM_no %= MAX_SRM; 
    if(MY_SRM.state==IDLE) 
    { 
      break; 
    } 
  } 
  if(I>=MAX_SRM) 
    return (NULL_SRM); 
  else 
    return SRM_no; 


系统会初始化static int变量为0,但该值会一直保存,所谓的不可重入... 

-------------------------------------------------------------------------- 
13.写出运行结果: 
{// test1 
    char str[] = "world"; cout < < sizeof(str) < < ": "; 
    char *p    = str;    cout < < sizeof(p) < < ": "; 
    char i    = 10;      cout < < sizeof(i) < < ": "; 
    void *pp  = malloc(10);  cout < < sizeof(p) < < endl; 


6:4:1:4 

-------------------------------------------------------------------------- 
14.写出运行结果: 
{// test2 
    union V { 
struct X { 
  unsigned char s1:2; 
  unsigned char s2:3; 
  unsigned char s3:3; 
} x; 

unsigned char c; 
    } v; 

    v.c = 100; 
    printf("%d", v.x.s3); 





-------------------------------------------------------------------------- 
15.用C++写个程序,如何判断一个操作系统是16位还是32位的?不能用sizeof()函数 

A1: 
16位的系统下, 
int i = 65536; 
cout < < i; // 输出0; 
int i = 65535; 
cout < < i; // 输出-1; 

32位的系统下, 
int i = 65536; 
cout < < i; // 输出65536; 
int i = 65535; 
cout < < i; // 输出65535; 

A2: 

int a = ~0; 
if( a>65536 ) 

    cout < <"32 bit" < <endl; 

else 

    cout < <"16 bit" < <endl; 



-------------------------------------------------------------------------- 
16.C和C++有什么不同? 

从机制上:c是面向过程的(但c也可以编写面向对象的程序);c++是面向对象的,提供了类。但是, 
c++编写面向对象的程序比c容易 

从适用的方向:c适合要求代码体积小的,效率高的场合,如嵌入式;c++适合更上层的,复杂的;  llinux核心大部分是c写的,因为它是系统软件,效率要求极高。 

从名称上也可以看出,c++比c多了+,说明c++是c的超集;那为什么不叫c+而叫c++呢,是因为c++比 
c来说扩充的东西太多了,所以就在c后面放上两个+;于是就成了c++ 

C语言是结构化编程语言,C++是面向对象编程语言。 
C++侧重于对象而不是过程,侧重于类的设计而不是逻辑的设计。 

-------------------------------------------------------------------------- 
17.在不用第三方参数的情况下,交换两个参数的值 
#include <stdio.h> 

void main() 

        int i=60; 
        int j=50; 
        i=i+j; 
        j=i-j; 
        i=i-j; 
        printf("i=%d/n",i); 
        printf("j=%d/n",j); 


方法二: 
i^=j; 
j^=i; 
i^=j; 

方法三: 
// 用加减实现,而且不会溢出 
a = a+b-(b=a) 

-------------------------------------------------------------------------- 
18.有关位域的面试题(为什么输出的是一个奇怪的字符) 

a.t = 'b';效果相当于 a.t= 'b' & 0xf; 

'b' --> 01100010 
'b' & 0xf -->>00000010 
所以输出Ascii码为2的特殊字符 


char t:4;就是4bit的字符变量,同样 
unsigned short i:8;就是8bit的无符号短整形变量 

原创粉丝点击