c语言常见面试题(二)

试题1:请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1

解答:

int checkCPU()
{
 {
  union w
  {
   int a;
   char b;
  } c;
  c.a = 1;
  return (c.b == 1);
 }
}

剖析:

   嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方 式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。例如,16bit宽的数0x1234在Little- endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址 存放内容
0x4000 0x34
0x4001 0x12

  而在Big-endian模式CPU内存中的存放方式则为:

内存地址 存放内容
0x4000 0x12
0x4001 0x34

  32bit宽的数0x123456在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址 存放内容
0x4000 0x56
0x4001 0x34
0x4002 0x12
0x4003 0x00

  而在Big-endian模式CPU内存中的存放方式则为:

内存地址 存放内容
0x4000 0x00
0x4001 0x12
0x4002 0x34
0x4003 0x56

  联合体union的存放顺序是所有成员都从低地址开始存放,面试者的解答利用该特性,轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写

 

试题2:写一个函数返回1+2+3+…+n的值(假定结果不会超过长整型变量的范围)

解答:


 

int Sum(int n)
{
  return ( (long)1 + n) * n/ 2;  //或return (1l + n) * n / 2;

}

剖析:
 
对于这个题,只能说,也许最简单的答案就是最好的答案。下面的解答,或者基于下面的解答思路去优化,不管怎么“折腾”,其效率也不可能与直接return ( 1 l + n ) * n / 2相比!

自动类型转换:

 如果一个运算符两边的运算数类型不同,先要将其转换为相同的类型,即较低类型转换为较高类型,然后再参加运算,转换规则如下图所示。
double ←── float 高

long

unsigned

int ←── char,short 低
● 图中横向箭头表示必须的转换,如两个float型数参加运算,虽然它们类型相同,但仍要先转成double型再进行运算,结果亦为double型。 纵向箭头表示当运算符两边的运算数为不同类型时的转换,如一个long 型数据与一个int型数据一起运算,需要先将int型数据转换为long型, 然后两者再进行运算,结果为long型。所有这些转换都是由系统自动进行的, 使用时你只需从中了解结果的类型即可。

试题3:不用第三变量交换两个数
 

voidswap(int a,int b)
{
a=a^b;
    b=b^a;
    a=a^b;
}
或者
void swap(int a,int b)
{
    a=a+b;
    b=a-b;
    a=a-b;
}
不过这两种方法只是修改了函数的形参,如果要修改实参,可以采用如下的方法:
void swap(int* a,int*b)
{
    *a=*a^*b;
    *b=*b^*a;
    *a=*a^*b;
    printf("In %s:a=%d,b=%d\n",__FUNCTION__,*a,*b);
}

试题4:求大数的阶乘例如100!,使用通常的做法会溢出,这里要使用数组的方法。

 #include

int len,a[201];  /*数字长度与存数字的数组,其中a[1]是个位,a[len]是最高位*/

void mul(int x)  /*乘法过程*/
{
  int i;
  for (i=1;i<=len;i++) a[i]*=x;  /*一位一位的乘*/
  for (i=1;i<=len+1;i++)  /*进位*/
  {
    a[i+1]+=a[i]/10;
    a[i]%=10;
  }
  len+=2;   /*计算新的数字长度*/
  while (a[len]==0) len--;  
}

void out()  /*输出数字*/
{
  int i;
  for (i=len;i>0;i--) printf("%d",a[i]);
  printf("\n");
}

int main()
{
  int i;
  for (i=0;i<=200;i++) a[i]=0;  /*初始化赋*/
  len=1;
  a[1]=1;
  for (i=2;i<=100;i++) mul(i);  /*从2到100高精乘*/
  out();  /*输出*/
  return 0;
}

试题5:写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。另外,当你写下面的代码时会发生什么事?

least = MIN(*p++, b);

解答:

#define MIN(A,B) ((A) <= (B) ? (A) : (B))
MIN(*p++, b)会产生宏的副作用

剖析:

这个面试题主要考查面试者对宏定义的使用,宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对“参数”进行的是一对一的替换。程序员对宏定义的使用要非常小心,特别要注意两个问题:

(1) 谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。所以,严格地讲,下述解答:

#define MIN(A,B) (A) <= (B) ? (A) : (B)
#define MIN(A,B) (A <= B ? A : B )  都应判0分;

(2) 防止宏的副作用。

  宏定义#define MIN(A,B) ((A) <= (B) ? (A) : (B))对MIN(*p++, b)的作用结果是:((*p++) <= (b) ? (*p++) : (*p++)) 这个表达式会产生副作用,指针p会作三次++自增操作。除此之外,另一个应该判0分的解答是:#define MIN(A,B) ((A) <= (B) ? (A) : (B));
这个解答在宏定义的后面加“;”,显示编写者对宏的概念模糊不清,只能被无情地判0分并被面试官淘汰。

 

试题6:为什么标准头文件都有类似以下的结构?
 

#ifndef __INCvxWorksh
#define __INCvxWorksh
#ifdef __cplusplus

extern "C" {
#endif
/* */
#ifdef __cplusplus
}

#endif
#endif /* __INCvxWorksh */


解答:

头文件中的编译宏

#ifndef __INCvxWorksh
#define __INCvxWorksh
#endif

的作用是防止被重复引用。

作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在symbol库中的名字与C语言的不同。例如,假设某个函数的原型为:

void foo(int x, int y);

该函数被C编译器编译后在symbol库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。_foo_int_int这样的名字包含了函数名和函数参数数量及类型信息,C++就是考这种机制来实现函数重载的。

为了实现C和C++的混合编程,C++提供了C连接交换指定符号extern "C"来解决名字匹配问题,函数声明前加上extern "C"后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了。

 

试题7:编写一个函数,作用是把一个char组成的字符串循环右移n个。比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefgh”

函数头是这样的:

//pStr是指向以'\0'结尾的字符串的指针
//steps是要求移动的n

void LoopMove ( char * pStr, int steps )
{
 //请填充...
}

解答:

正确解答1:

正确解答1:
 
void LoopMove (char *pStr,int steps )
{
  int n = strlen( pStr )- steps;
  char tmp[MAX_LEN];
  strcpy ( tmp, pStr+ n );
  strcpy ( tmp + steps, pStr);
  *( tmp + strlen ( pStr) ) = '\0';
  strcpy( pStr, tmp);
}

正确解答2:
 
void LoopMove (char *pStr,int steps )
{
  int= strlen( pStr )- steps;
  char tmp[MAX_LEN];
  memcpy( tmp, pStr+ n, steps );
  memcpy(pStr + steps, pStr, n);
  memcpy(pStr, tmp, steps);
}


剖析:

这个试题主要考查面试者对标准库函数的熟练程度,在需要的时候引用库函数可以很大程度上简化程序编写的工作量。

最频繁被使用的库函数包括:
(1) strcpy
(2) memcpy
(3) memset

试题8:请说出static和const关键字尽可能多的作用

解答:

static关键字至少有下列n个作用:

(1) 函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
(2) 在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3) 在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
(4) 在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(5) 在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。

const关键字至少有下列n个作用:

(1) 欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了(2) 对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3) 在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4) 对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5) 对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。例如:

const classA operator*(const classA& a1,const classA& a2);

operator*的返回结果必须是一个const对象。如果不是,这样的变态代码也不会编译出错:

classA a, b, c;
(a * b) = c; // 对a*b的结果赋值

操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。

剖析:

  小小的static和const居然有这么多功能,我们能回答几个?如果只能回答1~2个,那还真得闭关再好好修炼修炼。这个题可以考查面试者对程序设计知识的掌握程度是初级、中级还是比较深入,没有一定的知识广度和深度,不可能对这个问题给出全面的解答。大多数人只能回答出static和const关键字的部分功能。

试题9:分析下面的程序

struct s1
{
int i: 8;
int j: 4;
int a: 3;
double b;
};

struct s2
{
int i: 8;
int j: 4;
double b;
int a:3;
};

printf("sizeof(s1)= %d\n",sizeof(s1));
printf("sizeof(s2)= %d\n",sizeof(s2));
result: 12, 16(gcc运行的结果)
第一个struct s1
{
int i: 8;
int j: 4;
int a: 3;
double b;
};

理论上是这样的,首先是i在相对0的位置,占8位一个字节,然后,j就在相对一个字节的位置,由于一个位置的字节数是4位的倍数,因此不用对齐,就放在那里了,然后是a,要在3位的倍数关系的位置上,因此要移一位,在15位的位置上放下,目前总共是18位,折算过来是2字节2位的样子,由于double是8 字节的,Linux下的GCC奉行的是:任何2字节大小的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型(比如long,double)都以4为对齐模数。因此要在相对0要是4个字节的位置上放下,因此从18位开始到4个字节之间的位置被忽略,占8字节,因此,总共是12字节。
1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:   
struct bs  
{  
unsigned a:4  
unsigned :0 /*空域*/  
unsigned b:4 /*从下一单元开始存放*/  
unsigned c:4  
}  
在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。
2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位
3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如: 
struct k  
{  
int a:1  
int :2 /*该2位不能使用*/  
int b:3  
int c:2  
};  
从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。

