不可忽略的面试题

来源:互联网 发布:图论与网络流理论答案 编辑:程序博客网 时间:2024/05/18 00:20

一、什么叫可重入?

可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是
可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断
它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么
错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,
中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是
不能运行在多任务环境下的。


二、tcp/IP三次握手?
1.首先客户端通过向服务器端发送一个SYN来建立一个主动打开,作为三
路握手的一部分。(同步位为1)
2. 然后服务器端应当为一个合法的SYN回送一个SYN/ACK。(同步位和确
认位都为1)
3. 最后,客户端再发送一个ACK。这样就完成了三路握手,并进入了连接
建立状态。(确认位位1)


三、TCP/IP通信阻塞和非阻塞?
阻塞:当socket的接收缓冲区中没有数据时,read调用会一直阻
塞住,直到有数据到来才返回。当socket缓冲区中的数据量小于期望读取
的数据量时,返回实际读取的字节数。当socket的接收缓冲区中的数据大于
期望读取的字节数时,读取期望读取的字节数,返回实际读取的长度。
非阻塞:socket的接收缓冲区中有没有数据,read调用都会立刻返回。接收
缓冲区中有数据时,与阻塞socket有数据的情况是一样的,如果接收缓冲区
中没有数据,则返回错误号为EWOULDBLOCK,表示该操作本来应该阻塞的,但
是由于本socket为非阻塞的socket,因此立刻返回,遇到这样的情况,可
以在下次接着去尝试读取。如果返回值是其它负值,则表明读取错误。
因此,非阻塞的rea调用一般这样写:
if ((nread = read(sock_fd, buffer, len)) < 0)
{
   if(errno == EWOULDBLOCK)
  {
     return0; //表示没有读到数据
  }
  else 
     return-1; //表示读取失败
}

else return nread;  //读到数据长度


四、TCP/UDP区别?
TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器
彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP
提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端
传到另一端。


UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠
性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达
目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有
超时重发等机制,故而传输速度很快


五、林锐内存思考?
(1)
void GetMemory(char *p) 
{
  p =(char *)malloc(100);
}
void Test(void)
{
  char*str = NULL;
  GetMemory(str);
  strcpy(str,"hello world");
  printf(str);
}
答:程序崩溃因为GetMemory并不能
传递动态内存,Test函数中的 str一
直都是NULL
strcpy(str, "hello world");
将使程序崩溃 


(2)
char *GetMemory(void) 
{
  char p[]= "hello world";
  returnp;
}
void Test(void)
{
  char*str = NULL;
  str =GetMemory();
  printf(str);
}
答:可能是乱码 因为GetMemory返回的
是指向栈内存的指针,该指针的地址
不是 NULL,但其原现的内容已经被清
除,新内容不可知


(3)
void GetMemory2(char **p, int num) 
{
  *p =(char *)malloc(num);
}
void Test(void)
{
  char*str = NULL;
  GetMemory(&str,100);
  strcpy(str,"hello");
  printf(str);
}
答: (1)能够输出hello (2)内存泄漏没有free


(4)
void Test(void) 
{
  char*str = (char *) malloc(100);
  strcpy(str,hello);
  free(str);
  if(str!= NULL)
  {
     strcpy(str,world);
     printf(str);
   }
}
答:篡改动态内存区的内容,后果难以
预料,非常危险 因为free(str);之后
str成为野指针,if(str !=NULL)语句
不起作用


六、编写各种str函数?
strcpy函数
char *my_strcpy(char *dest, const char *src)
{
  char*temp;

  assert(dest!=NULL&& str != NULL);
  while((*dest++= *src++) != '\0');

  returntemp;
}


char *my_strcat(char *dest, const char *stc)
{
  char*temp;

  assert(dest!=NULL&& str != NULL);
  while(*dest)
  dest++;
  while((*dest++= *stc++) != '\0');

  returntemp;
}


char my_strstr(const char *str, char c)
{
  for(;*str != c; str++)
  if(*str== '\0')
    returnNULL;
  else
    returnstr;
}


int my_strlen(const char *str)
{
  char*temp = str;
  for(;*temp != '\0'; temp++);

  return(int)(temp - str);
}


void *my_memcpy(void * dest, const void *src,size_t count)
{
  char*temp_dest = (char *)dest;
  char*temp_src = (char *)src;

  assert(dest!= NULL && src != NULL);

  while(count--)
  *temp_dest++= *temp_src++;
  returndest;
}


