C语言中 continue,goto, void(空类型),你不知道的事情 ???

发布于:2023-01-31 ⋅ 阅读:(591) ⋅ 点赞:(0)

C语言中 continue,goto, void(空类型),你不知道的事情 ???

在这里插入图片描述

每博一文案

一个人从满心期待,走到心灰意冷,都是由一些生活中很小的瞬间积累起来的,
每失望一次就减一份热情,知道热情被消磨殆尽,故事就结束了,从来都没有
顷刻之间的心灰意冷,有的,只是日积月累的看透,你以为放弃是一瞬间的决定,
其实那个人已经在冷风中站了很久很久,当热情没有回应付出不被珍惜,真心没被善待,
再热的心也有变冷的一刻,这世间所有的爱都是快走,所遇的人都是缘分,不要等一个人
热情耗尽,才知道有些人一旦错过,就不在了。
                                   ——————  一禅心灵庙语



break 与 continue

  • break 表示跳出循环,
  • continue 表示跳出本次(一次循环) ,可continue 跳出循环后,又是跳出到了循环的那个位置

continue 跳出循环后,又是跳出到了循环的什么位置处

  1. 在 while() 循环中,请看下面的代码,其中当满足条件时,continue 跳转到什么位置中 ???
#define  _CRT_SECURE_NO_WARNINGS  1
#include<stdio.h>
int main()
{

	int  i = 0;
	while (i < 10)
	{
		i++;
		if (i % 2 == 0)
		{
			printf("continue:\n");
			continue; // 跳转的位置
		}
		printf(" %d ", i);
		
	}

	return 0;
}

在这里插入图片描述

是,(1)号,还是 (2)号 ,位置处, 答案: 是 跳转到 (2) 号位置(循环条件判定的位置),我们调试运行验证看看就知道了。从下面的调试结果上看,结论并没有错,跳转的位置是在 循环条件判断的位置处

在这里插入图片描述


  1. 在 do…while( )循环中 ,请看下面的代码,其中当满足条件时,continue 又是跳转到什么位置中 ???
int main()
{
	int i = 0;

	do {
		i++;
		
		if (i % 2 == 0)
		{
			printf("continue: \n");
			continue; // 跳出位置
		}
		printf(" %d ", i);

	} while (i < 10);

	return 0;
}

在这里插入图片描述

是,(1)号,还是 (2)号 ,位置处, 答案: 同样是 跳转到 (2)号 位置(循环条件的判定),同样的,我们再通过调试运行验证看看就知道了。从下面的调试结果上看,结论并没有错,跳转的位置是在 循环条件判断的位置处

在这里插入图片描述


  1. 在 for( ; ; ) 循环中 ,其中当满足条件时,continue 又是跳转到什么位置中 ???
int main()
{
	for (int i = 0; i < 10;i++ )
	{

		if (i % 2 == 0)
		{
			printf(" continue: \n");
			continue; // 跳转位置
		}

		printf(" %d ", i);
	}

	return 0;
}

在这里插入图片描述

是,(1)号,还是 (2)号 ,位置处, 答案: 跳转到 (2) 号位置( 循环条件更新处),这里因为for( ) 循环语句的特殊处,我们调试无法直接判断是跳转的具体的位置处,所以这里我们使用另外一种方法,就是假设法
假设我们 continue 跳转的位置是在 i < 10 (循环条件判定的位置处) 的话,根据 for( )循环语句的执行流程 : 初始化循环条件 ——> 循环条件的判定,真——> 执行循环内的语句 ——> 最后更新循环条件——> 再循环条件的判定… , 我们可以确定的是,如果 continue 跳转的的位置是循环条件的判定的话,i 将一直 是 2 , 因为 continue 的跳转,导致无限循环;
而我们运行结果却,不是无限循环,所以可以得出的结论是 **在 for( ) 循环语句中 continue 跳转的位置是在循环条件更新处

在这里插入图片描述

在这里插入图片描述


循环语句的注意点

  1. 在多重循环中,如果有可能,应当将最长的循环放在内层,最短的循环放在最外层,以减少 CPU 跨切循环的次数。如下:两个代码实例比较