试题9:.要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234;
   那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?

  答案:*((void (*)( ))0x100000 ) ( );
  首先要将0x100000强制转换成函数指针,即:
  (void (*)())0x100000
  然后再调用它:
  *((void (*)())0x100000)();
  用typedef可以看得更直观些:
  typedef void(*)() voidFuncPtr;
  *((voidFuncPtr)0x100000)();

1、将一个链表逆序

 

LinkList*reverse(LinkList*head)
{
  LinkList *p1,*p2= NULL,*p3= NULL;
  if(head == NULL || head->next== NULL)
    return head;
  p1 = head->next;
  while(p1!=NULL)
  {
    p3 = p1->next;
    p1->next = p2;
    p2 = p1;
    p1 = p3;
}
head->next = p2;
  // head = p2;


  return head;
}

2、计算一个字节里(byte)里面有多少bit被置1

 

#include<stdio.h>

int comb(constint c)
{
int count = 0;
int i = 0;
int cc = c;
while(i++<8)
{
    if((cc&1)==1)
    {
        count++;
    }
    cc = cc>>1;
}
return count;
}

int main()
{
const int c = 0xcf;
printf("%d\n",comb(c));
return 1;
}

3、在一个字符串中找到可能的最长的子字符串

 

#include<stdio.h>
#include <stdlib.h>
#include <string.h>

char *commanstring(char shortstring[],char longstring[])
{
  int i,j;
  char *substring= malloc(256);
  if(strstr(longstring,shortstring)!=NULL)
    return shortstring;
  for(i=strlen(shortstring)-1;i>0;i--)
  {
    for(j=0;j<=strlen(shortstring)-i;j++)
    {
      memcpy(substring,&shortstring[j],i);
    substring[i]='\0';
    if(strstr(longstring,substring)!=NULL)
      return substring;
    }
  }
  return NULL;
}

void main(void)
{
  char *str1 = "aocdfe";
  char *str2 = "pmcdfa";
  char *comman = NULL;
  if(strlen(str1)>strlen(str2))
    comman= commanstring(str2,str1);
  else
    comman = commanstring(str1,str2);
  printf("the longest comman string is:%s\n",comman);
 
}

4、字符串转换为整数

 

#include<stdio.h>
#include <string.h>
void reverse(char s[])
{ //字符串反转

    int c, i=0, j;
    for(j=strlen(s)-1;i<j;j--)
    { c=s[i];
        s[i]=s[j];
        s[j]=c;
        i++;
    }
}
void IntegerToString(char s[],int n)
{ int i=0,sign;
    if((sign=n)<0)//如果是负数,先转成正数

        n=-n;
    do //从个位开始变成字符,直到最高位,最后应该反转

    { s[i++]=n%10+'0';
    }while((n=n/10)>0);
    //如果是负数,补上负号

    if(sign<0)
        s[i++]='-';
    s[i]='\0';//字符串结束

    reverse(s);
}
void main()
{ int m;
    char c[100];
    m =215;
    IntegerToString(c,m);
    printf("integer = %d string = %s\n", m, c);
}

5、整数转换为字符串

 

#include<stdio.h>
#include <string.h>
int Atoi(char str[])
{
int i;
int weight = 1;// 权重

int rtn = 0;// 用作返回


for(i = strlen(str)- 1; i >= 0; i--)
{
   rtn += (str[i] - '0')* weight;//

   weight *= 10;// 增重

}

return rtn;
}

void main()
{
char str[32];

printf("Input a string :");
gets(str);

printf("%d\n", Atoi(str));
}

6、将一个字符串逆序

 

#include<stdio.h>
#include <string.h>

char *strconv(char*p)
{
    int length =strlen(p);
char *ptr = p;
char *ptr_1 = p + length -1;
while(ptr < ptr_1)
{
char c = *ptr;
*ptr = *ptr_1;
*ptr_1 = c;
++ptr;
--ptr_1;
}
return p;
}
int main()
{
char str[]="abcdef1234";
char *p;
p = strconv(str);
printf("%s",p);
return 1;
}

 6.函数 int compare(int a,int b),定义为该函数的函数指针P:为    int(*p)(int,int);p= compare;

7:设float a=2, b=4, c=3;,以下C语言表达式与代数式 (a+b)+c计算结果不一致的是
A.(a+b)*c/2
B.(1/2)*(a+b)*c
C.(a+b)*c*1/2
D.c/2*(a+b)
参考答案:B,因为a,b,c三个变量都是浮点数,所以在B答案中其结果是0,因为在计算1/2是就是0,如果改成1/2.0就正确了

你可能感兴趣的:(c语言常见面试题(二))