C语言学习记录230615

        老是直接开始总结今日的学习总是缺点劲,所以开头还是分享下今天有趣的事情吧。

        日本某个悲催的小伙子,为了搬家准备了一个人,就在要搬过去时发现新家被拆了,原因好像是拆解队搞错了要拆的房子,把新房子认为当做是老房子拆了,在学习工作之余,看到这样的消息,我只能感叹,笑容并未消失,但是笑容能转移。

        在认识和了解C语言的课程范围内,今天继续昨天尚未完全开始的部分,其实昨天是讲到了数组的部分,数组搭配for语句有好几个经典的方式,但因为也没啥特别需要讲的,就跳过好了。后续正式到数组的时候再展开补充。

        在课前作业题的解析中,也有不少细小的知识点,这里值得说一下:

                作业题中要求“多行输入”的情况,此前有多次输入的情况,PTA里一般会有明确的次数,所以我在通过scanf给变量赋值时,主要考虑的是变量是否能与转换说明的要求对应,而不考虑循环的问题,当仅一次输入的时候,我会写成while(scanf("%d",&a)!=1)continue;或者while(scanf("%d",&a)==EOF)continue;的形式,这里的意思是当scanf返回值不是1或者scanf返回值为文件结束符时(即读取数据与转换说明不符),则无限循环。后面实际上直接接空语句即可,这里写continue主要用于凸显这是个空语句,并没有实际意义。在OJ系统里,我喂进去这样的程序以后,提示我不正确,好在这个OJ系统能显示测试样例,我看了下是三行的多次输入,顿时有点摸不着头脑,恰好旁边有别人写好并且通过的代码,他们是写成while(scanf("%d",&a)!=EOF){循环体},恰好与我的意思相反。我后续问了下GPT知道了EOF为文件结束符,不同操作系统可以用ctrl+d合作和ctrl+z表示,我是window7系统,用ctrl+z能正常结束,才算解决了问题。当天课程讲课的时候说了下这个问题,才知道OJ系统是能自己输入文件结束符来结束程序的,这时多次输入就可以用上述方式解决。此外,EOF实际上等于-1,C语言定义了EOF -1的宏。

                最后,我再自己补充一下,其实包括printf在内的许多函数,其实都是有返回值的,这些返回值只是我们不用而已,在《C方法》一书中,有说道,有时特意为了表达这些值不用,会用一种委婉的方式表达我们不要这个数,即用强制类型转换,(void)表示将数值转变为空,例子如下:

                (void)(scanf("%d",&a);

        接下来是正式的当日有所收获的内容总结:

        (一)操作符(运算符)

                我更习惯于《C方法》一书中对于各类概念的定义,所以接下来操作符将统一用运算符称呼。

                算术运算符、关系运算符、简单赋值运算符和复合赋值运算符跳过,位运算符和移位运算符主要用于二进制数,这里也跳过。暂时重点放在单目运算符上,所谓单目运算符,即指只需要一个操作数的运算法,像‘+’  ‘-’  ‘*’  ‘/’都属于双目运算符,他们需要两个操作数,举个例子,1+2,这里1和2都是操作数,‘+’是运算符,他需要两个操作数,所以叫双目运算符,以此类推单目运算符即可。

               ① ‘!’非运算符(逻辑反运算符)

                简单的说就是取反,把非0值变为0,但0值并不是变为随便一个非0值,而是变为1,所以这个运算符在if语句的控制表达式中运用十分便利,举个例子,我们定义一个变量a,我们希望当a非0时能打印hello,或者a为0时打印world,当不用非运算符我们用if语句可以这么表示:

                                if(a)printf("hello");或者 if(a==0)printf("world");

                这里在判断a非0时挺清晰的,但当需要判断a为0时,需要用‘==’关系运算符,实际上有点拖沓。

                假如用非运算符,这里可以略做简化:

                                if(a)printf("hello");或者 if(!a)printf("world");

                这样就能非常清晰快速的实现判断a为0的情况,因为这里采用非运算符,当a为0时,!a对a取反,!a的值为1,if语句当控制表达式的值为非0值时,将运行语句。

                或许这里不值得这么铺开讲,但确实是个小细节。

                ②sizeof运算符

                +、-的单目运算符就跳过,跟数学上的正负是一个道理,在一个数前面加正负,逻辑上是跟数学一样的,sizeof运算符课程没讲多,主要是我自己梳理下自己知道的东西,sizeof运算符的作用,是用于将对应类型的字节长度计算出来,并且他的值是有专属于自己的固定类型的,它的类型为size_t,C99之前是不能直接输出这个类型的数据,C99以后好像可以通过转换说明%zu还是%uz能实现直接打印输出。这个运算符是单目的,他跟操作数结合是有讲究的,他的标准格式是sizeof(操作数),他的值就是对应操作数的类型所占的字节大小,比如字符类型的字节为1,所以sizeof(char)或者sizeof(任意字符变量)他的值都是1,而其他整型变量的字节大小,则跟实现有关,通俗的讲,跟机器有关系,不同的操作系统,他的值会不一样。这个运算符在数组和for语句里有一个经典的运用,就是利用sizeof求静态数组(必须是静态数组,动态内存分配的数组不能这么做,如果这么做,sizeof最后的)的数组元素数量,然后利用for语句进行循环初始化等其他操作,假设一个元素类型为int的数组a,大致大概代码如下:

                                for(int i=0;i<sizeof(a)/sizeof(int);i++)

                这里运用sizeof运算符计算了静态数组a的字节大小,然后又用sizeof计算了int类型所占的字节大小,我们知道数组表示的是一组相同数据类型的元素集合,那么用他们总的占用字节大小除以单个元素占用的字节大小,就能得出他们总的元素个数。运用这个方式对数组进行各类计算有很多好处,首先int类型的字节大小是跟着实现来的,其次数组的元素多少,我们有时候会想调整,而用这种方式的话,不管静态数组如何调整元素数量,我们都不用改变这个代码。(当然,采用宏的方式对静态数组赋值也是一个很不错的方式)。

                最后再小小提一点,我们前面知道了sizeof运算符的值是有特定类型的,他的类型是size_t,而i的类型为int,这里进行关系运算时,有时会因为两个操作数的类型不同而产生warnning,这里可以通过增加强制类型转换解决这个问题,上式按下面这么改就行:

                                 for(int i=0;i<(int)sizeof(a)/sizeof(int);i++)

                ③'~'取反运算符

                这个是新学到的,他的作用是对二进制数取反,接下来举个例子就能很容易理解,假设有一个二进制数为10010011,用取反运算符计算后,得到的值为01101100,就是对二进制各个位数进行取反,即0变成1,1变成0。

                ④'++'自增运算符和‘--’自减运算符

                简单说一下,自增和自减运算符是单目运算符,他复杂的原因在于它既可以放在变量前,也可以放在变量后,他本身的作用是在对变量+1或-1,例如声明并初始化一个变量a=1,

a++;或++a;都表示a的值+1,运算过后,a的值为2,自减运算符以此类推。单独一个语句运用这两个运算符没有任何疑问,但是当着两个运算符出现在相对复杂的表达式内,与其他运算符相结合的情况下,问题就会复杂起来,很复杂的情况这里不说,主要说明一下简单的情况下,自增自减运算符的结果:

                                b=a++;(这条语句执行后b=1,a=2)

                                b=++a;(这条语句执行后b=2,a=2)

                                b=a--;(这条语句执行后b=1,a=0)

                                b=--a;(这条语句执行后b=0,a=0)

                深入一点说,当运算符在变量前时,b在引用该变量时,会按变量自增或自减后的量计算,而当运算符在变量后时,该条语句引用该变量时,会按变量按没自增和自减运算。其实这里的情况相对简单,下面这条语句的情况,b的值就只能根据实现来自己运算了。

                                b=(a++)+(a++)+(a++)+(a++);

                这时,我们难以分辨a到底是是在那个节点进行自增和自减,所以写代码要切记避开这么去写,因为结果无法预测。

                ⑤(类型)强制类型转换运算符

                前面的总结有说过这个运算符,这个运算符主要用于将操作数转变为对应类型,如一个double类型的3.14,通过(int)转化为int类型后,他的值为3。

                ⑥条件运算符

                关系运算符和逻辑运算符大致跟现实中的数学一致,这里就跳过了,条件运算符有特定格式,主要为控制表达式?表达式1:表达式2;可以念出来,如果控制表达式为真,这个条件运算符的值为表达式1的值,若为假,则这个条件运算符的值为表达式2的值,这个运算符最常见的情况就是在自定义函数的返回值上,假如一个自定义函数用于判断两个数的大小,谁大返回谁的值,那么这个函数可以写成:

                                int Max(int num1,int num2)

                                {

                                        return num1>num2?num1:num2;

                                }

                当num1大于num2时,返回num1,若小于等于num2,则返回num2。

                ⑦逗号运算符

                逗号运算符的格式就是表达式1,表达式2,表达式3,……;

                逗号表达式的计算顺序是从左往右依次计算各个表达式的值,他的值是最后一个表达式的值。例如表达式1,表达式2,表达式3;这个逗号表达式的值就是表达式3的值。

        (二)关键字

                首先要明确关键字是不能自己定义的,并且这些关键字不能占用或者作为变量名,我接下来主要说一下我在课程中新认识到的几个关键字。

                ①auto

                auto关键字实际上表达的是自动存储期,定义一个int类型,前面的auto是自动隐藏的。

                ②register

                称为寄存器,主要作用是当我们觉得某个变量会大量频繁使用时,为了提高运行效率,希望将这个变量存放到寄存器中,我们就可以在类型名前增加register建议系统将这个变量放到寄存器内,注意这里说的是建议,实际放不放到寄存器,我们是不能决定的。这里我主要通过讲师还了解复习了以前学过的计算机概论。将存储层次和速度重新捋了一下,速度由快到慢(空间由小到大)分为寄存器→高速缓存(cache)→内存→硬盘→云盘。告诉缓存和寄存器存在的意义就是为了解决CPU速度过快,内存跟不上的问题。

                ③volatile

                这个我也不知道是啥,讲师也没细说,只说是学C++的话会仔细说一下,目前我也只是知道有这么个东西。

        (三)typedef类型定义

                这个东西很有用,但我懒得讲了,实际讲师也没细讲,我只能说,有这个东西存在,能省略很多东西的同时,也增加了程序的可读性和可移植性。他的主要作用就是给某个类型或者函数换个名字。有兴趣的人,可以翻翻《C方法》,这本书里的不少例子真的很经典。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值