希望和广大热爱技术的童鞋一起交流,成长。
分类:
2011-08-14 10:40:30
原文地址:C语言易错点(指针和形参传递) 作者:g_programming
C语言易错点(指针和形参传递)
1. 零值比较
一般意义上的零值有布尔型,整型,指针,浮点型。
(1) 布尔型:C语言里面没有布尔类型,但是C99标准提供了一个库来表示,头文件为
1) #include
2)
3) int main(void)
4) {
5) bool mybool = true;
6) if(mybool)
7) printf("mybool is true\n");
8) else
9) printf("mybool is false\n");
10) printf("c bool size is %d\n", sizeof(mybool));
11) return 0;
12) }
结果为:
mybool is true
c bool size is 4
请按任意键继续. . .
注意和C++的布尔型的区别,C语言里面的布尔型是用整数来代替的,零是假,任何非零的值为真,C++的布尔型是一个一个字节的变量。
1) //#include
2) #include
3) using namespace std;
4) int main(void)
5) {
6) bool mybool = true;
7) if(mybool)
8) cout<<"mybool is true"<
9) else
10) cout<<"mybool is false"<
11) cout<<"c++ bool size is "<
12) return 0;
13) }
结果为:
mybool is true
c++ bool size is 1
请按任意键继续. . .
(2) 整型:同类bool型用法一样 int test = 0;
if(test){
}
else{
}
(3) 指针:指针的零值为空指针NULL, int *ptest = NULL;
if(test == NULL){
}
else{
}
(4) 浮点型:浮点型的数据与零值比较不是直接测试与零是否相等,而是测试是否在0值左右的一个范围内。
if(fabs(flag2) < 1e-5)
printf("flag2 is 0\n");
else
printf("flsg2 is not 0\n");
2. 函数形参传递
(1)Strcpy函数的实现:
char *mystrcpy(char *des, const char *sur)
{
char *p = des;
assert((des !=NULL ) && (sur != NULL));
while((*des++=*sur++) != '\0')
;
return p;
}
(2)示例1:
1) void getmemory1(char *p)
2) {
3) p = (char *)malloc(100);
4) }
5) void test1(void)
6) {
7) char *str = NULL;
8) getmemory1(str);
9) strcpy(str, "hello world1 \n");
10) printf(str);
11) }
由于传入函数getmemory1的参数为指针,编译器为其准备一个副本_p传入,存在在系统栈里面,我们在getmemory1函数里面,为其动态分配空间存在于堆上,但是里面使用的是p的副本,分配空间的头指针指向的是_p,而不是p,所有该函数结束时p的值仍然没有改变,这里函数结束后不会把分配的空间释放掉,因为其分配的空间在堆上,如果程序员不释放的话其空间要在程序结束后由操作系统收回。测试函数使用未改变的的指针,其值为NULL,所有会出错。
(3)示例2:
1) char* getmemory2(void)
2) {
3) char p[] = "hello world2 \n";//在栈上
4) return p;
5) }
6) void test2(void)
7) {
8) char *str = NULL;
9) str = getmemory2();
10) printf(str);
11) }
由于函数getmemory2在内部使用了char p[] = "hello world2 \n"; 所有我们这里说一下他和
char *p1 = "hello world2 \n";的区别,首先char p[] = "hello world2 \n"都是存放在栈上的,函数执行完会自动释放,而char *p1 = "hello world2 \n";却不一样,char *p1存放在栈上这与前面一样,但是"hello world2 \n"却是存放在常量区的,程序结束后由系统释放 ,而且char p[]是一个数组名,为常量,而char *p1不是,在getmemory2函数中,char p[] = "hello world2 \n"都是存放在栈上的,函数作用范围结束后会自动释放,所以,即使返回了p指针,我们打印出来的字符串也是乱码但是如果将函数内部char p[] = "hello world2 \n"改变为char *p1 = "hello world2 \n",则不会出错,程序代码如下:
1) char* getmemory2(void)
2) {
3) char *p1= "hello world2 \n";
4) return p1;
5) }
6) void test2(void)
7) {
8) char *str = NULL;
9) str = getmemory2();
10) printf(str);
11) }
从这里我们可以认为,char p[] = "hello world2 \n"是一个字符型数组,p为数组名不可以改变,而char *p1 = "hello world2 \n"是指针指向的字符串常量,p1为可变指针,而字符串常量存在常量区。
(4)外传:字符串常量,我们可以知道"hello world2 \n"是一个字符串常量,并将第一个值的位置给一个指针p1,我们来看一下下面的表达式:
"hello world2 \n"+1
其实我咋一看,感觉这表达式没什么意义,在看了C和指针才知道,其实这个也是很有意思的,
"hello world2 \n"+1指向的是字符串常量的第二个字符,这样我们干脆就把它当成一个指针来看,这样下面的表达式也就有了意义
*"hello world2 \n"
这个表达式很明显的意思就是取字符串常量的第一个字符,依次我们可以进行的运算就可以知道如
"hello world2 \n"[3]; //等效于*("hello world2 \n"+3)
注意:由于字符串常量位于常量区,他的生命周期是恒定不变的,而且由于是常量,它不允许改变它的值,是只读的。
(5)示例3:
1) void getmemory3(char **p, int num)
2) {
3) *p = (char *)malloc(num);
4) }
5) void test3(void)
6) {
7) char *str = NULL;
8) int num = 100;
9) getmemory3(&str,num);
10) printf(str);
11) }
这次这个示例可能有点难懂了,传入函数getmemory3可以说是指针的指针,假设这里编译器为其准备的副本为__p,那么*__p即是指向char类型的指针,那么我们可以像平常那样对其动态分配空间,你可能会有疑问,为什么这里可以为他动态分配呢,虽然编译值指定__p = p;
但是他们指向的地址都是同一个,*__p = *p,分配空间后的头指针付给*__P 相当于赋给了*p,但是最好不要用这个方法分配空间。
注意:释放分配的空间后将指针设置为NULL,避免形成野指针。
(6)示例4:
1) char* getmemory3(void)
2) {
3) char *p = (char *)malloc(100);
4) printf("len is %d \n",strlen(mystrcpy(p, "hello mywold3")));
5) return p;
6) }
7) void test3(void)
8) {
9) char *str = NULL;
10) str = getmemory3();
11) printf(str);
12) }
getmemory3函数里面,char *p = (char *)malloc(100);分配空间,虽然指针p存在于系统栈在函数结束后将消亡,但是分配的空间存在于堆不会,返回分配的首指针,使该动态分配的区域有指针指向,置于可用状态。
3. 指针
我们这里直接通过下面一段代码来讲解:
char ch[7]="13579";
char cr = 'm';
char *cp = &cr;
printf(" &cr is %0x\n", &cr);//取cr变量的地址,也就是’m’存储的位置
printf(" cp is %0x\n", cp);/变量赋值的时候就是为’m’存储的位置
printf(" &cp is %0x\n", &cp);//取cp的地址
printf(" *cp is %c\n", *cp);//从存储’m’的位置,取出m
cp = ch;
printf(" *cp+1 is %c\n", *cp+1);//从存储’m’的位置,取出m并将m的值加‘*’优先级高
printf(" *(cp+1) is %c\n", *(cp+1));//地址cp加单位字节,然后取出+1地址的内容
printf(" cp is %0x\n", cp)
printf(" ++cp is %0x\n", ++cp);//地址cp先加单位字节再使用
printf(" cp is %0x\n", cp);
printf(" cp++ is %0x\n", cp++);//地址cp先使用再加单位字节
printf(" *cp is %c\n", *cp);
printf(" *++cp is %c\n", *++cp);//*,++优先级相同,自右向左,先指针加1再取内容
printf(" *cp is %c\n", *cp);
printf(" *cp++ is %c\n", *cp++);//*,++优先级相同,自右向左,先取内容再指针加1
//其实这里挺奇怪的 两者优先级相同,且是从右向左,那就应该是先++,但是我们这里可以这//样认为两者操作符没有挨着,所有遵循从左到右,先取值,再指针+1
printf(" *cp is %c\n", *cp);
printf(" ++*cp is %c\n", ++*cp);// *,++优先级相同,自右向左,先取内容再内容加1