您的位置:首页 > 编程语言 > C语言/C++

C语言一些细节

2015-06-18 22:06 323 查看

1、gets()方法

gets()函数是用来接受一个字符串的函数,此方法接受一个string类型参数,但是却没有检测此数值是否 有足够的空间来拷贝数据。由于gets()无法知道字符串的大小,必须遇到换行字符或文件尾才会结束输入,因此容易造成缓存溢出的安全性问题。建议使用fgets()取代。现在用个小程序说明一下到底有什么危害。

#include <stdio.h>
int main(void)
{
char a;
char name[3];
char b;
scanf("%c",&a);
printf("a:%c\n",a);            //第一次输出a的值
getchar();
gets(name);
scanf("%c",&b);
printf("name:%s;\na:%c;\nb:%c;\n",name,a,b);    //输出所有的值,注意a
}


运行结果如下:



增加一下字符串长度试试。



把字符串长度增加后,a的值莫名其妙被替换了,随后编辑器就不能正常工作了。

如例子中看到的一样,如果不能正确使用gets()造成的危害还是挺大的,会造成缓存溢出,覆盖其他位置的值。所以出于安全考虑,应该尽量用fgets()代替gets()。

2、strcpy() 方法

strcpy() 是用来复制字符串的函数。通过小程序看看strcpy() 的功能。

#include<stdio.h>
#include<string.h>
#define MAX 20

int main(void)
{
char a[MAX]="abc";
char b[MAX]="abcdefghi";
strcpy(a,b);
puts(a);
puts(b);
}


运行结果如下:



 此函数中还有两个高级属性——

  ①:它是char *类型,它返回的是第一个参数的值,即一个字符的地址。

  ②:第一个参数不需要指向数组的开始。

先附上代码来说明这两个属性:

#include<stdio.h>
#include<string.h>
#define MAX 40

int main(void)
{
char *a="beast";
char b[MAX]="you are the beast one.";
char *p;
p=strcpy(b+8,a);
puts(a);
puts(b);
puts(p);
}


b+8的位置是the的第一个字母t,

看到这个代码你是不是认为结果如下:

beast

you ate beasteast one

beasteast one

结果呢?



为什么会这样呢?

我们再来看看strcpy()函数的功能:将一个字符串复制到另一个字符串。这个代码无疑是把字符串a复制到b的第八个位置之后。

但是字符串的特性是什么呢?字符串最后一个字节存放的是一个空字符——“\0”,用来表示字符串的结束。把a复制到b之后会令a的空字符把复制后的字符串隔断,所以会显示到beast就会结束。

而输出p怎么会输出beast呢?这就是此函数的第一个属性,此函数会返回复制之后的字符串的首地址。所以还是还是字符串。

可能到这里你已经发现了一些问题,如果想把一个字符串的一部分复制到另一个字符串的某个位置,该怎么办呢,显然strcpy()函数是满足不了这个功能的,strncpy()函数是为了弥补strcpy()函数不能检查目标字符串是否容纳下源字符串的不足而设定的一个函数。并且完全可以实现这个功能。

#include<stdio.h>
#include<string.h>
#define MAX 30

int main(void)
{
char *a="abcdefg";
char b[MAX]="you are the beast one.";
strncpy(b+4,a,3);
puts(b);
}


这段代码把字符串a的前三个字符赋值到b的第五个位置之后,所以结果如下:



3、main() 方法的返回类型

Q:请问下面这段代码能否通过编译?如果能的话,那么这段代码中隐含什么问题吗?

#include<stdio.h>
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’返回类型能够让程序返回状态值。尤其是当这段程序作为其他应用的附属程序时这个状态值将更加重要。

4、内存泄漏

#include<stdio.h>
void main(void)
{
char *ptr = (char*)malloc(10);
if(NULL == ptr)
{
printf("\n Malloc failed \n");
return;
}
else
{
// Do some processing
}
return;
}


A:好,虽然上面的代码没有对指针 ptr 进行内存释放,但实际上即使是程序结束也不会造成内存泄露,因为当程序结束时所有一开始被占据的内存就全部清空了。但如果上面这段代码是在 while 循环里面那将会造成严重的问题

Note: 如果你需要了解更多关于内存泄露的问题以及如何使用工具检测内存泄露,你可以参考这篇文章 Valgrind

5、free()方法

Q:以下代码当用户输入’freeze’时会奔溃,而如果输入’zebra’则运行正常,这是为什么?

#include<stdio.h>
int main(int argc, char *argv[])
{
char *ptr = (char*)malloc(10);
if(NULL == ptr)
{
printf("\n Malloc failed \n");
return -1;
}
else if(argc == 1)
{
printf("\n Usage  \n");
}
else
{
memset(ptr, 0, 10);
strncpy(ptr, argv[1], 9);
while(*ptr != 'z')
{
if(*ptr == '')
break;
else
ptr++;
}
if(*ptr == 'z')
{
printf("\n String contains 'z'\n");
// Do some more processing
}
free(ptr);
}
return 0;
}


A:问题的根源是因为代码在while循环中改变了 ptr 指针的地址。当输入为’zebra’时,while循环甚至在执行 第一遍前就结束了,所以free()释放的内存地址就是一开始malloc()分配的地址。但是当输入’freeze’时, ptr记录的地址在while循环中被更改,因为将会是错误的地址传递到free()方法中引起崩溃。

6、atexit with _exit

Q:在以下代码,atexit()方法并没有被调用,你知道为什么吗?

#include<stdio.h>
void func(void)
{
printf("\n Cleanup function called \n");
return;
}
int main(void)
{
int i = 0;

atexit(func);

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

_exit(0);
}


A:这是因为使用了_exit() 方法。此方法并没有调用清除数据相关的方法,比如 atexit()等。

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 0;
}


A:以上的输出将是:

因为++与 * 的优先级一样,所以 *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 0;
}


A:这是因为,通过 *ptr = ‘T’,此行代码尝试更改只读内存存储的字符串’Linux’。此操作当然行不通所以才会造成崩溃。

10、Process that changes its own name

Q:你能否写一个程序在它运行时修改它的名称?

#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],0,strlen(buff));

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

// Simulate a wait. Check the process

// name at this point.
for(;i<0xffffffff;i++);

return 0;
}


11、局部变量的返回地址

Q:下面的代码有问题吗?如果有,如何修改?

#include<stdio.h>

int* inc(int val)
{
int a = val;
a++;
return &a;
}

int main(void)
{
int a = 10;

int *val = inc(a);

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

return 0;
}


A:虽然上面的代码有时运行会很好,但是在方法 inc() 中有很严重的隐患。当inc()方法执行后,再次使用局部变量的地址就会造成不可估量的结果。解决之道就是传递变量a的地址给main()。

12、处理 printf() 参数

Q:以下代码输出请问是什么?

#include<stdio.h>

int main(void)
{
int a = 10, b = 20, c = 30;

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

return 0;
}


A:输出将是

110..40..60


这是因为参数都是从右向左处理的,然后打印出来却是从左向右。

参考链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: