zoukankan      html  css  js  c++  java
  • C语言面试题1

    1.分析下面代码有什么问题?
    1
    2
    3
    4
    5
    6
    void test1()
    {
     char string[10];
     char* str1 = "0123456789";
     strcpy( string, str1 );
    }
    字符串str1需要11个字节才能存放下(包括末尾的’’),而string只有10个字节的空间,strcpy会导致数组越界;  
     
    2.分析下面代码有什么问题? 
     1 void test2()
     2 {
     3  char string[10], str1[10];
     4  int i;
     5  for(i=0; i<10; i++)
     6  {
     7  str1  = 'a';
     8  }
     9 strcpy( string, str1 );
    10 }
     
    首先,代码根本不能通过编译。因为数组名str1为 char *const类型的右值类型,根本不能赋值。
    再者,即使想对数组的第一个元素赋值,也要使用 *str1 = 'a'; 
    其次,对字符数组赋值后,使用库函数strcpy进行拷贝操作,strcpy会从源地址一直往后拷贝,直到遇到''为止。所以拷贝的长度是不定的。如果一直没有遇到''导致越界访问非法内存,程序就崩了。
    完美修改方案为:
     1 void test2()
     2 {
     3     char string[10], str1[10];
     4     int i;
     5     for(i=0; i<9; i++)
     6     {
     7         str1[i]  = 'a';
     8     }
     9     str1[9] = '';
    10     strcpy( string, str1 );
    11 }
    3.指出下面代码有什么问题?
    void test3(char* str1)
    {
     if(str1 == NULL){
            return ;
     }
     char string[10];
     if( strlen( str1 ) <= 10 )
     {
     strcpy( string, str1 );
     }
    }

    if(strlen(str1) <= 10)应改为if(strlen(str1) < 10),因为strlen的结果未统计’’所占用的1个字节。  

    4.写出完整版的strcpy函数

    1 char * strcpy( char *strDest, const char *strSrc ) 
    2 {
    3  assert( (strDest != NULL) && (strSrc != NULL) );//检测输入指针是否能访问
    4  char *address = strDest; 
    5  while( (*strDest++ = * strSrc++) != ‘0’ ); //复制字符串
    6  return address;//返回指针
    7 }

    5.检查下面代码有什么问题?

     1 void GetMemory( char *p )
     2 {
     3  p = (char *) malloc( 100 );
     4 }
     5 void Test( void ) 
     6 {
     7  char *str = NULL;
     8  GetMemory( str ); 
     9  strcpy( str, "hello world" );
    10  printf( str );
    11 }

    传入中GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的实参值,执行完 

    1
    2
    char *str = NULL;
    GetMemory( str ); 
    后的str仍然为NULL;
    1:传入形参并不能真正改变形参的值,执行完之后为空;
    2:在函数GetMemory中和Test中没有malloc对应的free,造成内存泄露

    6.下面代码会出现什么问题?

     1 char *GetMemory( void )
     2 { 
     3  char p[] = "hello world"; 
     4  return p; 
     5 }
     6 void Test( void )
     7 { 
     8  char *str = NULL; 
     9  str = GetMemory(); 
    10  printf( str ); 
    11 }
    1
    2
    char p[] = "hello world"
    return p; 

    p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。  

    7.下面代码会出现什么问题?

     1 void GetMemory( char **p, int num )
     2 {
     3  *p = (char *) malloc( num );
     4 }
     5 void Test( void )
     6 {
     7  char *str = NULL;
     8  GetMemory( &str, 100 );
     9  strcpy( str, "hello" ); 
    10  printf( str ); 
    11 }

    1. 传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句 

    1
    *p = (char *) malloc( num );

    后未判断内存是否申请成功,应加上: 

    1
    2
    3
    4
    5
    if ( *p == NULL )
    {
     ...//进行申请内存失败处理
    }
    同时应考虑num>0;
    2. 未释放堆内存 动态分配的内存在程序结束之前没有释放,应该调用free, 把malloc生成的内存释放掉
    3. printf(str) 改为 printf("%s",str),否则可使用格式化 字符串攻击

     8.下面代码会出现什么问题?

    1 void Test( void )
    2 {
    3  char *str = (char *) malloc( 100 );
    4  strcpy( str, "hello" );
    5  free( str ); 
    6  ... //省略的其它语句
    7 }

    在执行   
    char *str = (char *) malloc(100);   
    后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:   
    str = NULL;   
    试题6的Test函数中也未对malloc的内存进行释放。  

    9.看看下面的一段程序有什么错误?

    1 swap( int* p1,int* p2 )
    2 {
    3  int *p;
    4  *p = *p1;
    5  *p1 = *p2;
    6  *p2 = *p;
    7 }
    1.需要一个返回值void 
    2在swap函数中,p是一个“野”指针,有可能指向系统区,导致程序运行的崩溃。在VC++中DEBUG运行时提示错误“Access Violation”。该程序应该改为:
    1 void swap( int* p1,int* p2 )
    2 {
    3  int p;
    4  p = *p1;
    5  *p1 = *p2;
    6  *p2 = p;
    7 }

    10.分别给出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分。 
     
    11.以下为Windows NT下的32位C++程序,请计算sizeof的值
    1 void Func ( char str[100] )
    2 {
    3  sizeof( str ) = ?
    4 }
    5 void *p = malloc( 100 );
    6 sizeof ( p ) = ?

    sizeof( str ) = 4   
    sizeof ( p ) = 4   
    【剖析】   
    Func ( char str[100] )函数中数组名作为函数形参时,在函数体内,数组名失去了本身的内涵,仅仅只是一个指针;在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。   
    数组名的本质如下:   
    (1)数组名指代一种数据结构,这种数据结构就是数组;   
    例如:   

    1
    2
    char str[10];
    cout << sizeof(str) << endl;

    输出结果为10,str指代数据结构char[10]。   
    (2)数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;   
    char str[10];    
    str++; //编译出错,提示str不是左值  

    (3)数组名作为函数形参时,沦为普通指针。  
    Windows NT 32位平台下,指针的长度(占用内存的大小)为4字节,故sizeof( str ) 、sizeof ( p ) 都为4。  

    12.写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。另外,当你写下面的代码时会发生什么事?   
    least = MIN(*p++, b);  

     解答:   

    1
    #define MIN(A,B) ((A) <= (B) ? (A) : (B))   

    MIN(*p++, b)会产生宏的副作用   
    剖析:   
    这个面试题主要考查面试者对宏定义的使用,宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对“参数”进行的是一对一的替换。   
    程序员对宏定义的使用要非常小心,特别要注意两个问题:   
    (1)谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。所以,严格地讲,下述解答:   

    1
    2
    #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)的作用结果是:   

    1
    ((*p++) <= (b) ? (*p++) : (b))   
    这个表达式会产生副作用,指针p会作2次++自增操作。  
    除此之外,另一个应该判0分的解答是:   
    1
    #define MIN(A,B) ((A) <= (B) ? (A) : (B)); 
    这个解答在宏定义的后面加“;”,显示编写者对宏的概念模糊不清,只能被无情地判0分并被面试官淘汰。  
     
    13.为什么标准头文件都有类似以下的结构?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #ifndef __INCvxWorksh
    #define __INCvxWorksh 
    #ifdef __cplusplus
    extern "C" {
    #endif 
    /*...*/ 
    #ifdef __cplusplus
    }
    #endif 
    #endif /* __INCvxWorksh */

    参考答案

    头文件中的编译宏 
    1
    2
    3
    #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++的函数了。

    14.编写一个函数,作用是把一个char组成的字符串循环右移n个。比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefg” 函数头是这样的: 

    //pStr是指向以''结尾的字符串的指针 
    //steps是要求移动的n 
    1
    2
    3
    4
    void LoopMove ( char * pStr, int steps )
    {
     //请填充...
    }

    参考答案

    void LoopMove(char *str, int steps)
    {
    int len = strlen(str);
    char tmp[MAXSIZE];
    strcpy(tmp, str+len-steps);
    strcpy(tmp+steps, str);
    *(tmp+len)  = '/0';
    strcpy(str, tmp);
    }
    或:
    void LoopMove(char *str, int steps)
    {
    int len = strlen(str);
    char tmp[MAXSIZE];
    memcpy(tmp, str+len-steps, steps);
    memcpy(str+steps, str, len-steps);
    memcpy(str, tmp, steps);
    }
    15.已知WAV文件格式如下表,打开一个WAV文件,以适当的数据结构组织WAV文件头并解析WAV格式的各项信息。
    WAVE文件格式说明表

    偏移地址

    字节数

    数据类型

    内 容

    文件头

    00H

    4

    Char

    "RIFF"标志

    04H

    4

    int32

    文件长度

    08H

    4

    Char

    "WAVE"标志

    0CH

    4

    Char

    "fmt"标志

    10H

    4

    过渡字节(不定)

    14H

    2

    int16

    格式类别

    16H

    2

    int16

    通道数

    18H

    2

    int16

    采样率(每秒样本数),表示每个通道的播放速度

    1CH

    4

    int32

    波形音频数据传送速率

    20H

    2

    int16

    数据块的调整数(按字节算的)

    22H

    2

    每样本的数据位数

    24H

    4

    Char

    数据标记符"data"

    28H

    4

    int32

    语音数据的长度

    参考答案

    将WAV文件格式定义为结构体WAVEFORMAT:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    typedef struct tagWaveFormat
     char cRiffFlag[4]; 
     UIN32 nFileLen; 
     char cWaveFlag[4]; 
     char cFmtFlag[4]; 
     char cTransition[4]; 
     UIN16 nFormatTag ; 
     UIN16 nChannels; 
     UIN16 nSamplesPerSec; 
     UIN32 nAvgBytesperSec; 
     UIN16 nBlockAlign; 
     UIN16 nBitNumPerSample; 
     char cDataFlag[4]; UIN32 nAudioLength; } WAVEFORMAT;
    假设WAV文件内容读出后存放在指针buffer开始的内存单元内,则分析文件格式的代码很简单,为: 
    WAVEFORMAT waveFormat; 
    memcpy( &waveFormat, buffer,sizeof( WAVEFORMAT ) ); 
    直接通过访问waveFormat的成员,就可以获得特定WAV文件的各项格式信息。 
    【剖析】 
    试题6考查面试者组织数据结构的能力,有经验的程序设计者将属于一个整体的数据成员组织为一个结构体,利用指针类型转换,可以将memcpy、memset等函数直接用于结构体地址,进行结构体的整体操作。 透过这个题可以看出面试者的程序设计经验是否丰富。


     16.请说出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显然不符合编程者的初衷,也没有任何意义。   
     
     
     17.请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1

    参考答案

    【解答】 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为: 

    内存地址

    存放内容

    0x4000

    0x78

    0x4001

    0x56

    0x4002

    0x34

    0x4003

    0x12

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

    内存地址

    存放内容

    0x4000

    0x12

    0x4001

    0x34

    0x4002

    0x56

    0x4003

    0x78

    联合体union的存放顺序是所有成员都从低地址开始存放,面试者的解答利用该特性,轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写。如果谁能当场给出这个解答,那简直就是一个天才的程序员。 

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

    参考答案

    【解答】

    1
    2
    3
    4
    int Sum( int n )
     return ( (long)1 + n) * n / 2;  //或return (1l + n) * n / 2;
    }
    【剖析】 
    对于这个题,只能说,也许最简单的答案就是最好的答案。下面的解答,或者基于下面的解答思路去优化,不管怎么“折腾”,其效率也不可能与直接return ( 1 l + n ) * n / 2相比!    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int Sum( int n )
    {
     long sum = 0;
     forint i=1; i<=n; i++ )
     {
     sum += i;
     }
     return sum;
     
     
     
     

    C语言经典面试题2

    1.可以做switch()的参数类型是:

    答:只能是int和char型。

    2.写出float X与零值比较的if语句

    答:if(x>-0.000001&x<0.000001)

    3.C语言中将一个字符转换成整数的函数是哪个?写出这个函数的原型

    答:函数:atol()

        功能:把字符串转换成长整型

        原型:long atol(const char *nptr)

    示例:

     1 #include<stdio.h>
     2 
     3 int main()
     4 
     5 {
     6 
     7   long lNum=0;
     8 
     9   char *str="12345";
    10 
    11   lNum=atol(str);
    12 
    13   printf("String=%s integer=%d
    ",str,lNum);
    14 
    15   return 0;
    16 
    17 }

    4.

    1 unsigned char *p1=(unsigned char *)0x801000;
    2 
    3   unsigned long *p2=(unsigned long *)0x810000;

      p1+5=?,p2+5=? 

    答:p1+5=0x801005,p2+5=0x810020;

    5.求值:

    1 int aNum=5,bNum=0;
    2 
    3         bNum=(aNum++)(aNum++);
    4 
    5         aNum=?,bNum=?

    答:aNum=7,bNum=25

    6 一16位整数1101010110110111,每四位为一个整数,写函数求值

    答:

     1 char sumFunc(unsigned short num)
     2 
     3     {
     4 
     5       char ch=0;
     6 
     7       int  iCount=4;
     8 
     9       do{
    10 
    11            c+=n&15;
    12 
    13            n>>4;
    14 
    15          }(while--i);
    16 
    17      return 0;
    18 
    19      }

    7.编程实现两个字符串s和t,把t字符串拼接到s字符串尾,s字符串有足够的空间存放t字符串

     1 void strConnect(char *s,char *t,int n)
     2 
     3 {
     4 
     5   char *p=s;
     6 
     7   char *q=t;
     8 
     9   if(q==NULL)
    10 
    11      return;
    12 
    13   while(*p!='')
    14 
    15      p++;
    16 
    17   while(*q!='')
    18 
    19   {
    20 
    21      *p=*q;
    22 
    23       p++;
    24 
    25       q++;
    26 
    27    }
    28 
    29    *p='';
    30 
    31 }
    32 
    33 void main()
    34 
    35 {
    36 
    37    char cArray1[10]="abcde";
    38 
    39    char cArray2[3]="xyz";
    40 
    41    strConnect(cArray1,cArray2,3);
    42 
    43    printf("%s",cArray1);
    44 
    45 }

    8.static全局变量与普通全局变量的区别,static局部变量与普通局部变量的区别,static函数与普通函数的区别。

    答:1.非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的,而静态全局变量(只初始化一次)限制了其作用域,只在定义该变量的源文件内有效,同一源程序的其它源文件都不能使用它。

        2.static局部变量(只初始化一次)存储在静态区,普通局部变量存储在栈中,存储方式不同。

        3.static函数在内存中只有一份,普通函数在每个被调用中都维持一份拷贝。

    9.char a;

      char *str=&a;

      strcpy(str,"hello");

    问这段代码有什么问题?

    答:没有为str分配内存空间,将会发生异常,将一个字符串复制进一个字符变量指针所指指针,虽然可以正确输出结果,但因为越界进行内在读写导致程序崩溃。

     
     
  • 相关阅读:
    POJ 2236 Wireless Network(并查集)
    POJ 2010 Moo University
    POJ 3614 Sunscreen(贪心,区间单点匹配)
    POJ 2184 Cow Exhibition(背包)
    POJ 1631 Bridging signals(LIS的等价表述)
    POJ 3181 Dollar Dayz(递推,两个long long)
    POJ 3046 Ant Counting(递推,和号优化)
    POJ 3280 Cheapest Palindrome(区间dp)
    POJ 3616 Milking Time(dp)
    POJ 2385 Apple Catching(01背包)
  • 原文地址:https://www.cnblogs.com/ordinary-world/p/9971045.html
Copyright © 2011-2022 走看看