learn_C_deep_8 (循环语法的理解、void的用法以及理解)

目录

循环语法的理解

break关键字

continue关键字 

 continue跳转的位置

goto关键字

void的用法以及理解

void是否可以定义变量

为何 void 不能定义变量

void的应用场景

void 指针


循环语法的理解

        for循环是一种常用的循环结构,它适合于在已知循环次数的情况下重复执行相同的代码块。for循环语法如下:

for(条件初始化; 条件判定; 条件更新){
        //业务代码
}

        while循环是另一种常用的循环结构,它适合于在不知道循环次数的情况下重复执行代码块,只需要在每次循环前先检查一次循环条件。 while循环语法如下:

条件初始化
while(条件判定){
        //业务更新
条件更新
}

        do-while循环和while循环很类似,它们的区别在于do-while循环的判断是在循环体执行完后才进行的,因此它保证了循环体至少能够被执行一次。 do-while循环语法如下:

条件初始化
do{
        条件更新
}while(条件判定);

三种循环对应的死循环写法场景

while(1)

{

        while语句

}

for(;;)

{

        for语句;

}

do

{

        do-while语句;

}while(1);

        在C语言中,每个程序都有三个默认打开的流,即标准输入流(stdin)、标准输出流(stdout)和标准错误流(stderr)。它们分别对应键盘输入、屏幕输出和错误信息输出,可以在程序中使用它们来读取或写入数据,或者输出一些提示信息、错误信息等。在C语言中,我们可以使用各种标准输入输出函数(如printf、scanf、fgets等)来访问这些默认的流,从而实现程序的输入输出。同时,这三个标准流也可以在程序中进行重定向,例如将标准输出流重定向到文件上,从而将程序的输出内容保存到文件中。

        在图片的代码中,为什么我们的代码没有文件操作,却会打开这些流呢?

        实际上,你使用标准输入输出函数时,代码中并没有显式的文件操作代码,因为标准库已经将文件操作封装到标准输入输出函数中了。标准输入输出函数是使用FILE数据类型来操作标准输入输出流的。标准输入、输出、错误输出描述符在程序开始执行时就已经被打开了,因此当你使用标准输入输出函数时,实际上是操作这些默认打开的流,而不需要显式地打开文件。

例如,当你使用printf函数将输出内容写入标准输出流(通常是控制台屏幕)时,实际上是写入到了stdout指向的流。同样,当你使用scanf函数从标准输入流(通常是键盘输入)读取输入内容时,实际上是从stdin指向的流读取数据。  

了解上面的内容后,我们再来了解一下getchar函数。

        getchar函数是一个标准输入函数,用于从标准输入流(stdin)中读取一个字符。

它的函数原型如下:

        getchar函数不需要任何参数,当从标准输入流中读取到一个字符时,它会将该字符作为返回值返回给调用者。如果从流中读取到文件结束标志EOF(End-Of-File),则返回EOF。        

        因此,我们可以根据getchar函数的返回值来判断是否读取到有效字符。 在使用getchar函数时,需要注意以下几点:

1. 当程序使用getchar函数时,它会一次读取一个字符,如果想要读取多个字符,需要使用循环语句(如while、for等)结合getchar函数来实现。

2. 在读取字符时,getchar函数会自动忽略空白字符(如空格、制表符、回车等),直到读取到一个非空白字符为止。如果想要读取空白字符,可以使用scanf函数等其他读取函数。

我们接下里就来看看循环语法和getchar函数的应用

#include <stdio.h>
#include <windows.h>
int main()
{
	while (1) { //while的死循环写法
		char c = getchar(); 
		if ('#' == c) {
			break;//结束死循环
		}
		printf("%c\n", c);
	}
	system("pause");
	return 0;
}

