12个有趣的C语言问答(详解)
本文参照博文《12个有趣的C语言问答》,在原文的基础上增加来对应的知识点的详细介绍。
1 gets()方法
Q:下面的代码有一个被隐藏的问题,你能找到它吗?
#include <stdio.h> int main(void)
{
char buff[];
memset(buff, , sizeof(buff));
gets(buff);
printf("%s\n", buff); return ;
}
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[];
fgets(str, siezof(str), stdin);
值得注意的是:谨记fgets()只读取n-1个字符。所以,fgets()读取到换行符、文件尾或读完n-1个字符便会进行返回。
2 strcpy()方法
Q:密码防护是很基本的功能,看看能够搞定下面这一段代码?
#include <stdio.h>
#include <memory.h>
int main(int argc, char *argv[])
{
int flag = ;
char passwd[]; memset(passwd, , sizeof(passwd));
strcpy(passwd, argv[]); if ( == strcmp("LinuxGeek", passwd)){
flag = ;
}
if (flag){
printf("\n Password cracked \n");
}else{
printf("\n Incorrect password \n");
} return ;
}
说明:该程序通过在运行时携带一个密码参数,然后程序会将用户输入的密码参数值与真实的密码比较,如果两者相等就输出cracked信息,否则输出incorrect提示。
3 main()函数的返回类型
Q:请问下面这段代码能否通过编译?如果能的话,那么这段代码中隐含什么问题吗?
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
char *ptr = (char *)malloc();
if (NULL == ptr){
printf("\n Malloc failed \n");
return;
}else{
//Do some processing
free(ptr);
}
return;
}
A:代码能通过编译,但是会留下针对main()函数返回值类型的警告。main()函数的真正返回值类型应该是int而不是void,这是因为int返回类型可以返回程序运行的状态值,尤其是当这段程序作为其他应用的附属程序时这个状态值将更加重要。
mainret.c::: warning: return type of ‘main’ is not ‘int’ [-Wmain]
void main(void)
^
4 内存泄漏
Q:请问,以下代码有内存泄漏吗?
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *ptr = (char*)malloc();
if (NULL == ptr){
printf("\n Malloc failed \n");
return -;
}else{
//Do some processing
} return ;
}
A:不会,虽然上面的代码没有对指针ptr进行内存释放,但实际上即使是程序结束也不会造成内存泄漏,因为当程序结束时所有一开始被占据的内存就全部清空了。但是,如果上面分配内存这段代码是在while循环里面那将会造成严重的问题。
5 free()方法
Q:以下代码,当用户输入'freeze'时会崩溃,而如果输入'zebra'则运行正常,为什么?
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
int main(int argc, char *argv[])
{
char *ptr = (char *)malloc(); if (NULL == ptr){
return -;
}
if (argc == ){
printf("\n Usage: add a string \n");
}else {
memset(ptr, , );
strncpy(ptr, argv[], );
while (*ptr != 'z'){
if (*ptr == ' ') break;
else ptr++;
}
if (*ptr == 'z'){
printf("\n String contains 'z' \n");
//Do some more processing
}
free(ptr);
} return ;
}
A:问题的根源是因为代码在while循环中改变了 ptr 指针的地址。当输入为'zebra'时,while循环甚至在执行第一遍前就结束了,所以free()释放的内存地址就是一开始malloc()分配的地址。但是当输入'freeze'时, ptr记录的地址在while循环中被更改,因此将会使错误的地址传递到free()方法中引起崩溃。
注意:调用free()方法释放内存时,参数必须要么是NULL,要么是先前从malloc/calloc或者realloc返回的地址,不能将一次动态申请的内存的部分释放。
6 atexit()和_exit()
Q:以下代码中的atexit()方法并没有被调用,直到为什么吗?
#include <stdio.h>
#include <unistd.h> void func(void)
{
printf("\n Clean up function called \n");
} int main(void)
{
int i = ; atexit(func);
for (; i < 0xFFFF; i++);
_exit();
}
A:这是因为使用了 _exit() 方法。此方法并没有调用清除数据相关的方法,比如 atexit()等。exit和_exit都是用来正常终止一个进程的,主要区别是_exit会立刻进入内核,而exit先执行一些清除工作(包括执行各种终止处理程序,关闭所有标准I/O等,一旦关闭了IO,例如printf等函数就不会输出任何东西了),然后才进入内核。这两个函数会对父子进程有一定的影响,当用vfork创建子进程时,子进程会先在父进程的地址空间运行(这跟fork不一样),如果子进程调用了exit就会把父进程的IO给关掉。
补充:还有一种在程序退出前执行相应函数的方法,就是调用<stdlib.h>中提供的_onexit()回调函数,用法如下:
#include <iostream>
#include <string>
#include <stdlib.h> using namespace std; int fun(void){
cout << "Exit Function\n";
return ;
} int main()
{
_onexit(fun);
cout << "Finished.\n";
return ;
}
需要注意的是,回调函数要求返回值必须是int类型,否则会报错!
7 void*与C结构体
Q:能够设计一个方法接受任意类型的参数然后返回整数?同时,是否有办法传递多个这样的参数?
A:一个能接受任意类型参数的方法像下面这个样子:
int func(void *ptr)
如果需要传递多个参数,那么我们可以传递包含这些参数的结构体。
8 *与++运算符
Q:以下代码将输出什么?为什么?
#include <stdio.h>
int main(void)
{
char *ptr = "Linux";
printf("\n [%c] \n", *ptr++);
printf("\n [%c] \n", *ptr);
return ;
}
A:程序的输出结果如下:
[L] [i]
因为++与 * 的优先级一样,所以 *ptr++ 将会从右向左操作。按照这个逻辑,ptr++ 会先执行然后执行*ptr。所以第一个结果是'L'。也因为 ++ 被执行了,所以下一个printf() 结果是'i'。
9 Making changes in code segment
Q:以下代码运行时一定会崩溃,你能说出原因吗?
#include <stdio.h>
int main(void)
{
char *ptr = "Linux";
*ptr = 'T';
printf("\n [%s] \n", ptr); return ;
}
A:这是因为字符串常量“Linux”是以只读的形式存储的,而通过*ptr='T'语句,此代码尝试更改只读内存存储的字符串内容,此操作当然行不通,所以才会导致崩溃。
10 Process that changes its own name
Q:你能否写一个程序,在它运行时修改它的名称?
A:以下的代码可以:
#include <stdio.h>
#include <memory.h> int main(int argc, char *argv[])
{
int i = ;
char buff[]; memset(buff, , sizeof(buff));
strncpy(buff, argv[], sizeof(buff)); memset(argv[], , strlen(buff));
strncpy(argv[], "NewName", );
//Simulate a wait. Check the process name at this point
for (; i < 0xFFFFFFFF; i++); return ;
}
可以通过下面的方法测试
$ gcc chname.c -o chname
$ ./chname &
[]
$ ps
PID TTY STAT TIME COMMAND
pts/ R : NewName
11 局部变量的返回地址
Q:下面的代码有问题吗?如果有,如何修改?
#include <stdio.h>
int* inc(int val)
{
int a = val;
a++;
return &a;
} int main(void)
{
int a = ;
int *val = inc(a);
printf("\n Increamented value is equal to [%d] \n", *val); return ;
}
A:虽然上面的代码有时运行会很好,但是在方法 inc() 中有很严重的隐患,因为它返回了局部变量的地址。当inc()方法执行后,再次使用局部变量的地址就会造成不可估量的结果。解决之道就是传递变量a的地址给main()。PS:我觉得最后一句的说法有问题。
12 处理printf()参数
Q:请问以下代码的输出是什么?
#include<stdio.h> int main( void )
{
int a = , b = , c = ; printf ("\n %d..%d..%d \n", a+b+c, (b = b*), (c = c*));
return ;
}
A:程序的输出如下:
....
这是因为参数都是从右向左处理的,然后打印出来却是从左向右。
12个有趣的C语言问答(详解)的更多相关文章
- 《12个有趣的C语言问答》(4)
C语言面试问答——<12个有趣的C语言问答>评析(4) 前文链接:http://www.cnblogs.com/pmer/p/3324063.html 8,Making changes i ...
- 《12个有趣的C语言问答》评析2
<12个有趣的C语言问答>评析(2) 前文链接:http://www.cnblogs.com/pmer/p/3313913.html (没存盘,遭遇过热保护.至少4个问答的评论白写了.默哀 ...
- 12个滑稽的C语言面试问答——《12个有趣的C语言问答》评析(5)
前文链接:http://www.cnblogs.com/pmer/archive/2013/09/17/3327262.html A,局部变量的返回地址 Q:下面的代码有问题吗?如果有,如何修改? # ...
- 12个有趣的C语言问答
转自:http://www.admin10000.com/document/913.html 1,gets() 方法 Q:以下代码有个被隐藏住的问题,你能找到它吗? 1 2 3 4 5 6 7 8 9 ...
- 12个有趣的c语言面试题
1.gets()函数 问:请找出下面代码里的问题: #include int main(void) { char buff[10]; memset(buff,0,sizeof(buff)); gets ...
- 深入理解C语言 - 指针详解
一.什么是指针 C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址.CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位.这里,数据对象是指存储在 ...
- c++、Java、python对应的编译型语言和解释性语言区别详解
1.首先明确一点: 高级语言是不能直接在CPU上运行的.CPU只能处理机器语言,就是黑客帝国里面那个10101010101110的数字流. 那么为了让机器语言能够在CPU上运行,那么就必须将其变成机器 ...
- 12个有趣的C语言面试题
摘要:12个C语言面试题,涉及指针.进程.运算.结构体.函数.内存,看看你能做出几个! 1.gets()函数 问:请找出下面代码里的问题: #include<stdio.h> int ma ...
- C语言关键字详解
相对于其他语言来说,C语言的关键字算是少的了.在C98中关键子总共只有32个,我们来分析一下每个关键字在C语言中它独特的作用. 1.关于数据类型的关键字 (1) char :声明字符型变量或函数 ( ...
随机推荐
- glog摘记
projcet url:https://code.google.com/p/google-glog/ usage: application-level logging setting flags GL ...
- Android 电子邮件发送成功与失败的提示
前言 欢迎大家我分享和推荐好用的代码段~~ 声明 欢迎转载.但请保留文章原始出处: CSDN:http://www.csdn.net ...
- java复习1 java简单介绍
在学校的时候.学JAVA学的模棱两可,半知半解.工作以后给我带来了非常大的困扰,所以我须要在学一遍.如今就開始吧... . java[1]是一种能够撰写跨平台应用软件的面向对象的程序设计语言,是由Su ...
- 在Struts2中使用ValueStack、ActionContext、ServletContext、request、session等 .
笔者不知道该用哪个词来形容ValueStack.ActionContext等可以在Struts2中用来存放数据的类.这些类使用的范围不同,得到的方法也不同,下面就来一一介绍. 声明:本文参考Strut ...
- 如何选择一个 Linux Tracer (2015)
http://www.oschina.net/translate/choosing-a-linux-tracer
- 深入理解C++中的mutable关键字
mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词. 在C++中,mutable也是为了突破const的限制而设置的.被mutable修饰的变量,将 ...
- JavaScript网站设计实践(三)设计有特色的主页,给主页链接添加JavaScript动画脚本
一.主页一般都会比较有特色,现在在网站设计(二)实现的基础上,来给主页添加一点动画效果. 1.这里实现的动画效果是:当鼠标悬停在其中某个超链接时,会显示出属于该页面的背景缩略图,让用户知道这个链接的页 ...
- android学习——popupWindow 在指定位置上的显示
先看效果图,免得浪费大家时间,看是不是想要的效果 . 直接上代码 ,核心方法. [java] view plaincopy private void showPopupWindow(View pare ...
- 2.CentOS更换阿里源
第一步:备份本地yum源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 第二步:下载阿里y ...
- MATLAB基础入门笔记
为了参加那个电工杯,豁出去啦,时间真的很短,但是得挑战呀..对于MATLAB编程,有一些了解,MATLAB(矩阵实验室的简称)是一种专业的计算机程序,用于工程科学的矩阵数学运算,说说它的开发环境. 任 ...