1、遭人遗弃的 goto
有一个项目经验:程序质量和 goto 的出现次数成反比。所以在程序中要尽量避免使用 goto
下面就通过一个实例分析一下 goto 的副作用
//8-1.c
#include<stdio.h>
#include<malloc.h>
void func(int n){
int *p = NULL;
if (n < 0){
goto STATUS;
}
p = (int*)malloc(sizeof(int) * n);
STATUS:
p[0] = n;
free(p);
}
int main(){
printf("begin...\n");
printf("func(1)\n");
func(1);
printf("func(-1)\n");
func(-1);
printf("end...\n");
return 0;
}
上面的程序如果 n 小于 0,程序直接goto STATUS,跳过 malloc,没有分配内存,p 为空指针,对 p[0] 赋值会导致程序崩溃。
2、void 的意义
2.1 void 作用
void 用于修饰函数返回值和参数。如果一个函数没有返回值,应该将其声明为 void;如果函数没有参数,应该将其声明为 void
小问题:函数没有返回值,直接不写返回值不就行了吗,函数没有参数,直接不写参数不就行了。
下面我们来尝试一下:
//8-2.c
#include<stdio.h>
f(){
}
int main(){
int i = f(1,2,3);
return 0;
}
对上面的结果:编译通过,运行通过,有一条警告,没有返回值默认为 int。
1、对于没有返回值类型的函数,默认返回值类型为 int
2、对于没有参数的函数,默认参数个数为任意个
所以,void 修饰函数返回值和参数是为了表示“无”,没有返回值类型,没有参数
C 语言没有定义 void 究竟是多大内存的别名,这个模子没有大小,所以在无法从内存中裁剪出 void 对应的变量。不存在 void 类型的变量,但是有 void 类型的指针。
比如:void var;void array[5] 是不合法的;void* p是合法的。
小贴士:C 语言规范包括两部分,1、ANSI C:标准 C 语言规范;2、扩展 C:在ANSI C基础上进行了扩充
不同公司在制作自己编译器的时候,对标准 C 语言规范做出了扩充看如下代码
#include<stdio.h>
int main(){
printf("%ld\n", sizeof(void));
return 0;
}
上面的代码在ANSI C编译器中无法通过编译,但是对于支持 GNU 标准的 gcc
编译器而言是合法的,打印结果为:1
2.2 void 指针的意义
- C 语言规定只有相同类型的指针才可以相互赋值
- void* 指针作为左值用于‘接收’任意类型的指针
- void* 指针作为右值使用时需要进行强制类型转换
int* pI = (int*)malloc(sizeof(int));
char* pC = (char*)malloc(sizeof(char));
void* p = NULL;
int* pni = NULL;
char* pnc = NULL;
p = pI; // ok
pni = p; // 类型转换
p = pC; // ok
pnc = p; // 类型转换
//8-3.c
#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;
}
对于上面的程序,函数的功能是把传入地址的每个字节的数值都变成 n, 为什么要把 src 定义为void* 类型的指针呢,因为传入的参数有多种类型,如int*, char*, double|* 等等,传入不同的指针类型,都可以操作。
3、小结
1、现代工程软件中禁用 goto 语句
2、void 是一种抽象的数据类型,不能用于定义变量
3、void 类型用于声明函数无参数,函数无返回值
4、可以定义void* 类型的指针,void* 类型的指针可以接受任意类型的指针值