getchar的返回值类型为什么是int,而不是char。

        实际上,C语言规定getchar函数的返回值类型为int,而不是char。这是为了能够表示EOF(End-Of-File)值,其值宏定义为-1。

        在标准输入流中,如果读取到文件结束标志EOF,getchar函数会返回EOF,以便程序能够正确地判断文件是否已经读取完毕。如果将getchar函数的返回值类型定义为char,那么EOF就无法被正确地表示,因为EOF是一个int类型的常量。例如,如果getchar函数的返回值类型是char,那么当从标准输入流中读取到EOF时,getchar函数会返回char类型的-1。此时,程序就无法正确地判断是读取到了EOF,还是实际上读取到了字符-1。

       此外,在一些特殊情况下,比如运行在宽字符环境下时,一个字符可能需要占用多个字节,此时将返回值类型设置为 int,可以避免数据截断。例如:当我们读取到1 0000 0000,而读取char类型只有八个比特位,此时读取的就是0000 0000在其合法范围内,那么就会读取错误的数据。

        因此,C语言规定getchar函数的返回值类型为int,以便在读取到EOF时能够正确地返回EOF值,让程序能够正确地判断文件是否已经读取完毕。

总结:使用getchar一定不能忘记你自身输入的回车符(`\n`)。 

break关键字

        `break` 关键字是C语言中用于控制循环语句的关键字,它用于立即退出当前循环,并跳到循环语句后面的代码中执行。

continue关键字 

        `continue` 关键字是C语言中用于控制循环语句的关键字,它可以用于跳过当前循环中剩余的语句,直接进入下一次循环。

 continue跳转的位置

总结:while语句和do-while语句判断条件在的位置就是continue跳转的位置,而for语句的条件更新在的位置就是continue跳转的位置。

goto关键字

        `goto`关键字是一种跳转语句,它可以用于无条件地跳转到程序中的某个标记(label)处继续执行,但仅仅是本代码块内,不可跨函数、文件使用。

#include <stdio.h>
#include <windows.h>
int main()
{
	int i = 0;
START:
	printf("[%d]goto running ... \n", i);
	Sleep(1000);
	++i;
	if (i < 10) {
		goto START;
	}
	printf("goto end ... \n");
	system("pause");
	return 0;
}

        这是一段 C 语言的程序代码。它使用了 goto 语句来实现循环的功能。程序开始时,定义了一个计数器变量 i,并初始化为 0。然后使用标签 START,打印输出一条信息,暂停程序执行一秒钟,计数器变量自加 1。接着判断计数器变量 i 是否小于 10,如果小于 10,则跳转到标签 START 处重新执行循环;否则,打印输出一条结束信息,暂停程序执行并等待用户按下任意键结束程序。总的来说,这段程序使用了 goto 语句来实现了一个简单的循环结构,但是因为 goto 容易导致程序流程混乱,使用时要谨慎。

void的用法以及理解

        在C语言中,void是一种特殊的数据类型,表示“无类型”或“空类型”。

void是否可以定义变量

        在C语言中,void是一种“空类型”,它没有具体的值或大小,因此不能定义为变量类型,也不能用作变量的数据类型。

  很明显,在vs和gcc编译器中是错误的,禁止运行。我们再用sizeof求一下它所占的空间。

很明显,在vs编译器空间大小为0,自然也就不能开辟空间。

在gcc编译器下,大小又是多少呢? 

很明显,在vs编译器空间大小为1,但是在vs编译器上显示大小为0,这不是矛盾嘛?

实际上,这是编译对void类型做出的不同解释:

        1.在vs编译器空间大小为0,自然定义变量也是错误的。

        2.在gcc编译器空间大小虽然为1,但是它定义变量却是错误的。按照常量说有了空间就可以定义变量,但是这里不可以,因为void在gcc编译器只仅作为一个占位符看待,所以也不能定义变量。

总结:void本身就被编译器解释为空类型,强制的不允许定义变量

为何 void 不能定义变量

        定义变量的本质:开辟空间

        而void作为空类型,理论上是不应该开辟空间的,即使开了空间,也仅仅作为一个占位符看待。所以,既然无法开辟空间,那么也就无法作为正常变量使用,既然无法使用,编译器干脆不让他定义变量。

在vs2013中,sizeof(void)=0

在gcc中,sizeof(void)=1(但编译器依旧理解成,无法定义变量)

