【C语言进阶剖析】8、goto 和 void 分析

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* 类型的指针可以接受任意类型的指针值

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基本的C语言语法分析器示例,使用lex和yacc编写: 1. 首先,我们需要定义我们的C语言语法规则。下面是一个简单的示例: ``` program -> declaration_list declaration_list -> declaration | declaration_list declaration declaration -> var_declaration | func_declaration var_declaration -> type_specifier ID ; type_specifier -> int | float | double | char func_declaration -> type_specifier ID ( params ) compound_stmt params -> param_list | void param_list -> param | param_list , param param -> type_specifier ID compound_stmt -> { local_declarations statement_list } local_declarations -> local_declarations var_declaration | ε statement_list -> statement | statement_list statement statement -> expression_stmt | compound_stmt | selection_stmt | iteration_stmt | return_stmt expression_stmt -> expression ; selection_stmt -> if ( expression ) statement | if ( expression ) statement else statement iteration_stmt -> while ( expression ) statement return_stmt -> return ; | return expression ; expression -> var = expression | simple_expression var -> ID simple_expression -> additive_expression relop additive_expression | additive_expression additive_expression -> additive_expression addop term | term term -> term mulop factor | factor factor -> ( expression ) | var | call | NUM call -> ID ( args ) args -> arg_list | ε arg_list -> expression | arg_list , expression ``` 2. 接下来,我们使用lex编写一个词法分析器,它将读取C代码并将其分解为标记流。以下是一个简单的示例: ``` %{ #include "y.tab.h" %} %% "auto" return AUTO; "break" return BREAK; "case" return CASE; "char" return CHAR; "const" return CONST; "continue" return CONTINUE; "default" return DEFAULT; "do" return DO; "double" return DOUBLE; "else" return ELSE; "enum" return ENUM; "extern" return EXTERN; "float" return FLOAT; "for" return FOR; "goto" return GOTO; "if" return IF; "int" return INT; "long" return LONG; "register" return REGISTER; "return" return RETURN; "short" return SHORT; "signed" return SIGNED; "sizeof" return SIZEOF; "static" return STATIC; "struct" return STRUCT; "switch" return SWITCH; "typedef" return TYPEDEF; "union" return UNION; "unsigned" return UNSIGNED; "void" return VOID; "volatile" return VOLATILE; "while" return WHILE; [0-9]+ yylval.num = atoi(yytext); return NUM; [a-zA-Z]+ yylval.name = strdup(yytext); return ID; [ \t\n] /* ignore whitespace */ . return yytext[0]; %% int yywrap() { return 1; } ``` 3. 最后,我们使用yacc编写语法分析器,它将使用我们的语法规则和词法分析器生成的标记流来分析C代码。以下是一个简单的示例: ``` %{ #include <stdio.h> #include <stdlib.h> #include <string.h> %} %token ID NUM AUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM EXTERN FLOAT FOR GOTO IF INT LONG REGISTER RETURN SHORT SIGNED SIZEOF STATIC STRUCT SWITCH TYPEDEF UNION UNSIGNED VOID VOLATILE WHILE %% program: declaration_list ; declaration_list: declaration | declaration_list declaration ; declaration: var_declaration | func_declaration ; var_declaration: type_specifier ID ';' ; type_specifier: INT | FLOAT | DOUBLE | CHAR ; func_declaration: type_specifier ID '(' params ')' compound_stmt ; params: param_list | VOID ; param_list: param | param_list ',' param ; param: type_specifier ID ; compound_stmt: '{' local_declarations statement_list '}' ; local_declarations: local_declarations var_declaration | /* empty */ ; statement_list: statement | statement_list statement ; statement: expression_stmt | compound_stmt | selection_stmt | iteration_stmt | return_stmt ; expression_stmt: expression ';' ; selection_stmt: IF '(' expression ')' statement | IF '(' expression ')' statement ELSE statement ; iteration_stmt: WHILE '(' expression ')' statement ; return_stmt: RETURN ';' | RETURN expression ';' ; expression: var '=' expression | simple_expression ; var: ID ; simple_expression: additive_expression relop additive_expression | additive_expression ; additive_expression: additive_expression addop term | term ; term: term mulop factor | factor ; factor: '(' expression ')' | var | call | NUM ; call: ID '(' args ')' ; args: arg_list | /* empty */ ; arg_list: expression | arg_list ',' expression ; relop: '<' | LE | '>' | GE | EQ | NE ; addop: '+' | '-' ; mulop: '*' | '/' ; %% int main() { yyparse(); return 0; } void yyerror(char *s) { printf("Error: %s\n", s); } ``` 这是一个基本的C语言语法分析器示例,它将读取C代码并检查它是否符合我们的语法规则。需要注意的是,这只是一个简单的示例,实际的C语言语法规则远比这个复杂,因此需要更复杂的词法和语法规则来处理它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值