有趣的c语言面试题,12到C++面试题(有趣)

1. gets()函数

Q:下面的代码中隐含着安全问题,能发现吗?

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 #include2 int main(void)

3 {

4   char buff[10];

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

6

7   gets(buff);

8

9   printf("n The buffer entered is [%s]n",buff);

10

11   return 0;

12 }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

A:问题在于gets()函数,这个函数是接收标准输入的一串字符串,并且没有检查字符串缓冲区的大小就

直接拷贝到buff数组中,这可能导致在写入buff内存时溢出,可以使用fgets()函数代替这个函数,

char *fgets(char *str,int n,FILE *stream

);

2.strcpy()函数

Q:下面代码是一个密码验证的过程,是否可以在不知道密码的情况下验证通过

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 #include2 #include 3

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

5 {

6

7   char passwd[10];

8   int flag = 0;

9   memset(passwd,0,sizeof(passwd));

10

11   strcpy(passwd, argv[1]);

12

13   if(0 == strcmp("LinuxGeek", passwd))

14   {

15     flag = 1;

16   }

17

18   if(flag)

19   {

20     printf("n Password cracked n");

21   }

22   else

23   {

24     printf("n Incorrect passwd n");

25

26   }

27   return 0;

28  }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

A:strcpy()函数没有验证输入的字符串长度,所有在执行时可能出现写内存出现溢出,这很危险,如这代码,

flag是初始化为0的,当内存溢出时可能会写到flag内存中,这会使得flag内存不为0,即使不执行if语句的

比较flag也为真,所以就相当于密码正确

如:

$ ./psswd aaaaaaaaaaaaa

输出:

Password cracked

可以使用strncpy()函数代替

现在编译器也发现这种情况,所以在为程序分配内存时是分散的分配内存,如果要看到上面执行的情况,使用gcc的话

可以使用 ‘-fno-stack-protector’参数(我没验证)

3.main()函数的返回类型

Q:下面代码是否能编译通过?是否还存在其他问题

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 #include2

3 void main(void)

4 {

5   char *ptr = (char*)malloc(10);

6

7   if(NULL == ptr)

8   {

9     printf("n Malloc failed n");

10     return;

11   }

12   else

13   {

14     // Do some processing

15

16     free(ptr);

17   }

18

19   return;

20 }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

A:对于现在的编译器这段代码是可以编译通过的,不过是会有警告,main()返回类型最好使用int类型,

当一个函数执行结束时最后返回一个状态值,现在C/C++返回一个0值表示程序正常退出,否则有异常.

4.内存泄露

Q:下面代码执行结果会出现内存泄露吗?

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 #include2

3 void main(void)

4 {

5   char *ptr = (char*)malloc(10);

6

7   if(NULL == ptr)

8   {

9     printf("n Malloc failed n");

10     return;

11   }

12   else

13   {

14     // Do some processing

15   }

16

17   return;

18 }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

A:其实内存泄露是个很严重的问题,其实上面代码执行结果不会出现内存泄露,虽然没有使用free()

回收内存,但是当程序执行结束后程序里分配的内存会自动释放,如果是分配的内存放到一个死循环里

就会出现严重的内存泄露,或者程序一直执行着,动态分配的内存会一直占有着无法释放.

有篇文章介绍了内存泄露的检测方法:http://www.cnblogs.com/skynet/archive/2011/02/20/1959162.html

5.free()函数

Q:下面代码执行时输入如 ‘freeze’会崩溃但输入如t ‘zebra’就不会为什么?

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 #include2

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

4 {

5 char *ptr = (char*)malloc(10);

6

7 if(NULL == ptr)

8 {

9 printf("n Malloc failed n");

10 return -1;

11 }

12 else if(argc == 1)

13 {

14 printf("n Usage n");

15 }

16 else

17 {

18 memset(ptr, 0, 10);

19

20 strncpy(ptr, argv[1], 9);

21

22 while(*ptr != 'z')

23 {

24 if(*ptr == '')

25 break;

26 else

27 ptr++;

28 }

29

30 if(*ptr == 'z')

31 {

32 printf("n String contains 'z'n");

33 // Do some more processing

34 }

35

36 free(ptr);

37 }

38

39 return 0;

40 }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

A:这个问题主要是指针移到的问题,当输入如‘zebra’这样的字符串('z'开头)时,ptr指针没有移到,所以

ptr指针指向的内存还是malloc分配的原内存的起始地址,但是输入‘freeze’时,ptr指针移到了,已经不是指向原来

分配的内存的起始地址了,所有free时就会出错

题外话:在实现如strcpy的函数时,如下

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 char *strcpy(char *strDest, const char *strSrc)

2 {

3 assert((strDest != NULL) && (strSrc != NULL));

4

5 if(strDest == strSrc)

6 return strDest; //注意这个..

7

8 char *pstr = strDest; //保存原始地址

9 while((*strDest++ = *strSrc++) != ' ');

10 return pstr;

11 }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

定义了一个指针pstr保存了原始地址,然后再执行移到操作,结束后再返回写入的原始地址以便进行链式操作,