在这里插入图片描述

  1. 建议 for 循环语句的循环控制变量的取值采用 “半开半闭区间” 的写法。 半开半闭区间的写法和闭区间的写法虽然在功能作用上是相同的,但相比之下,半开半闭的写法更加直观。二者对比如下表:

在这里插入图片描述

  1. 尽量不要在 for 循环体内修改循环变量的,防止循环失控
for( int i = 0; i < 10 ; i++)
{
    i = 10; // 不可,很可能违背了,你的原意
    ...
}
  1. 循环要尽可能短,要使代码清晰,一目了然
  2. for 语句的循环条件的判定不要包含任何浮点类型的对象。
    因为浮点类型存在,精度上的误差,可能会改变循环的次数 。具体原因,大家可以移步到 🔜🔜🔜 浮点数的精确度的探究_ChinaRainbowSea的博客-CSDN博客

goto 关键字

Linux内核代码中存在这大量的 goto语句 ,所以我们并不能太过片面的忽略掉它,如下:

在这里插入图片描述

一般来说,编码的水平与 goto 使用的次数成反比。有的人主张慎用但不禁用 goto 语句,但我个人主张禁用的,

自从提倡结构化设计以来,goto 就成了有争议的语句,首先,由于 goto 语句可以灵活跳转,但如果不加以限制的话,它的确会破坏结构化的设计;其次 goto 语句经常带来错误或隐患,它可能跳过了变量的初始化,重要的计算语句等,如下:

 int * p = NULL;
goto end;
p = (int*)malloc(sizeof(int));   // 被 goto 语句跳转了,并没有初始化到
end:


goto 语句的使用

int main()
{
	goto end;
	printf("hello 1 \n");
	printf("hello 2 \n");
	end:
	printf("hello 3 \n");
	printf("hello 4 \n");
	printf("hello 5 \n");

	return 0;
}

在这里插入图片描述


goto 语句的无限循环 的使用

int main()
{
	end:
	printf("hello 1 \n");
	printf("hello 2 \n");
	printf("hello 3 \n");
	goto end;
	printf("hello 4 \n");
	printf("hello 5 \n");


	return 0;
}

在这里插入图片描述


goto 语句实现简单的循环

int main()
{
	int i = 0;
end:
	i++;
	printf("%d ", i);

	if (i < 10)
	{
		goto end;
	}

	return 0;
}

在这里插入图片描述


void 类型

void 有什么好讲的呢 ?如果你说没有,那就没有,但如果你说有,那就真的有。

void 的字面意思是 “空类型”

1. void不能代表一个真实的变量

void 类型不可以定义变量:

int main()
{
	void x;  

	return 0;
}

在这里插入图片描述


为什么 void 类型不可以定义变量

定义变量的本质是:开辟空间,存放数据,而 void 作为空类型,在 VS 2019 中是没有开辟空间的,大小上 VS 2019 定义为了 0 ,如下: 0 自然无法存放数据,就失去了定义变量的意义了,

在这里插入图片描述

但是在 Linux 中的 gcc 编译器定义该 大小是 1,无论是 VS 2019 定义的 0 ,还是 Linux 中的 gcc 的 1 ,理论上是不应该开辟空间的,即使开辟了空间,也仅仅是作为一个 占位符
所以既然无法开辟空间,那么也就无法作为正常变量使用,即无法正常使用,编译器也解释为 空类型,就强制不允许定义变量操作,

另一种简单的说法就是,void类型的在内存中的大小不确定,无法开辟确定的空间,所以就不开辟空间的了,就无法作为正常变量使用了。


2. void 修饰函数返回值

在C语言中,凡是不加返回值类型的函数,就会被编译器作为 返回 int 类型处理 ,但是,有许多程序员却误认为是 void 类型 ,如下:默认是 int 类型

fun()
{
	return 10;
}

int main()
{
	printf("%d", fun());

}

在这里插入图片描述


