1.请找出下面代码的问题。
- #include<stdio.h>
- int main(void)
- {
- char buff[10];
- memset(buff,0,sizeof(buff));
- gets(buff);
- printf("\n The buffer entered is [%s]\n",buff);
- 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.下面是一个简单的密码保护功能,你能在不知道密码的情况下将其破解吗?
答案:破解上述密码关键在于攻破strcpy函数的漏洞,并没有检查passwd的容量是否足够。所以如果用户输入一个足够照成缓存溢出并且重写flag默认值所存在位置的长密码,即使无法通过验证,flag验证位也变成了非零,也就可以获得被保护的数据了。
- #include<stdio.h>
- int main(int argc, char *argv[])
- {
- int flag = 0;
- char passwd[10];
- memset(passwd,0,sizeof(passwd));
- strcpy(passwd, argv[1]);
- if(0 == strcmp("LinuxGeek", passwd))
- {
- flag = 1;
- }
- if(flag)
- {
- printf("\n Password cracked \n");
- }
- else
- {
- printf("\n Incorrect passwd \n");
- }
- return 0;
- }
解析:要避免上述情况应采用strncpy函数。
3.问:下面的代码会导致内存泄漏吗?
答:尽管上面的代码并没有释放分配给“ptr”的内存,但并不会在程序退出后导致内存泄漏。在程序结束后,所有这个程序分配的内存都会自动被处理掉。但如果上面的代码处于一个“while循环”中,那将会导致严重的内存泄漏问题!
4.下面的程序会在用户输入'freeze'的时候出问题,而'zebra'则不会,为什么?
答案:当输入zebra时,while循环会在执行前被终止,因此传给free()的变量ptr就是传给malloc的地址;但是在freeze时,ptr存储的地址在while中会被修改,因此导致传给free的地址出错,也就导致了崩溃。
5.
在下面的代码中,atexit()并没有被调用,为什么?
答案:这是因为_exit()函数的使用,该函数并没有调用atexit()等函数清理。如果使用atexit()就应当使用exit()或者“return”与之相配合。
解析:用atexit()函数来注册程序正常终止时要被调用的函数。atexit()函数的参数是一个函数指针,函数指针指向一个没有参数也没有返回值的函数。atexit()的函数原型是:int atexit (void (*)(void));在一个程序中最多可以用atexit()注册32个处理函数,这些处理函数的调用顺序与其注册的顺序相反,也即最先注册的最后调用,最后注册的最先调用。
举例:
- #include<stdio.h>
- #include<stdlib.h>
- void func1(void)
- {
- printf("in func1\n");
- }
- void func2(void)
- {
- printf("in func2\n");
- }
- void func3(void)
- {
- printf("in func3\n");
- }
- int main()
- {
- atexit(func3);
- atexit(func2);
- atexit(func1);
- printf("In main\n");
- return 0;
- }
输出结果:
- In main
- in func1
- in func2
- in func3
6.下面的代码段有错,你能指出来吗?
答:这是因为,通过*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 中。 一般而言,打开 文件 后会做一些文件读取或写入的动作,若打开文件失败,接下来的读写动作也无法顺利进行,所以一般在f open ()后作错误判断及处理。参数说明:mode有下列几种形态字符串:r+ 以可读写方式打开文件,该文件必须存在。rw+ 读写打开一个文本文件,允许读和写。wb 只写打开或新建一个二进制文件;只允许写数据。wb+ 读写打开或建立一个二进制文件,允许读和写。ab+ 读写打开一个二进制文件,允许读或在文件末追加数据。