12个有趣的C语言问答(详解)
1 gets()方法
Q:下面的代码有一个被隐藏的问题,你能找到它吗?
?1 #include
?2
?3 int main(void)
?4 {
?5? ? char buff[10];
?6? ? memset(buff, 0, sizeof(buff));
?7? ? gets(buff);
?8? ? printf("%s\n", buff);
?9
10? ? return 0;
11 }
?
A:这个不显眼的问题就是使用了gets()方法,其函数原型如下:
char* gets(char *s);
此方法接受一个字符数组参数,但是却没有检查此数组是否有足够的空间来拷贝数据。gets()函数是不安全的,不推荐使用,一般情况下编译器也会给出警告提示:the `gets' function is dangerous and should not be used。gets()不检查预留存储区是否能够容纳实际输入的数据。多出来的字符简单的溢出到相邻的存储区,可能会导致错误。
所以,这里我们一般用fgets()方法更好,函数原型如下:
char* fgets(char *s, int n, FILE *stream);
一般使用fgets()函数,都是读取文件当中的n-1个字符到s中,其实,此函数还有一个很好的用处就是从标准输入流中读取字符串,而且不用担心输入的字符个数超出了字符数组的大小而导致溢出的问题!要怎样做呢?如下:
char str[10];?
fgets(str, siezof(str), stdin);
值得注意的是:谨记fgets()只读取n-1个字符。所以,fgets()读取到换行符、文件尾或读完n-1个字符便会进行返回。
?
2 strcpy()方法
Q:密码防护是很基本的功能,看看能够搞定下面这一段代码?
?
?1 #include
?2 #include
?3 int main(int argc, char *argv[])
?4 {
?5? ? int flag = 0;
?6? ? char passwd[10];
?7
?8? ? memset(passwd, 0, sizeof(passwd));
?9? ? strcpy(passwd, argv[1]);
10
11? ? if (0 == strcmp("LinuxGeek", passwd)){
12? ? ? ? flag = 1;
13? ? }
14? ? if (flag){
15? ? ? ? printf("\n Password cracked \n");
16? ? }else{
17? ? ? ? printf("\n Incorrect password \n");
18? ? }
19
20? ? return 0;
21 }
?
说明:该程序通过在运行时携带一个密码参数,然后程序会将用户输入的密码参数值与真实的密码比较,如果两者相等就输出cracked信息,否则输出incorrect提示。
?
3 main()函数的返回类型
Q:请问下面这段代码能否通过编译?如果能的话,那么这段代码中隐含什么问题吗?
?
#include
#include
void main(void)
{
? ? char *ptr = (char *)malloc(10);
? ? if (NULL == ptr){
? ? ? ? printf("\n Malloc failed \n");
? ? ? ? return;
? ? }else{
? ? ? ? //Do some processing
? ? ? ? free(ptr);
? ? }
? ? return;
}
?
A:代码能通过编译,但是会留下针对main()函数返回值类型的警告。main()函数的真正返回值类型应该是int而不是void,这是因为int返回类型可以返回程序运行的状态值,尤其是当这段程序作为其他应用的附属程序时这个状态值将更加重要。
mainret.c:3:6: warning: return type of ‘main’ is not ‘int’ [-Wmain]
?void main(void)
? ? ? ^
?
4 内存泄漏
Q:请问,以下代码有内存泄漏吗?
?
#include
#include
int main(void)
{
? ? char *ptr = (char*)malloc(10);
? ? if (NULL == ptr){
? ? ? ? printf("\n Malloc failed \n");
? ? ? ? return -1;
? ? }else{
? ? ? ? //Do some processing
? ? }
? ? return 0;
}
?
A:不会,虽然上面的代码没有对指针ptr进行内存释放,但实际上即使是程序结束也不会造成内存泄漏,因为当程序结束时所有一开始被占据的内存就全部清空了。但是,如果上面分配内存这段代码是在while循环里面那将会造成严重的问题。
?
5 free()方法
Q:以下代码,当用户输入'freeze'时会崩溃,而如果输入'zebra'则运行正常,为什么?
?
?1 #include
?2 #include
?3 #include
?4 int main(int argc, char *argv[])
?5 {
?6? ? char *ptr = (char *)malloc(10);
?7
?8? ? if (NULL == ptr){
?9? ? ? ? return -1;
10? ? }
11? ? if (argc == 1){
12? ? ? ? printf("\n Usage: add a string \n");
13? ? }else {
14? ? ? ? memset(ptr, 0, 10);
15? ? ? ? strncpy(ptr, argv[1], 9);
16? ? ? ? while (*ptr != 'z'){
17? ? ? ? ? ? if (*ptr == ' ') break;
18? ? ? ? ? ? else ptr++;
19? ? ? ? }
20? ? ? ? if (*ptr == 'z'){
21? ? ? ? ? ? printf("\n String contains 'z' \n");
22? ? ? ? ? ? //Do some more processing
23? ? ? ? }
24? ? ? ? free(ptr);
25? ? }
26
27? ? return 0;
28 }
?
A:问题的根源是因为代码在while循环中改变了 ptr 指针的地址。当输入为'zebra'时,while循环甚至在执行第一遍前就结束了,所以free()释放的内存地址就是一开始malloc()分配的地址。但是当输入'freeze'时, ptr记录的地址在while循环中被更改,因此将会使错误的地址传递到free()方法中引起崩溃。
注意:调用free()方法释放内存时,参数必须要么是NULL,要么是先前从malloc/calloc或者realloc返回的地址,不能将一次动态申请的内存的部分释放。
?
6 atexit()和_exit()
Q:以下代码中的atexit()方法并没有被调用,直到为什么吗?
?
#include
#include
void func(void)
{
? ? printf("\n Clean up function called \n");
}
int main(void)
{
? ? int i = 0;
? ? atexit(func);
? ? for (; i < 0xFFFF; i++);
? ? _exit(0);
}
?
A:这是因为使用了 _exit() 方法。此方法并没有调用清除数据相关的方法,比如 atexit()等。exit和_exit都是用来正常终止一个进程的,主要区别是_exit会立刻进入内核,而exit先执行一些清除工作(包括执行各种终止处理程序,关闭所有标准I/O等,一旦关闭了IO,例如printf等函数就不会输出任何东西了),然后才进入内核。这两个函数会对父子进程有一定的影响,当用vfork创建子