遭人遗弃的goto
高手潜规则:禁用goto,程序质量与goto的出现次数成反比;
一般在内核模块的入口函数才会大量使用goto语句,用来处理异常;
goto常常会破坏结构化程序的顺序执行;
goto语句也称为无条件跳转语句,一般格式为
goto 语句标签;
其中语句标签是按标识符规定书写的符号,放在某一语句行的前面,标号后加冒号(:);
语句标签起标示语句的作用,与goto语句配合使用;
C语言中不限制程序中使用标签的次数,但各标签不得重名;goto语句是改变程序流向,转去执行语句标签所标识的语句;
goto语句一般通常与分支语句配合使用,可用来实现条件转移,构成循环,跳出循环体等功能;
但是结构化程序中不建议使用goto语句,以免造成程序混乱,使理解和调试程序都产生困难;
示例
#include <stdio.h>
int main(){
int n = 0;
printf("input a string\n");
loop:
if (getchar() != '\n') {
n++;
goto loop;
}
printf("%d\n", n);
return 0;
}
例如输入: hello world
回车打印: 11
goto副作用分析
goto有可能会造成跳过一些本来应该执行的语句,破坏结构化程序设计顺序执行的规则;
/* goto副作用分析 */
#include <stdio.h>
void func(int n) {
int* p = NULL;
if(n < 0) { //当n>=0时程序就会执行的很好
goto STATUS; //跳过堆内存的分配,使程序崩溃
}
/* 破坏结构化程序的顺序执行 */
p = malloc(sizeof(int) * n);
STATUS:
p[0] = n;
}
int main() {
f(1);
f(-1);
return 0;
}
编译的时候被编译了,但是执行的时候被goto跳过,造成结果的不确定性;
#include <stdio.h>
int x = 5;
int main () {
printf("%p\n", &x);
goto a;
{
/* 执行的时候goto会跳过,但仍然会被正常编译 */
int x = 3; /* 又重新申请了一个同名局部变量x */
printf("%p\n", &x);
pritnf("int x = 3\n");
a:
/* 这里的x都是局部变量 */
printf("x = %d\n", x);
printf("%p\n", &x);
}
/* 以后的x是全局变量 */
printf("x = %d\n", x);
printf("%p\n", &x);
return 0;
}
void关键字
void修饰函数返回值和参数
如果函数没有返回值,那么应该将其声明为void类型;
如果函数不接收参数,应该声明其参数为void类型;
void修饰函数返回值和参数仅为了表示无;
不存在void变量
void v; /* 编译报错 */
没有void的标尺;
c语言中类型名是固定大小内存的别名,但没有定义void究竟是多大内存的别名;
void不对应任何变量的大小
void类型的指针是存在的;
void指针的意义
c语言规定只有相同类型的指针才可以相互赋值;
void*指针作为左值用于接收任意类型的指针;
void*指针作为右值赋值给其他指针时需要强制类型转换,才能够赋值给其他类型的指针;
malloc()返回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; //oops!
p = pC; //ok
pnc = p; //oops!
void*指针的使用,实现my_memset()函数;
#include <stdio.h>
void* my_memset(void *p, char c, int size) {
void *ret = p;
char *dest = (char *)p;
int i = 0;
for (i = 0; i < size; i++) {
dest[i] = c;
}
return ret;
}
int main(){
int arr[5] = {1, 2, 3, 4, 5};
long num = 9999;
char str[10] = "hello";
int i = 0;
for (i = 0; i < 5; i++) {
printf("%d\t", arr[i]);
}
printf("%ld\t", num);
printf("%s", str);
printf("\n");
my_memset(arr, 0, sizeof(arr));
my_memset(&num, 0, sizeof(num));
my_memset(str, 65, sizeof(str) - 1);
for (i = 0; i < 5; i++) {
printf("%d\t", arr[i]);
}
printf("%ld\t", num);
printf("%s", str);
printf("\n");
return 0;
}
extern关键字
extern用于声明外部定义的变量或函数;
extern用于告诉编译器用c方式编译;
C++编译器和一些变种C编译器默认会按自己的方式编译函数和变量,通过extern关键字可以命令编译器以标准c方式进行编译;
// g++ test.c
#include <stdio.h>
extern "C" {
int add(int a, int b){
return a + b;
}
}
int main(){
printf("res = %d\n", add(2, 3));
return 0;
}
extern用于声明外部定义的变量或函数;
// test2.c
int g = 100;
int get_min(int a, int b) {
return (a < b) ? a : b;
}
//gcc test1.c test2.c
#include <stdio.h>
extern int g; //声明引用外部定义的变量
extern int get_min(int a, int b); // 声明引用外部定义的函数
int main() {
printf("g = %d\n", g);
printf("get_min(3, 5) res %d\n", get_min(3, 5));
return 0;
}
sizeof关键字
sizeof是编译器的内置指示符,不是函数
sizeof用于计算相应实体所占的内存大小,不用运行就可以知道,编译时确定;
sizeof的值在编译期就已经确定
sizeof不是函数
#include <stdio.h>
int main() {
int a;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a); //sizeof不是函数
printf("%d\n", sizeof(int));
/* printf("%d\n", sizeof int ); */
C语言中int前面不能出现unsigned/signed/const之外的;
不能是sizeof
类型是不能这么写的;
return 0;
}
高手潜规则:禁用goto,程序质量与goto的出现次数成反比;
一般在内核模块的入口函数才会大量使用goto语句,用来处理异常;
goto常常会破坏结构化程序的顺序执行;
goto语句也称为无条件跳转语句,一般格式为
goto 语句标签;
其中语句标签是按标识符规定书写的符号,放在某一语句行的前面,标号后加冒号(:);
语句标签起标示语句的作用,与goto语句配合使用;
C语言中不限制程序中使用标签的次数,但各标签不得重名;goto语句是改变程序流向,转去执行语句标签所标识的语句;
goto语句一般通常与分支语句配合使用,可用来实现条件转移,构成循环,跳出循环体等功能;
但是结构化程序中不建议使用goto语句,以免造成程序混乱,使理解和调试程序都产生困难;
示例
#include <stdio.h>
int main(){
int n = 0;
printf("input a string\n");
loop:
if (getchar() != '\n') {
n++;
goto loop;
}
printf("%d\n", n);
return 0;
}
例如输入: hello world
回车打印: 11
goto副作用分析
goto有可能会造成跳过一些本来应该执行的语句,破坏结构化程序设计顺序执行的规则;
/* goto副作用分析 */
#include <stdio.h>
void func(int n) {
int* p = NULL;
if(n < 0) { //当n>=0时程序就会执行的很好
goto STATUS; //跳过堆内存的分配,使程序崩溃
}
/* 破坏结构化程序的顺序执行 */
p = malloc(sizeof(int) * n);
STATUS:
p[0] = n;
}
int main() {
f(1);
f(-1);
return 0;
}
编译的时候被编译了,但是执行的时候被goto跳过,造成结果的不确定性;
#include <stdio.h>
int x = 5;
int main () {
printf("%p\n", &x);
goto a;
{
/* 执行的时候goto会跳过,但仍然会被正常编译 */
int x = 3; /* 又重新申请了一个同名局部变量x */
printf("%p\n", &x);
pritnf("int x = 3\n");
a:
/* 这里的x都是局部变量 */
printf("x = %d\n", x);
printf("%p\n", &x);
}
/* 以后的x是全局变量 */
printf("x = %d\n", x);
printf("%p\n", &x);
return 0;
}
void关键字
void修饰函数返回值和参数
如果函数没有返回值,那么应该将其声明为void类型;
如果函数不接收参数,应该声明其参数为void类型;
void修饰函数返回值和参数仅为了表示无;
不存在void变量
void v; /* 编译报错 */
没有void的标尺;
c语言中类型名是固定大小内存的别名,但没有定义void究竟是多大内存的别名;
void不对应任何变量的大小
void类型的指针是存在的;
void指针的意义
c语言规定只有相同类型的指针才可以相互赋值;
void*指针作为左值用于接收任意类型的指针;
void*指针作为右值赋值给其他指针时需要强制类型转换,才能够赋值给其他类型的指针;
malloc()返回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; //oops!
p = pC; //ok
pnc = p; //oops!
void*指针的使用,实现my_memset()函数;
#include <stdio.h>
void* my_memset(void *p, char c, int size) {
void *ret = p;
char *dest = (char *)p;
int i = 0;
for (i = 0; i < size; i++) {
dest[i] = c;
}
return ret;
}
int main(){
int arr[5] = {1, 2, 3, 4, 5};
long num = 9999;
char str[10] = "hello";
int i = 0;
for (i = 0; i < 5; i++) {
printf("%d\t", arr[i]);
}
printf("%ld\t", num);
printf("%s", str);
printf("\n");
my_memset(arr, 0, sizeof(arr));
my_memset(&num, 0, sizeof(num));
my_memset(str, 65, sizeof(str) - 1);
for (i = 0; i < 5; i++) {
printf("%d\t", arr[i]);
}
printf("%ld\t", num);
printf("%s", str);
printf("\n");
return 0;
}
extern关键字
extern用于声明外部定义的变量或函数;
extern用于告诉编译器用c方式编译;
C++编译器和一些变种C编译器默认会按自己的方式编译函数和变量,通过extern关键字可以命令编译器以标准c方式进行编译;
// g++ test.c
#include <stdio.h>
extern "C" {
int add(int a, int b){
return a + b;
}
}
int main(){
printf("res = %d\n", add(2, 3));
return 0;
}
extern用于声明外部定义的变量或函数;
// test2.c
int g = 100;
int get_min(int a, int b) {
return (a < b) ? a : b;
}
//gcc test1.c test2.c
#include <stdio.h>
extern int g; //声明引用外部定义的变量
extern int get_min(int a, int b); // 声明引用外部定义的函数
int main() {
printf("g = %d\n", g);
printf("get_min(3, 5) res %d\n", get_min(3, 5));
return 0;
}
sizeof关键字
sizeof是编译器的内置指示符,不是函数
sizeof用于计算相应实体所占的内存大小,不用运行就可以知道,编译时确定;
sizeof的值在编译期就已经确定
sizeof不是函数
#include <stdio.h>
int main() {
int a;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a); //sizeof不是函数
printf("%d\n", sizeof(int));
/* printf("%d\n", sizeof int ); */
C语言中int前面不能出现unsigned/signed/const之外的;
不能是sizeof
类型是不能这么写的;
return 0;
}