C/C++面试笔试题

来源:互联网 发布:看电影翻译软件 编辑:程序博客网 时间:2024/06/05 00:34


1.请找出下面代码的问题。

    1. #include<stdio.h> 
    2. int main(void
    3.     char buff[10]; 
    4.     memset(buff,0,sizeof(buff)); 
    5.     gets(buff); 
    6.     printf("\n The buffer entered is [%s]\n",buff); 
    7.     return 0; 

解答:函数get()的使用不当,这个函数从stdin接受字符串而不检查其复制缓存的容积,会导致缓存溢出。推荐使用函数fget()实现。

涉及到的知识点:

a:  memset函数按字节赋值,作用是在一段内存块中填充某个给定的值,它是对较大结构体或数组进行清零操作最快的方法

void *memset(void *s,int ch,size_t  n)含义将s前n个字节用ch替换并返回s

举例:将char a[20]清零,memset(a,0,20)

b: fget()和gets()函数的区别

(1):fgets()比gets()函数安全。gets没有指定输入字符的大小,限制输入缓存区的大小,如果输入字符串大于定义的数组长度,会发生内存越界堆栈溢出;而fgets()会根据数组定义长度截断。

(2):strlen检查两个输入字符串长度,结果不同,同样输入1 2 3 gets只有一次换行长度为3,fget有两次长度为4

c: fgets()函数的用法

fgets(str,n,fp)   str存放字符串起始地址,n是int类型变量,文件指针

表示意义:从文件fp所指文件中读入n-1个字符放入str为起始地址的内存空间内

举例:char buf[10];

    memset(buf,0,sizeif(buf));

    fgets(buf,10,stdin);



2.下面是一个简单的密码保护功能,你能在不知道密码的情况下将其破解吗?

  1. #include<stdio.h> 
  2.  
  3. int main(int argc, char *argv[]) 
  4.     int flag = 0; 
  5.     char passwd[10]; 
  6.     memset(passwd,0,sizeof(passwd)); 
  7.     strcpy(passwd, argv[1]); 
  8.     if(0 == strcmp("LinuxGeek", passwd)) 
  9.     { 
  10.         flag = 1; 
  11.     } 
  12.     if(flag) 
  13.     { 
  14.         printf("\n Password cracked \n"); 
  15.     } 
  16.     else 
  17.     { 
  18.         printf("\n Incorrect passwd \n"); 
  19.     } 
  20.     return 0; 
答案:破解上述密码关键在于攻破strcpy函数的漏洞,并没有检查passwd的容量是否足够。所以如果用户输入一个足够照成缓存溢出并且重写flag默认值所存在位置的长密码,即使无法通过验证,flag验证位也变成了非零,也就可以获得被保护的数据了。

解析:要避免上述情况应采用strncpy函数。


3.问:下面的代码会导致内存泄漏吗?

  1. #include<stdio.h> 
  2. void main(void
  3.     char *ptr = (char*)malloc(10); 
  4.     if(NULL == ptr) 
  5.     { 
  6.         printf("\n Malloc failed \n"); 
  7.         return
  8.     } 
  9.     else 
  10.     { 
  11.         // Do some processing 
  12.     } 
  13.     return

答:尽管上面的代码并没有释放分配给“ptr”的内存,但并不会在程序退出后导致内存泄漏。在程序结束后,所有这个程序分配的内存都会自动被处理掉。但如果上面的代码处于一个“while循环”中,那将会导致严重的内存泄漏问题!


4.下面的程序会在用户输入'freeze'的时候出问题,而'zebra'则不会,为什么?

  1. #include<stdio.h> 
  2. int main(int argc, char *argv[]) 
  3.     char *ptr = (char*)malloc(10); 
  4.     if(NULL == ptr) 
  5.     { 
  6.         printf("\n Malloc failed \n"); 
  7.         return -1; 
  8.     } 
  9.     else if(argc == 1) 
  10.     { 
  11.         printf("\n Usage  \n"); 
  12.     } 
  13.     else 
  14.     { 
  15.         memset(ptr, 0, 10); 
  16.         strncpy(ptr, argv[1], 9); 
  17.         while(*ptr != 'z'
  18.         { 
  19.             if(*ptr == ''
  20.                 break
  21.             else 
  22.                 ptr++; 
  23.         } 
  24.         if(*ptr == 'z'
  25.         { 
  26.             printf("\n String contains 'z'\n"); 
  27.             // Do some more processing 
  28.         } 
  29.        free(ptr); 
  30.     } 
  31.     return 0; 

答案:当输入zebra时,while循环会在执行前被终止,因此传给free()的变量ptr就是传给malloc的地址;但是在freeze时,ptr存储的地址在while中会被修改,因此导致传给free的地址出错,也就导致了崩溃。


5.

在下面的代码中,atexit()并没有被调用,为什么?

  1. #include<stdio.h> 
  2. void func(void
  3.     printf("\n Cleanup function called \n"); 
  4.     return
  5. int main(void
  6.     int i = 0; 
  7.     atexit(func); 
  8.     for(;i<0xffffff;i++); 
  9.     _exit(0); 

答案:这是因为_exit()函数的使用,该函数并没有调用atexit()等函数清理。如果使用atexit()就应当使用exit()或者“return”与之相配合。

解析:用atexit()函数来注册程序正常终止时要被调用的函数。atexit()函数的参数是一个函数指针,函数指针指向一个没有参数也没有返回值的函数。atexit()的函数原型是:int atexit (void (*)(void));在一个程序中最多可以用atexit()注册32个处理函数,这些处理函数的调用顺序与其注册的顺序相反,也即最先注册的最后调用,最后注册的最先调用。

举例:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. void func1(void)
  4. {
  5.         printf("in func1\n");
  6. }
  7. void func2(void)
  8. {
  9.         printf("in func2\n");
  10. }
  11. void func3(void)
  12. {
  13.         printf("in func3\n");
  14. }
  15. int main()
  16. {
  17.         atexit(func3);
  18.         atexit(func2);
  19.         atexit(func1);
  20.         printf("In main\n");
  21.         return 0;
  22. }

输出结果:
  1. In main
  2. in func1
  3. in func2
  4. in func3


6.

下面的代码段有错,你能指出来吗?

  1. #include<stdio.h> 
  2. int main(void
  3.     char *ptr = "Linux"
  4.     *ptr = 'T'
  5.     printf("\n [%s] \n", ptr); 
  6.     return 0; 

答:这是因为,通过*ptr = ‘T’,会改变内存中代码段(只读代码)“Linux”的第一个字母。这个操作是无效的,因此会造成seg-fault或者崩溃。


7.写一个运行时改变自己进程名的程序。

#include<stdio.h>

int main(int argc, char *argv[])

{

int i=0;

char buff[100];

memset(buff,0,sizeof(buff));

strncpy(buff,argv[0],sizeof(buff));

memset(argv[],0,sizeof(buff));

strncpy(argv[],"NewName",7);

for(;i<0xffffffff;i++)

return 0;

}


8.下面程序的输出结果是:

#include<stdio.h>
int func(int num)
{
static int a;
num+=++a;
return num;


}
int main()
{
int num=0;
func(num);
printf("%d",func(num));
}

输出结果:2


9.C/C++文件操作函数

(1)fseek函数的使用

int fseek( FILE *stream, long offset, int origin );

第一个参数stream为文件指针
第二个参数offset为偏移量,整数表示正向偏移,负数表示负向偏移
第三个参数origin设定从文件的哪里开始偏移

origin可能取值含义取值SEEK_SET
文件开头0
SEEK_CUR
当前位置
1
SEEK_END
文件结尾
2

  举例:
  fseek(fp,100L,0);把fp指针移动到离文件开头100字节处;
  fseek(fp,100L,1);把fp指针移动到离文件当前位置100字节处;
   fseek(fp,100L,2);把fp指针退回到离文件结尾100字节处。


(2)fopen的使用

函数原型:FILE * fopen(const char * path,const char * mode);
返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno 中。一般而言,打开文件后会做一些文件读取或写入的动作,若打开文件失败,接下来的读写动作也无法顺利进行,所以一般在fopen()后作错误判断及处理。
参数说明:
参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。
mode有下列几种形态字符串:
r 以只读方式打开文件,该文件必须存在。
r+ 以可读写方式打开文件,该文件必须存在。
rb+ 读写打开一个二进制文件,允许读写数据,文件必须存在。
rw+ 读写打开一个文本文件,允许读和写。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
wb 只写打开或新建一个二进制文件;只允许写数据。
wb+ 读写打开或建立一个二进制文件,允许读和写。
ab+ 读写打开一个二进制文件,允许读或在文件末追加数据。





0 0
原创粉丝点击