void 修饰 函数的返回值类型:1.作为占位符,让用户明确该函数不需要返回值,提高代码的可读性,2. 告知编译器,这个函数的返回值(不要)无法接受。

我们在编写一个程序时,对于任何函数都必须一个不漏地指定其返回类型。返回类型是 int 虽然默认是,但也不要省略,阅读其代码的时候,是这个家伙 忘记了,还是故意偷懒省略不写呢???,
如果函数没有返回值,那么一定要声明为 void 类型。这是程序良好可读性的需要,也是编程规范性的要求,另外,加上 void 类型声明后,也可以发挥代码的 “ 自注释” 的作用。所谓代码的 “自注释” 即代码能自己注释自己。

void fun()
{
	return 10;
}

int main()
{
	printf("%d", fun());  // 报错, 函数 fun() 是 void返回值类型

	return 0;

}

在这里插入图片描述


3. void 修饰函数参数

表示:这个函数不接受任何参数;并给予警告,

void 充当函数的参数列表: 表示告知用户和编译器,该函数不需要传参数,强行传参数的话,不会报错,但会给予警告。而相反,如果一个函数的没有参数,也没有被 void修饰参数列表的话,强制传参数的话,不会报错,也不会发出警告 。

所以如果一个函数没有参数的话,将函数的参数列表设置成 void 是一个好的编程习惯,可以将错误明确的提前发现。还会给予警告。

void test1()
{
	printf("test1\n");

}

void test2(void)
{
	printf("test2\n");
}


int main()
{
	test1(1,2,3);  // 不会报错,也不给予警告
	test2(1,2,3);  // 不会报错,但会给予警告

	return 0;
}

在这里插入图片描述

在这里插入图片描述


void* 指针

1. 为什么 void 不能定义变量可以定义 void* 指针

因为 void* 是指针,即地址,其空间大小是可以明确出来的,32位 4 个字节,64位 8 个字节空间的大小,

int main()
{
	void* p = NULL;

	printf("%d",sizeof(p));

	return 0;
}

在这里插入图片描述


void* 类型的指针可以被任意的类型的指针接受

int main()
{

	void* p = NULL;
	int* x = NULL;
	double* y = NULL;
	
	x = p;
	y = p;  // void* 指针可以被任意类型的指针接受


	return 0;
}

在这里插入图片描述


同样 void* 类型的指针可以接受任何类型的指针类型

int main()
{
	void* p = NULL;
	int* x = NULL;
	double* y = NULL;

	p = x;  // void* 类型的指针可以接受任何类型的指针
	p = y;

	return 0;
}

在这里插入图片描述


这一点,十分的常用,如:在一些系统,接口的设计上,尽量设计成通用的接口如:

在这里插入图片描述


在这里插入图片描述


c库 的网站地址如下:🔜🔜🔜 memcmp - C++ Reference (cplusplus.com)

定义的void* 指针 类型不可以通过 ++,–,指针地址的移动 为什么呢?

如下代码中的 p 指针地址 ++,–,操作,的实质是移动根据对应类型的所占字节的大小,移动对应的字节位数,而 void* 是空类型,在 VS 2019 中定义的类型大小为 0 个字节,移动 0 个字节数没有意义。

int main()
{
	void* p = NULL;
	p++;  // 报错
	p--;  // 报错

	return 0;
}

在这里插入图片描述


void* 类型的指针式不可以被解引用的 ,

为什么 ?? ?

因为解引用是表示 把存放到该指针中的地址的变量,通过地址再根据类型的大小把数据取出来,而 void 空类型 ,在 VS 2019 中定义的大小为 0 ,根据类型大小 0 怎么取数据出来。

还有一种说法就是,void 类型的大小不确定,不知道如何取出。

int main()
{
	int num = 10;
	void* p = &num;
	printf("%d", *p);
    
	return 0;
}

在这里插入图片描述


正常类型是可以解引用的。

int main()
{
	int num = 10;
	int* p = &num;
	printf("%d", *p);


	return 0;
}

在这里插入图片描述


最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见!


网站公告

今日签到

点亮在社区的每一天
去签到