C/C++常见笔试面试题(三)——字符数组做形参、内存申请与释放、宏

来源:互联网 发布:男士斜挎包知乎 编辑:程序博客网 时间:2024/06/04 01:36

      以下题目是我在牛客网上看到的。

链接:https://www.nowcoder.com/questionTerminal/7beda454b8a444428dfb6b13f9787e96来源:牛客网

      1、分析代码是否有问题

void GetMemory( char *p ) {  p = (char *) malloc( 100 ); } void Test( void )  {  char *str = NULL;  GetMemory( str );   strcpy( str, "hello world" );  printf( str ); }

      分析:代码有问题。输出的内容还是 NULL ,因为在传参时,p 和  str 同时指向一段新内存地址。但是在 GetMeory()函数中,p 又指向了一段新的内存,所以,str 指向的还是原来那个 NULL 。如果要改变 str 的值,需要传二级指针。什么意思呢,你要改变 a 的值,需要传 &a 。你要改变指针变量的值,需要传二级指针。最后注意:使用malloc(),还要使用 free()进行释放,否则会内存泄露。具体修改意见如下:

void GetMemory( char **p ) {  *p = (char **) malloc( 100 ); } void Test( void )  {  char *str = NULL;  GetMemory( &str );   strcpy( str, "hello world" );  printf( str ); 
 free(str);
}

     2、分析下面代码问题

char *GetMemory( void ){  char p[] = "hello world";  return p; }void Test( void ){  char *str = NULL;  str = GetMemory();  printf( str ); }

      代码分析:上述代码输出的是个乱码。char p[]="hello world";相当于char p[12],strcpy(p," hello world" ).p是一个数组名,属于局部变量,存储在栈中, " hello world" 存储在文字存储区,数组p中存储的是 " hello world" 的一个副本,当函数结束,p被回收,副本也消失了(确切的说p指向的栈存储区被取消标记,可能随时被系统修改),而函数返回的p指向的内容也变得不确定,文字存储区的 " hello world" 未改变。可以这样修改:

 ①char* p= " hello world" ; return p; 这里p直接指向文字存储区的 " hello world" ,函数按值返回p存储的地址,所以有效。 

②static char p[]= " hello world" ; return p; static指出数组p为静态数组,函数结束也不会释放,所以有效。

      那就有人问了,如果是 int a = 5;return a;的话,返回值就是5啊。这个问题以后再讨论。

      3、分析下面代码问题:

void GetMemory( char **p, int num ){ *p = (char *) malloc( num );}void Test( void ){ char *str = NULL; GetMemory( &str, 100 ); strcpy( str, "hello" );  printf( str ); }

      代码分析:

      1)传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句
      *p = (char *) malloc( num );
      后未判断内存是否申请成功,应加上:
        if ( *p == NULL )
            {
            ...//进行申请内存失败处理
            }
      同时应考虑num>0;
      2)未释放堆内存 动态分配的内存在程序结束之前没有释放,应该调用free, 把malloc生成的内存释放掉
      3) printf(str) 改为 printf("%s",str),否则可使用格式化 字符串攻击

      4、分析下面代码:

void Test( void ){ char *str = (char *) malloc( 100 ); strcpy( str, "hello" ); free( str );  ... //省略的其它语句}

      代码分析:还是那个问题,需要判断内存申请是否成功;此外,在free()以后,要将 str 设置为空指针,否则它可能变成野指针。所以在最后应该加上 str = NULL;

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

      判断BOOL类型,if(!val)即可;判断 int 类型,if(val == 0)即可;判断 float 类型,if(fabs(val) >= 0.000001)即可;判断指针类型,if(var = NULL)。有人问了,像 int 类型,指针类型也可以 if(!val), 但是这样表述不好。

      6、以下为Windows NT下的32位C++程序,请计算sizeof的值

void Func ( char str[100] ){ sizeof( str ) = ?}void *p = malloc( 100 );sizeof ( p ) = ?

      分析:之前博客我分析过,在32位的计算机中,指针类型都是占4个字节。本例中,当数组作为函数形参时,直接退化成指针,当成指针使用,所以,答案为:4,4

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

      分析:之前我给大一的小孩当助教的时候碰到过这一题,当时我对这个地方还是比较陌生的。正确的写法应该是这样的:

#define MIN(a,b) ((a) <= (b) ? (a) : (b))

      在实际写法中,要注意几个问题:1) 、保险起见,宏的参数和整个宏都要用()括起来,如果你这样写:

#define MIN(a,b) (a) <= (b) ? (a) : (b)
#define MIN(a,b) (a <= b ? a : b)

都是不可以的。如果你这样写:

#define MIN(a,b) ((a) <= (b) ? (a) : (b));

更是不可以,可能直接就被面试官给 Pass了。请注意:宏的后面绝对不能加 ;

      8、分析题:标准头文件为什么都有类似的结构?

#ifndef __INCvxWorksh
#define __INCvxWorksh
#ifdef __cplusplus
extern "C" {
#endif
/*...*/
#ifdef __cplusplus
}
#endif
#endif /* __INCvxWorksh */


      分析:头文件中的编译宏

#ifndef __INCvxWorksh#define __INCvxWorksh#endif

的作用是为了被重复。预处理就是在进行编译的第一遍词法扫描和语法分析之前所作的工作。说白了,就是对源文件进行编译前,先对预处理部分进行处理,然后对处理后的代码进行编译。这样做的好处是,经过处理后的代码,将会变的很精短。 关于预处理命令中的文件包含(#include),宏定义(#define),书上已经有了详细的说明,在这里就不详述了。这里主要是对条件编译(#ifdef,#else,#endif,#if等)进行说明。以下分3种情况:

1:情况1:

#ifdef _XXXX

...程序段1...

#else

...程序段2...

#endif

 这表明如果标识符_XXXX已被#define命令定义过则对程序段1进行编译;否则对程序段2进行编译。

例: 

#define NUM

.............

.............

.............

#ifdef NUM

 printf("之前NUM有过定义啦!:) \n");

#else

 printf("之前NUM没有过定义!:( \n");

#endif

}

 如果程序开头有#define NUM这行,即NUM有定义,碰到下面#ifdef NUM的时候,当然执行第一个printf。否则第二个printf将被执行。

 我认为,用这种,可以很方便的开启/关闭整个程序的某项特定功能。

2:情况2: 

#ifndef _XXXX 

...程序段1... 

#else 

...程序段2... 

#endif

 这里使用了#ifndef,表示的是if not def。当然是和#ifdef相反的状况(如果没有定义了标识符_XXXX,那么执行程序段1,否则执行程序段2)。例子就不举了。

3:情况3:

#if 常量 

...程序段1...

#else

...程序段2...

#endif 

 这里表示,如果常量为真(非0,随便什么数字,只要不是0),就执行程序段1,否则执行程序段2。我认为,这种方法可以将测试代码加进来。当需要开启测试的时候,只要将常量变1就好了。而不要测试的时候,只要将常量变0。

阅读全文
2 0
原创粉丝点击