我实现这个函数的时候就是出现了一个很二的错误,就是移到操作后直接返回strDest指针,现在的strDest指针

已经移到字符串末尾了,如果那样的话要释放strDest内存就会出错了

6.atexit和_exit

Q:下面代码中,atexit()函数没有被调用,why?

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 #include2 void func(void)

3 {

4 printf("n Cleanup function called n");

5 return;

6 }

7

8 int main(void)

9 {

10 int i = 0;

11

12 atexit(func);

13

14 for(;i<0xffffff;i++);

15

16 _exit(0);

17 }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

A:主要是因为_exit()函数,这个函数是直接终止程序,没有调用一些如atexit()的清理函数,要想调用

atexit()函数,就需要调用exit()函数或者return返回操作退出程序.

MSDN给出了exit()和_exit()函数的区别

Theexit and

_exit functions terminate the calling process.exit calls,

in last-in-first-out (LIFO) order,

the functions registered byatexit and

_onexit, then flushes all file buffers

before

terminating the process. _exit terminates the process without processing

atexit

or

_onexit or flushing stream buffers. The status value is typically set to 0 to indicate

a

normalexit and

set to some other value to indicate an error

7.void* 和 C 结构体

Q:能否设计一个函数能够接受任何类型的参数? 也可以传递多个参数给这个函数?

A:

int func(void *ptr);

传递的时候需要强制转换为void*类型,到函数内以后再强制转换回来,如果要传多个参数,可以定义一个

结构体,将要传递的参数放到结构体里,定义一个结构体对象,对成员赋值后,将对象传给函数.

注:我不知道在哪里看到有人说C/C++是不安全的语言,因为可以类型强制转换,但在这里发现强制转换

带来的好处.

8. * 和 ++ 操作

Q:下面的代码输出的结果是什么? and Why?

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 #include2

3 int main(void)

4 {

5 char *ptr = "Linux";

6 printf("n [%c] n",*ptr++);

7 printf("n [%c] n",*ptr);

8

9 return 0;

10 }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

A:输入的结果:

[L]

[i]

Why? 这个涉及到*号和++的优先级问题,*和++的优先级是一样的,编译器在识别时是先右后左,

所有先检测到++后到*,不过++是在ptr后面,所以自加在*ptr后执行,输出L,然后移动到i处,下

一条语句执行的时候输出i.

原作者给出来的解释我不太理解

(ptr++ is evaluated first and then *ptr. So both these operations result in ‘L’),

为什么说两个操作结果都是'L'???

9.改变代码区(只读区)

Q:下面代码为什么会崩溃?

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 #include2

3 int main(void)

4 {

5 char *ptr = "Linux";

6 *ptr = 'T';

7

8 printf("n [%s] n", ptr);

9

10 return 0;

11 }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

A:因为 *ptr = 'T' 操作尝试改变在代码区的 "Linux"的字符串,这是不合法的,其实如果要改变的话

可以先动态分配一块内存(在堆区),然后再Copy "Linux"到这块内存中,可以就可以执行*ptr = 'T'操作

10.程序改变自己的名字

Q:如何实现一个程序在运行的时候改变自己的名字

A:这个需要知道main()函数的两个参数的意义了

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 #include2 #include 3 int main(int argc, char *argv[])

4 {

5 int i = 0;

6 char buff[100];

7

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

9

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

11 memset(argv[0],0,strlen(buff));

12

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

14

15 // Simulate a wait. Check the process

16 // name at this point.

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

18

19 return 0;

20 }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

main()函数的argc表示的是传进来的参数个数,argv[]保存的是参数的内容,但是

argv[0]保存的是程序自己名字

11.返回局部变量地址

Q:下面的代码是否存在问题?如果有的话那如何修改?

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 #include2

3 int* inc(int val)

4 {

5 int a = val;

6 a++;

7 return &a;

8 }

9

10 int main(void)

11 {

12 int a = 10;

13

14 int *val = inc(a);

15

16 printf("n Incremented value is equal to [%d] n", *val);

17

18 return 0;

19 }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

A:问题是返回了一个局部变量的地址,a的作用域只在inc()函数中,函数结束后a的内存会释放,如果使用

一个已经被释放了的内存相当危险,解决方法可以传给inc()函数的参数修改为传地址或者引用(C++)

不使用值传递,int* inc(int *val)或者int* inc(int &val)

12.printf()函数参数的执行

Q:代码的输出结果是什么?

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

1 #include2

3 int main(void)

4 {

5 int a = 10, b = 20, c = 30;

6

7 printf("n %d..%d..%d n", a+b+c, (b = b*2), (c = c*2));

8

9 return 0;

10 }

b75e+bgQ7oJ4xae1XGylllAz1zLO5aqyl8cmwnY3B6u6avp0NbxgcO9ujRL3Q4tFpcltwPGJPdfjtALJqGg18svFXgZe1Bj43ao

A:输出的结果为

110..40..60

函数的参数是从右到左执行的,但是打印是从左到右的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值