C语言进阶——goto 和 void 的分析08
遭人遗弃的goto:
- 高手潜规则:禁止使用goto
- 项目经验:程序质量与goto的出现次数成反比
- 最后的判决:将goto打入冷宫
程序示例1:(goto副作用分析)
1 #include <stdio.h> 2 #include <malloc.h> 3 4 void func(int n) 5 { 6 int* p = NULL; 7 8 if( n < 0 ) 9 { 10 goto STATUS; 11 } 12 13 14 p = (int*)malloc(sizeof(int) * n); 15 16 STATUS: 17 p[0] = n; 18 19 free(p); 20 } 21 22 int main() 23 { 24 printf("begin...\n"); 25 26 printf("func(1)\n"); 27 28 func(1); 29 30 printf("func(-1)\n"); 31 32 func(-1); 33 34 printf("end...\n"); 35 36 return 0; 37 }
在函数中,从第8行到第16行本来是顺序执行的,可是当满组if条件的时候,就会破环这个程序原来的结构,导致出现错误。
goto语句就介绍到这里,下面来讲一下void:
void修饰函数返回值和参数:
- 如果函数没有返回值,那么就应该将其声明为void
- 如果函数没有参数,应该声明其参数为void
程序示例2:
1 #include<stdio.h> 2 3 f() 4 { 5 6 } 7 8 int main() 9 { 10 int i= f(1,2,3,4); 11 printf("%d\n",i); 12 return 0; 13 }
上面这段程序,可能会有人认为它在编译的时候会报错,其实不然:
- 当函数没有返回值的时候,会默认返回一个int类型,也就是说你可以在这个函数里面加上一条return用于返回一个int语句,但是如果你不加的话,编译器会默认给你返回一个1。
- 当函数的参数列表没有任何参数,且没有声明为void,默认可以接受任意多个参数
为什莫会这样呢,因为C语言不是一种强类型的语言。
程序示例3:
#include<stdio.h> void f(void) { return 3; } int main() { int i= f(1,2,3,4);//error printf("%d\n",i); return 0; }
这段代码是将示例2中的代码稍加修改得到的,现在这段代码是不可以通过编译的。
void的意义,有人想到,void既然是一种类型,那么我们是否可以像使用其他类型,比如说int,char 一样的使用它来创建变量呢?答案是否定的,你可以将void理解为一种抽象类型。
不存在void普通变量(指针变量例外),C语言中没有定义void究竟是多大内存的别名,也就是说没有void标尺,无法在内存中裁剪出void对应的变量。
小贴士:
ANSIC:标准C语言规范
扩展C:在ANSIC的基础上进行了扩充
程序示例4:
1 #include<stdio.h> 2 3 int main() 4 { 5 printf("%d\n",sizeof(void)); 6 return 0; 7 }
上面这段代码在ANSIC编译器中无法通过编译,但是对于支持GNU标准的额gcc编译器而言是合法的。(但是我们最好是按照C语言标准来学习C语言,所以我们可以认为上面这段代码是不合法的,有的编译器生产厂家认为标准C的特性不够,是自己加上去的)
void虽然不能定义普通的变量,但是可以创建指针变量:
- C语言规定只有相同类型的指针才可以相互赋值
- void * 指针作为左值用于 “接受” 任意类型的指针
- void* 指针作为右值使用时需要进行强制类型转换
程序示例5:
1 #include<stdio.h> 2 #include<stdlib.h> 3 int main() 4 { 5 int *pi = (int *) malloc(sizeof(int)); 6 char *pc = (char *)malloc(sizeof(char)); 7 void *p=NULL; 8 int *pni=NULL; 9 char * pnc=NULL; 10 11 p=pi; //ok 12 pni =p; //error 13 14 p=pc; //ok 15 pnc = p; //error 16 17 return 0; 18 }
在标准C语言中12 和 15 行会报错,但是有的编译器会扩展C语言规范,所以上述代码在你的编译器中有可能时通过的。
程序示例6:
#include <stdio.h> void MemSet(void* src, int length, unsigned char n) { unsigned char* p = (unsigned char*)src; int i = 0; for(i=0; i<length; i++) { p[i] = n; } } int main() { int a[5]; int i = 0; MemSet(a, sizeof(a), 0); for(i=0; i<5; i++) { printf("%d\n", a[i]); } return 0; }
MeMset这个函数用void类型的指针接受一个int(也可以接受其他类型的指针),然后将其强制转化为unsigned char 类型的指针,将其每个字节都设置为0,最后在主函数会输出 5 个0,因为每一个字节都被设置为0,所以四个为0的字节组合的一个int类型的值也是0,你也可以将他的每个自己设置为其他的数。
小结:
- 现代软件工程中禁止使用goto语句
- void是一种抽象类型的数据类型
- void类型不能用于定义变量
- void类型用于声明函数的无参数
- void类型用于用于声明函数的无返回值
- 可以定义void* 类型的指针
- void* 类型的子真可以接受任意类型的指针值