第8讲 - C语言关键字(8)
先写一串代码:
#define _CRT_SECURE_NO_WARNINGS 1
int main()
{while (1){int c = getchar();if ('#' == c){break;}putchar(c);}return 0;
}
我们对putchar进行解释:把一个字符显示到我们到标准输出,也就是显示器上
这段代码有什么用呢?
答:可以检验我们输入到键盘上或者显示器上的内容是否都是字符,比如我们输入1234,假如我们输入的不是字符的话,if语句首先不满足条件,执行putchar函数,因为putchar函数能够把一个字符显示到我们的显示器上,假如我们输入到键盘上的内容并不是字符是,putchar函数是无显示的,所以证明了我们输入到键盘上或者显示器上的内容全部都是字符
我们对代码进行优化
#define _CRT_SECURE_NO_WARNINGS 1
int main()
{while (1){int c = getchar();if ('#' == c){break;}putchar(c);}printf("\nbreak out\n");return 0;
}
我们进行编译
由此看见,使用break时,我们会跳出整个循环。
当我们使用continue时
#define _CRT_SECURE_NO_WARNINGS 1
int main()
{while (1){int c = getchar();if ('#' == c){continue;}putchar(c);}printf("\nbreak out\n");return 0;
}
我们进行运行
由此可以知道continue的意思:continue的意思是跳过本次循环,也就是跳过一次循环
注意:continue跳过本次循环后,再次来到循环的判断位置,在这里就是到while循环的判定处
这里,我们出现一个问题:是不是对于所有的循环,continue都会再次来到循环的判断?
我们进行来举几个例子:
首先是do while循环
int main()
{do{printf("hello\n");if (flag){continue;}} while (cond)return 0;
}
在do while循环内部的if循环中,看见continue,continue过后,程序又从哪里开始运行呢?
答:到循环的判断条件处,也就是while(cond)
接下来就是for循环
int main()
{int i = 0;for (i = 0; i < 10; i++){printf("hello world\n");if (flag){continue;}}return 0;
}
对于for循环,continue的意思是跳过本次循环,然后跳转到for循环的条件更新出,也就是i++。
我们再举一个continue的例子
#include<Windows.h>
int main()
{int i = 0;for (i = 0; i < 10; i++){printf("continue before :%d\n", i);if (i == 5){printf("continue\n");continue;}printf("continue after:%d\n", i);Sleep(500);}return 0;
}
还是之前提到的问题:continue之后代码运行到什么位置?
答:这里需要假设,有三种情况,第一种情况运行到for循环内部的第一个printf
我们对这种情况进行解释:因为我们没有经过for循环,而i始终等于5,所以我们死循环打印continue并执行continue
第二种情况:continue之后代码运行到条件判断处,也就是i<10处
我们对这种情况进行解释:因为我们的i始终=5<10,所以我们始终执行if语句内部的内容,所以也是死循环
第三种情况:continue之后代码运行到条件更新处
我们对这种情况进行解释:我们continue之后,代码运行到i++处,所以i进行条件更新后,变成6,再进行条件判定,所以可以执行完毕整个for循环,我们进行编译看一下代码
代码并不是死循环,所以对于for循环内部的continue,跳过本次循环后,跳转到for循环的条件更新处
总结:continue对于不同的循环语句,跳过循环后跳转到不同的位置:对于while循环和do while循环语句,continue跳过循环后跳到while循环的条件判定处,对于for循环,continue跳过循环后跳到for循环的条件更新处。
在多层循环中,最长的循环放在最外层,最短的循环放在最内层,以减少cpu跨切循环次数
两个理由,第一个:cpu在存储数据的过程中,会不断缓存数据,在我们两个不同位置的代码来回切换的过程中,我们的cpu也是从缓存到过期来回跳转,这就导致我们的cpu在存储数据的过程中花费了太长的时间
第二个理由:局部性原理,意思就是代码呈现很密集的情况下,代码的效率会大大提高,因为我们的程序在加载的过程中,默认会把一个代码附近的代码也会加载到系统当中的,所以代码越密集,代码的执行效率越高。
for循环语句尽量要写成前闭后开型区间
理由有两个
第一个:前闭后开型区间的循环次数简单直观,就是两个区间端点值相减,例如
for (i = 0; i <10; i++){
这个语句的循环此时就是10-0为10 ,假如我们写成前闭后闭型区间时,我们的循环次数就是两个区间的端点值+1,例如,
for (i = 0; i <=9; i++){
我们的循环次数就是9-0+1也就是10
第二个:下标计算更加方便,例如
for (i = 0; i <10; i++){
假如我们根据for循环的循环次数,创建一个数组,注意数组的首元素也就是arr[1]对应的是i=0,arr[10]对应的是i=9,把整个数组都包裹完全了
for循环内,注意不要有浮点型的数据
goto语句的介绍
我们举一个例子
int main()
{goto end;printf("hello 1\n");printf("hello 2\n");printf("hello 3\n");
end:printf("hello 4\n");printf("hello 5\n");printf("hello 6\n");return 0;
}
goto语句是这样,跳过goto end和标签end的代码,直接执行标签end以后的代码。
如图所示 ,上面的goto语句表示的是向下跳转,其实,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");printf("hello 6\n");return 0;
}
我们进行编译
代码出现死循环,原因是什么?
答:代码每次运行到goto语句都会跳转到end标签处,end打印后会继续到goto语句,所以死循环
接下来,我们举一个goto语句应用的例子
int main()
{int i = 0;
start:printf("[%d] goto running \n", i);i++;if (i < 10){goto start;}printf("goto end\n");return 0;
}
我们进行运行
goto语句可不可以跨代码块使用
答:不能,我们进行检测
void fun()
{
start:printf("enter fun()");
}
int main()
{int i = 0;printf("[%d] goto running \n", i);i++;if (i < 10){goto start;}printf("goto end\n");return 0;
}
我们把goto语句的标签start放在我们自定义的函数fun()里面,我们进行编译,如果goto语句可以跨代码使用,应该会打印出enter fun()
可以发现,生成错误,所以:goto语句是不能跨代码块使用
很多公司禁止使用goto语句,不过这个问题我们还是灵活看待,goto在解决很多问题时有奇效的
下面提出一个问题:是否可用void来定义变量
我们写一个代码进行检测
int main()
{void x = 0;
}
我们进行编译
所以是不能用void类型来定义变量的,那么原因是什么呢?
有人会说:因为void类型是空类型,定义变量时开辟的空间是未知的,所以不能用来定义变量。
这种说法是可以理解的,但是真正的原因是什么呢?
我们先写一个代码
int main()
{printf("%d", sizeof(void));return 0;
}
我们这里求的就是void类型的变量占据空间的大小,我们进行编译
我们可以发现,结果为0,我们再在Linux系统上尝试一下
我们发现,在linux系统中void类型的变量却占据1个字节,所以void类型的变量开辟的空间不一定都是0,所以上面的说法不成立。真正正确的说法在下面总结处
总结:void本身就被编译器解释为空类型,强制的不允许定义变量
我们来写一个代码
void test()
{printf("hello test\n");return 1;
}
int main()
{test();
}
我们的编译器是能编译过去的
但是只要我们创建了一个变量来接受这个函数的返回值
void test()
{printf("hello test\n");return 1;
}
int main()
{int a=test();
}
我们进行编译,可以发现
代码报错,所以我们得出结论void类型的函数,正常情况下是能编译过去的,但是假如我们创建了一个变量来接受这个函数,就会报错。
我们把函数的返回值类型void去掉,可以发现
test()
{printf("hello test\n");}
int main()
{test();
}
test()
{printf("hello test\n");}
int main()
{int a= test();
}
无论是否创建变量接受函数的返回值,函数都不会报错
所以在c语言中,可以不带返回值,默认的返回值类型是int
那是不是就说明了以后我们在书写函数的时候,是不是可以不带返回值类型呢?
答:不可以,如果我们的函数没有返回值时,我们还是要在返回值的前面加上void类型
因为:在默认的情况下,函数的返回值是整型,在没有返回值的时候,返回值的类型也是整型,所以当别人看见你的代码,别人就会产生疑问:是这个忘写返回值了,还是这个函数是没有返回值的。
void修饰函数返回值有两个作用
1:一方面告诉代码的编写者不要添加返回值了
2:当你加上void类型,即使你有返回值,如果你不接受的情况下,编译是能编过的,但是只要你想要接受,或者存储返回值,编译就无法通过,起到检验错误的作用
我们再写一串代码
int test1()
{return 1;
}
int test2(void)
{return 1;
}
int main()
{test1(1, 2, 3, 4);
}
我们对代码进行分析:首先,第一个函数test1和第二个函数test2的区别在于test2函数没有参数的
我们对test1传参,进行编译,可以发现,代码正常运行
但是当我们把对test2函数传参时,在编译器vs2013版本下,依旧正常运行,但是代码产生警告
总结:void充当函数的形参列表,告诉编译器和代码的编写者函数不需要传参
void不能用来创建变量,那么void*可以吗?
答:可以,原因如下:因为类型明确,void*就是一个指针,在32维平台下占四个字节,64位平台下占八个字节
写一串代码
int main()
{void*p = NULL;double*x = NULL;int *y = NULL;x = p;y = p;
}
我们进行编译,可以发现代码正确运行
总结:void型的指针可以用其他任何类型的指针来接受
我们再换一种写法
int main()
{void*p = NULL;double*x = NULL;int *y = NULL;x = p;y = p;p = x;p = y;
}
我们进行运行,依旧成功运行
总结:void型的指针可以接受任意类型的指针
提问:void类型的指针可以进行加减运算吗?
我们写一个代码进行检验
int main()
{int *p = NULL;p++;p--;
}
我们首先写一个整型指针,检验其是否可以进行加减运算
代码是可以运行的 ,接下来我们试试void类型的指针
int main()
{void *p = NULL;p++;p--;
}
可以发现
产生错误,所以void型的指针是不能进行加减运算的
究其原因是:指针加减通常是从一个指针变量移位到另一个指针变量,所以涉及到指针类型的步长,因为void类型是没有步长的,也就不清楚加1减1移位的距离,所以不能进行加减运算
注意:在Linux系统中,void类型的指针是可以加减运算的,原因是因为在linux中,void类型的步长被认为是1
void类型的指针能够直接解引用吗?
答:不能
int main()
{void*p = NULL;*p;
}
我们进行编译:
有人可能会认为p为空指针,所以不能解引用,所以我们换一个其他的
int main()
{int a = 10;void*p = &a;*p;
}
注意:我们之前有说过,void型的指针能够接受任意类型的指针变量,所以也能接受int类型的指针。我们进行运行
依旧是非法寻址。
总结:void类型的指针不能直接解引用
为什么呢?
答:因为p的类型void类型,void类型的指针解引用后也是void类型的变量,因为void类型的变量是无法创建的,所以void型的指针不能直接解引用
注意:c语言中有字符串,但是没有字符串类型。
第8讲 - C语言关键字(8)相关推荐
- c语言 char转int_C语言关键字及进制的转换你都知道吗?
前面我们讲过 C语言简洁.紧凑 使用方便.灵活 那是什么使得C语言这么方便呢? 那就是关键字,或称保留字 C语言的关键字共有32个 根据关键字的作用 可分为 数据类型关键字 控制语句关键字 存储类型 ...
- c语言关键字不做标识符,C语言标识符、关键字、注释、表达式和语句
这一节主要讲解C语言中的几个基本概念. 标识符 定义变量时,我们使用了诸如 a.abc.mn123 这样的名字,它们都是标识符(Identifier). 标识符就是程序员自己起的名字,除了变量名,后面 ...
- 计算机java语言教程,计算机JAVA教程二讲Java语言基础知识.doc
计算机JAVA教程二讲Java语言基础知识 2.1简单数据类型 2.1.1 标识符和保留字 1.标识符 程序员对程序中的各个元素加以命名时使用的命名记号称为标识符(identifier).Java语言 ...
- C语言关键字----Const
C中CONST的使用: 虽然这听起来很简单,但实际上,const的使用也是c语言中一个比较微妙的地方,微妙在何处呢?请看下面几个问题. 问题:const变量 & 常量 为什么下面的例子在使用一 ...
- 【译】volatile C语言关键字,如何使用?
原文 概要 许多程序员无法正确的理解C语言关键字volatile.这并不奇怪,大多数C原因书籍不过一两句一带而过.本文将告诉你如何正确使用它. 在C/C++嵌入式代码中,你是否经历过下面的情况: 代码 ...
- c语言 关键字 and,C语言的关键字(一)
每次讲关键字之前,我总是问学生:C 语言有多少个关键字?sizeof 怎么用?它是函数 吗?有些学生不知道 C 语言有多少个关键字,大多数学生往往告诉我 sizeof 是函数,因为 它后面跟着一对括号 ...
- 从关键字~C语言 — 期末考,考研,面试中那些你不得不知道的C语言关键字细节
目录 引言: 初步认识了C语言的结构之后 博主将带您进入关键字的世界了 1. 关于变量 1.1 什么是变量 1.2 变量的定义与声明 1.3 为什么要定义变量 1.4 变量定义的本质 2. 关键字 2 ...
- 第3讲 C语言之函数讲解 Function
第3讲 C语言之函数讲解 Function 1. C语言函数的定义与声明(define & declare) 1.1 C语言的入口函数Main函数 1.2 C语言的自定义函数 1.2.1 C语 ...
- c语言入门第1讲,第1讲-c语言入门.ppt
第1讲-c语言入门.ppt * * * * * * * * * * * * * * * * * * * * * * * * * * * 母板制作:J0座机电话号码 张茂国 母板制作:J0座机电话号码 ...
最新文章
- HTML教程--多页面窗体
- Linux gdb 破解软件密码
- 数据库面试题【十五、优化查询过程中的数据访问】
- [转帖]Linux修改时区
- C++中 引用与取地址的区别
- 爬虫 spider08——爬取腾讯娱乐新闻【使用redis去重】
- 简述使用REST API 的最佳实践
- 淘宝设计万能PSD分层模板(简约页面设计——少即是多)
- 《统计学习方法》(李航)读书笔记
- 【备注】【C14】《HeadFirstJava(中文版)(第2版)》 PDF 下载
- 初次联系导师短信模板_申博经验分享|如何联系导师?
- Mark down学习日记
- 计算机应用唐家琪,基于机器学习的蛋白质相互作用预测研究
- 爬取雪球网的股票信息评论
- A - Robot Rapping Results Report
- 智商情商哪个重要_情商与智商哪个更重要?三个方面让你彻底明白
- 了解黑客经常使用哪些工具
- 英语句子主干成分分析
- [VCS]Coverage Options Introduction
- [原][彩]情诗两首[下]--期对酒于襄阳,待重归于长江。
热门文章
- ImportError: /home/yang/anaconda3/envs/sar/bin/../lib/libgio-2.0.so.0: undefined symbol: g_unix_get_
- CRMEB商城直播功能-微信小程序直播
- SQL Server性能优化
- 第1关 Numpy创建数组 (educoder
- android下开启AP热点
- 微信装修装饰小程序源码开发方案
- Windows指纹登录
- 原始套接(ARP协议的使用)
- Java中的statis用法
- Matplotlib——饼图pie()函数