C语言学习趣事_经典面试题系列_3

         有日子没有学习C语言了, 前些天在看windows程序设计时, 按照win的体系结构,在VC 6里面找到

下面一段代码,发现自己的C语言功底实在是差之又差。代码为我看到的C代码,至目前为止,还只能理解个大概;

估计要慢慢体会了。

View Code
DECLARE_HANDLE(HDC);


#define DECLARE_HANDLE(name)
struct name##__
{
int unused;
};
typedef
struct name##__ *name

      上面的代码等我理解后再和大家分享吧, 下面说说这一次的C语言面试题。

 8、找错题
 找出下面几个函数的错误:
试题1:
  void test1()
       {
      char string[10];
      char* str1 = "0123456789";
      strcpy( string, str1 );
       }

/*
   这个题目咋一看,没有任何错误, 给strcpy()函数传递的两个实参参数类型均能满足要求。
   但是细心一看我们会发现这个函数存在越界问题,"0123456789"这个字符串
   的长度为 strlen("0123456789") + 1 = 11 , 而很显然string[10],不可能存储这么大的空间
   通常在使用strcpy函数时一定要考虑源串、和目的串的大小问题。

*/
   函数改成下面的形式可能会健壮一些:
    int StrCpy(const char *source; char dest[])
     {
         if( NULL==source || NULL == dest || ( strlen(dest) < strlen(source) ) )
              return 1;   // 返回值=1 表示复制失败
         else
             strcpy(dest,source);           
         return 0;   //返回值=0 表示复制成功
     }
 

试题2::
void test2()
{
   char string[10], str1[10];
   int i;
   for(i=0; i<10; i++)
     {
        str1 = 'a';
      }
 strcpy( string, str1 );
}
/*
这个题目考查了两个问题: 
    1、 数组的首地址是常量,不可以作为左值, 即str1是一个常量,
        它代表整个数组的首地址。
    2、 第二数组的引用需要用下标,除了初始化时可以int iArray[10]={1,2}
        这样赋值外,在其他地方不可以批量给数组元素赋值。
    3、 同时strcpy复制函数是针对具有'\0'的字符类型变量,因此这个函数赋
        值同样存在赋值越界的情况。
*/
改成下面的方式估计会健壮一些:
void  test2( )
{
   char string[10],
         str1[10] ;
   for(int i=0; i<10; i++)
      str1[i] = 'a';
       str[9]='\0';
   strcpy(string , str1);
}

试题3:
void test3(char* str1)
  {
    char string[10];
    if( strlen( str1 ) <= 10 )
      {
        strcpy( string, str1 );
       }
   }
 //试题3同样存在越界的可能性。如果strlen(str1)=10, 则实际上str1占用的空间是11个。
 //strlen函数返回的长度没有计算末尾'\0'字符。 因此需要注意。
改为下面的方式可能会更健壮:
void  test3(char* str1)
  {
    char string[10];
    if( strlen( str1 ) < 10 && NULL != str1 )
        strcpy( string, str1 );
   }


试题4:
 void GetMemory( char *p )
   {
     p = (char *) malloc( 100 );
   }

 void Test( void )
  {
   char *str = NULL;
   GetMemory( str );
   strcpy( str, "hello world" );
   printf( str );
  }
/*
首先这个题目存在内存泄露的问题和指针指向空地址问题
说说这个题目的存在的几个问题:
 1、 在GetMemory函数里面, 没有对malloc函数返回值进行测试
     if(NULL==p)
 2、 在函数里面没有对指针p进行释放
     free(p);
 3、这里会有一个问题,在C语言中默认时按值传递的, 不是按照地址传递的。
    在程序里面不能改变str的指向。
      GetMemory(str);不能改变str的指向。 
    函数原型为:
       void GetMemory(char *p); 定义的就是一个指针类型的参数。
  4、在函数内部不能改变传值参数的值
*/

/*****************
malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,
它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求
的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果
有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多
的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,
malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大
的内存块。如果无法获得符合要求的内存块,malloc函数会返回NULL指针,因此在调用malloc动态申请内存块时,一定要
进行返回值的判断。
**************/

改成下面形式可能更健壮一些:
  Void GetMemory(char **p)
   {     
         char *temp;
         if(NULL != (temp=(char *)malloc(1000)))
               *p=temp;
         free(temp);
   }
                        

试题5:
char *GetMemory( void )
{
 char p[] = "hello world";
 return p;
}

void Test( void )
{
 char *str = NULL;
 str = GetMemory();
 printf( str );
}

/* 其实怎么说呢这个题目的理解比上面一个题目来对简单, 但是通过这个题目和上面
的题目需要知道一个事实:
    那就是函数内部声明的局部变量(static类型的除外,当然还有register的除外),这里指的
    是auto类型的变量, 其内存空间是在系统为应用程序开辟的栈里面申请。
    而malloc函数申请的空间则是从系统为应用程序开辟的堆里面申请。堆里面申请的不会自动释放,
    而栈里面申请的会随着函数声明周期的结束而自动释放。 
这个题目的错误之处在于没有理解局部变量的生命周期。
*/
改成下面的形式可能会更健壮:
char *getmemory(void)
{
    char *p=NULL;
    if(NULL !=(p=(char *)malloc(strlen("hello word")+1))
       return p;
}

/*

 但是这样返回的的指针是游离的, 没有任何意义

*/
 

试题6:

void GetMemory( char **p, int num )
{
 *p = (char *) malloc( num );
}

void Test( void )
{
 char *str = NULL;
 GetMemory( &str, 100 );
 strcpy( str, "hello" );
 printf( str );
}
/*
   这个题目在第四个题目已经实现和论述,不再论述
 指的一提的是:
     传递&str值,并不能改变str的指向。
*/

试题7:

void Test( void )
{
 char *str = (char *) malloc( 100 );
 strcpy( str, "hello" );
 free( str );
 ... //省略的其它语句
}
/*
   这个题目比上面的更加简单, 它的问题就是没有对malloc函数的返回情况进行
   检测,
   如果 NULL=(char *)malloc(NUM) 那么strcpy函数将不会成功执行,
  
*/

 

你可能感兴趣的:(C语言)