int my_strcmp(char *str1, char *str2)
{
  int ret= 0;

  while((ret= *(unsigned char*)str1 - *(unsigned char*)str2)&&*str1&&*str2)
  {
    str1++;
    str2++;
  }
  if(ret< 0)
    ret =-1;
  elseif(ret > 0)
    ret =1;
    returnret;
}


七、链表操作
(1)逆序?
node *reverse_node(node *head)
{
  node*record, *current;

  if(head== NULL || head->next == NULL)
  returnhead;

  current= head->next;
  head->next= NULL;
  while(current!= NULL)
  {
     record= current->next;
     current->next= head->next;
     head->next= current;
     current= record;
   }
   return head;
}
(2)插入
node *add_node(node *head, node *data)
{
   node*current = head;
   while(current->next!= NULL)
   current= current->next;
   current->next= data;
   data->next= NULL;
   returnhead;
}
(3)删除
node *del_node(node *head, node *data)
{
   node*pf,*pb;
   pf =head->next;

  while(pf!= NULL && pf->data != data->data)
  {
     pb =pf;
     pf =pf->next;
  }
  if(pf->data== data->data)
  {
     pb->next = pf->next;
     free(pf);
  }
  else
    printf("NOnode!\n");
}
(4)单链表(非循环)倒数第4个元素?
思路:让第一个元素先走四步,然后两个游标指针一起走。
node *Get_Node(node *head)
{
   int i;
   node*first = head;
   node*back  = head;

   for(i=0;i<4; i++)
  {
     if(first->next== NULL)
       printf("Nodeless than four!\n");
     first= first->next;
  }
  while(first!= NULL)
  {
     first= first->next;
     back= back->next;
   }
   returnback;
}
(5)如何找出链表中间元素?
思路:让前一个指针每次走两步,后一个指针每次走一步。
node *Get_middle_node(node *head)
{
  node*first = head;
  node*back = head;

  while(first!= NULL)
  {
    first= first->next->next;
    back =back->next;
  }
  returnback;
}
(6)删除一个无头单链表的一个节点(链表很长10万以上)?
思路:把当前节点的下个节点数据拷贝到当前节点,然后删除下一个节点。
void del_node(node *del_node)
{
  node*record = del_node->next;
  node*current = del_node;

  current->data= record->data;  //数据拷贝,假设结构体数据为data
  current->next= record->next;  //指针跳转
  free(record);
}
(7)如何判断一个单链表是否有环?
思路:我们用一个步长1和一个步长2的游标指针遍历链表,观察是否有两个游标相遇的时刻。
int judge_list_circle(node *head)
{
   node*first = head;
   node*back = head;

   while(first->next!= NULL && back->next->next != NULL)
  {
    first= first->next;
    back =back->next->next;
    if(first== back)
    returntrue;
  }
  returnfalse;
}


八、两种排序法(冒泡排序、选择排序)
void bubble(int a[], int n)
{
   inti,j,temp;

   for(i=0;i<n-1; i++)
   for(j=i+1;j<n; j++)
  {
     if(a[i]> a[j])
    {
       temp= a[i];
       a[i]= a[j];
       a[j] = temp;
     }
   }
   return 0;
}


void choise(int a[], int n)
{
  int i,j, temp, min;

  for(i=0;i<n-1; i++)
  {
    min=i;
    if(j=i+1;j<n; j++)
    {
       if(a[i]> a[j])
       min= j;
    }
    if(min!= i)
    {
       temp= a[i];
       a[i]= a[min];
       a[min]= temp;
    }
  }
  return0;
}


九、字符串反转?
char str_rev(char *str, size_t len)
{
   char*start = str;
   char*end = str+len-1;
   charch;

   if(str!= NULL)
  {
     while(start < end)
    {
      ch =*start;
      *start++ = *end;
      *end--= ch;
    }
  }
  returnstr;
}


十、关键字volatile有什么含意 并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会
去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重
新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1) 并行设备的硬件寄存器(如:状态寄存器);
2) 一个中断服务子程序中会访问到的非自动变量;
3) 多线程应用中被几个任务共享的变量;


1) 一个参数既可以是const还可以是volatile吗?解释为什么。
2) 一个指针可以是volatile 吗?解释为什么。
3) 下面的函数有什么错误:
int square(volatile int *ptr)
{
   return *ptr  *  *ptr;
}
答案:
1) 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想
不到地改变。它是const因为程序不应该试图去修改它。
2) 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修该一个指向一个buffer的指针时。
3)这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向
一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr)
{
   inta,b;
   a =*ptr;
   b =*ptr;
   returna * b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr)
{
   int a;
   a =*ptr;
   returna * a;
}


十一、内存分配应该注意什么问题?
1、检查内存分配是否成功。
2、内存生命周期,程序结束时记得free,避免内存泄露。
3、使用过程中避免指针越界访问,会导致不必要的错误。