void的应用场景

        1.用于函数的返回值类型。在C语言中,函数可以返回各种不同类型的数据,但是有些函数并不需要返回任何值,这时候就可以使用void类型的函数。void函数不需要使用return语句,因为它们不返回任何值。

 但是自定义函数,没有返回值,如果不写void,会让阅读你代码的人产生误解:他是忘了写,还是想默认int?这样就会导致代码的体验感变差。

 总结:void作为函数返回值,代表不需要,这里是一个"占位符"的概念,是告知编译器和给阅读源代码看的,建议程序如果没有返回值的时候,将返回值类型写成void。

        2.用于函数参数类型。当一个函数不需要接受任何参数时,可以使用void作为函数参数类型。例如,int main(void) 是一个不接受任何参数的函数。

         总结:如果一个函数没有参数,将参数列表设置成void,是一个不错的习惯,因为可以将错误明确提前发现,另外,阅读你代码的人,也一眼看出,不需要参数。相当于"自解释"。

        3.用于指针类型。在C语言中,void*指针可以指向任何类型的数据,但是它不能被直接解引用,必须先进行类型转换才能使用。因此,void指针常用于作为通用指针类型,用于传递指针参数,例如: ```c void print(void *data, size_t size);

void 指针

void不能定义变量,那么void*呢?

#include <stdio.h>
#include <windows.h>
int main()
{
	void* p = NULL; //可以
	system("pause");
	return 0;
}

 void*定义变量no error!

为什么void*可以呢?

因为void*是指针,是指针,空间大小就能明确出来,有空间就可以创建变量。

void* 能够接受任意指针类型

#include <stdio.h>
int main()
{
	void* p = NULL;
	int* x = NULL;
	double* y = NULL;
	p = x; //虽然类型不同,但是编译器并不报错 x - int*
	p = y; //同上 y - double*
	return 0;
}

        `void*` 可以接受任意指针类型是因为C语言中所有指针类型的大小都是相同的。指针的大小和所指向的数据类型没有关系,它只与寻址空间的大小有关,通常为4个字节或8个字节。因此,任意类型的指针都可以转换成 `void*` 类型指针,因为这种转换不会改变指针所占用的大小和指向的内存空间。

当我们反过来将void*赋给x,y呢?

#include <stdio.h>
int main()
{
	void* p = NULL;
	int* x = NULL;
	double* y = NULL;
	p = x; //虽然类型不同,但是编译器并不报错 x - int*
	p = y; //同上 y - double*

	x = p;
	y = p;
	return 0;
}

同样没有报错,甚至连警告都没有。

总结:void*可以被任何类型的指针接收,同时也可以接收任意指针类型(常用)。

void * 定义的指针变量可以进行运算操作吗?

#include <stdio.h>
int main()
{
	void* p = NULL;
	p++; //报错
	p += 1; //报错
	return 0;
}

        `void*` 指针类型是一个通用的指针类型,用于通用的指针操作,它本身是一个无类型指针,不直接支持指针运算。

        由于 `void*` 指针类型无法知道它指向的内存的数据类型和占用多少字节,所以在使用 `void*` 类型指针时,不能进行指针运算。若对 `void*` 类型指针进行加、减、乘、除、位运算等操作是没有意义的,并且这样的操作通常会导致编译错误。

再来看一下在gcc编译器的环境下是否可以?

因为在gcc编译器下void空间大小为1,自然就可以进行运算操作。p本身就是地址(十六进制),int强制转为整形,然后再以十进制打印出来结果。

void*指针可以直接解引用吗?

#include<stdio.h>
int main()
{
	int a = 10;
	void* p = (void*)&a;
	//由于void*可以接受任意类型的地址
	//也可以写成void* p = &a;
	*p;
	return 0;
}

我们发现在gcc和vs编译器下都出现了问题。 

        总结:void*指针是一种无类型指针,它不能直接解引用。因为void*指针并不知道它所指向的内存区域的数据类型,所以不能像其他指针那样通过解引用来获取值。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值