十二、字符串回文判断?
int str_is_resv(char *str)
{
   int i;
   int len = strlen(str);
   char*start = str;
   char*end   = str+len-1;

   for(i=0;i<len/2; i++)
   {
      if(*(start+i)!= *(end-i))
        return-1;
   }
   return0;
}


十三、字符串循环左移?
//循环左移,库函数实现 
char *str_rev1(char *str, int bit)
{
   int len= strlen(str);
   int n =bit%len;
   chartemp[128] = "";

   strcpy(temp,str);
   strncpy(str,str+n, len-n);
   str[len-n]= '\0';
   strncat(str,temp, n);

   returnstr;
}


//循环左移,不用库函数
char *str_rev2(char *str, int bit)
{
  chartemp_str[128];
  int i,j;
  intstr_len = strlen(str);
  bit =bit%str_len;

  for(i=0;i<bit; i++)
  {
    *(temp_str+i)= *(str+i);
  }
  for(i=0;i<str_len-bit; i++)
  {
    *(str+i)= *(str+bit+i);
  }
  for(j=0;j<bit; j++)
  {
    *(str+i+j) = *(temp_str+j);
  }
   returnstr;
}


十四、如何编程判断大小端?
/*返回1表示小端,返回0表示大端*/
int check_cpu_endian()
{
  union 
  {
    unsignedint a;
    unsignedchar b;
   }s;
   s.a = 1;      //0x01 
   return(s.b == 1);
}


十五、如何判断一个字节(char)里面多少bit被置1?
int count_bit(int value)
{
  int i,count=0;

  for(i=0;i<8; i++)
  {
    if(value&0x01== 0x01)
      count++;
    value = value >> 0x01;
  }
  returncount;
}


十六、判断一个整数里面有多少个1?
int count_bit(int value)
{
  intcount=0; 

  while(value)
  {
    count++;
    value&= value-1;
  }
  returncount;
}


十七、二分法查找?
int mid_serach(int arry[], int key, int size)
{
  int low,high, mid;

  low = 0;
  high =size-1;

  while(low< high)
  {
    mid = (low+high)/2;
    if(key== arry[mid])
      returnmid;
   elseif(key < arry[mid])
     high= mid-1;
   elseif(key > arry[mid])
     low =mid+1;
  }
  return-1;
}


十八、字符串转成十进制数?
int str_to_int(char *str)
{
  int temp= 0;

  while(*str!= '\0')
  {
    temp =temp*10 + *str - '0';
    str++;
  }
  //temp =atol(str);    //库函数一步完成
   returntemp;
}


十九、OSI模型分哪几层?
物理层、数据链路层、网络层、传输层、应用层、表示层、会话层
集线器hub工作在OSI参考模型的(物理)层;
网卡工作在OSI参考模型的(物理)层;
路由器router工作在OSI参考模型的(网络)层;
交换机Switch工作在OSI参考模型的(数据链路)层。


二十、给定某个整数n(0-10000),求小于等于n的整数中与7有关的整数
(被7整除或者数字中含有7,只要求求出个数,不用列觉出每一个数字)
int fun(int data)
{
  intcount=0;
  int i;
  intfirst,second,third,four;

  for(i=1;i<=data; i++)
  {
    first = i%10;
    second= i/10%10;
    third = i/100%10;
    four  = i/1000;
    if((i%7 == 0) || (first == 7) || (second == 7) || (third == 7) || (four ==7)) 
    {
      //printf("qianbai shi ge is:%d %d %d %d\n", four, third, second, first);
      count++;
    }
  }
  return count;

}


二十一、将字符串中每个单词的第一个字符变为大写?
char *fun(char *str)
{
  int i;
  int flag= -1;
  int len= strlen(str);

  if(islower(str[0]))
  {
    str[0]= toupper(str[0]);
  }
  for(i=1;i<len; i++)
  {
    if(str[i]=='' && islower(str[i+1]))
    {
      str[i+1]= toupper(str[i+1]);
    }
  }
  returnstr;
}


二十二、统计出具有n个元素的一维整数数组中大于等于
所有元素平均值的元素个数。验证该函数?
int fun(int a[], int size)
{
  int i=0;
  floatsum=0, aver=0;
  intcount=0; 

  for(i=0;i<size; i++)
    sum+=a[i];
  aver =sum/size;

  for(i=0;i<size; i++)
  {
     if(a[i]> aver)
       count++;
  }
  printf("Theaver is %f\n", aver);

  returncount;
}

0 0
原创粉丝点击