当前位置 博文首页 > jtwqwq的博客:翁恺老师C语言程序设计网课(6)

    jtwqwq的博客:翁恺老师C语言程序设计网课(6)

    作者:[db:作者] 时间:2021-08-19 09:52

    6.2.1 逻辑类型:表示关系运算和逻辑运算结果的量

    bool类型
    首先要包含头文件#include<stdbool.h>,然后可以使用bool,true,false。

    bool b=6>5;
    

    只要bool量不是0,都=1.

    6.2.2 逻辑运算:对逻辑量进行与、或、非运算

    逻辑运算是对逻辑量进行的运算,结果只有0或1。
    逻辑量是关系运算或逻辑运算的结果。

    运算符描述示例结果
    !逻辑非!aa的true或false反转
    &&逻辑与a&&b只有a&b都是true时结果才是true
    ||逻辑或a||b只有a&b都是false时结果才是false

    例:表达数学区间时,(4,6)或[4,6]:
    同样像之前if一样,不可以写4<x<6这种式子。因为4<x的结果是一个逻辑值(0或1)
    x>4&&x<6x>=4&&x<=6
    判断一个字符是大写字母:c>='A'&&c<='Z'
    优先级:!>&&>||
    例:!done&&(count>MAX):done是0且count>MAX时结果为1

    所有的优先级

    优先级运算符结合性
    1()从左到右
    2!,+,-,++,–从右到左(单目的+和-)
    3*,/,%从左到右
    4+,-从左到右
    5<,<=,>,>=从左到右
    6==,!=从左到右
    7&&从左到右
    8||从左到右
    9=,+=,-=,*=,/=,%=从右到左

    短路:逻辑运算自左向右,如果左边足以决定结果,就不会进行右边的计算了
    a==6&&b==1如果左边a!=6就终止了。
    即:&&左边false就不做右边了;||左边是true就不做右边了。就算右边是赋值的话右边也不会做了。

    int a=-1;
    if(a>0&&a++>1){
    	printf("OK\n");
    }printf("%d\n",a);
    

    这样输出结果a=-1,没有进行右边的a++。
    所以不要把赋值和复合赋值组合进表达式!

    6.2.3 条件运算符

    条件运算符1:问号

    cnt=(cnt>20)?cnt-10:cnt+10;
    //格式:条件?条件满足时的值:条件不满足时的值
    

    相当于

    if(cnt>20)cnt-=10;
    else cnt+=10;
    

    这种条件运算符?的优先级高于赋值运算符,但是低于其他运算符,但是这样会很麻烦。
    条件运算符自右向左,先计算右边的分支
    不建议使用嵌套的条件表达式!太复杂,太难理解

    条件运算符2:逗号
    逗号是个运算符,连接两个表达式,并且以右边的表达式的值作为其结果。
    逗号的优先级是最低的,所以两边的表达式会先运算,而右边的表达式的值留下来作为运算的结果。

    i=3+4,5+6;//编译时会提示warning。因为赋值的优先级也要高于逗号,该式子其实是
    (i=3+4),(5+6);//右边编译错误,i=7
    如果写作i=(3+4,5+6);//编译时同样会warning,因为3+4没有用到;i=11
    

    逗号表达式主要在for中使用

    for(i=0,j=10;i<j; i++,j--)
    

    目前来说,逗号表达式只有这一个用处。

    7.1.1 初见函数

    回想起区间[m,n]内素数求和的例子,对于每个数我们定义isPrime=1,如果最后仍然=1的话sum+=i.
    这样让这一层循环显得很大,而这一层代码的功能很单纯
    我们可以以函数的形式把这一堆代码取出来

    int isPrime(int i)
    {
    	int ret=1;
    	int k;
    	for(k=2;k<i-1;k++){
    		if(i%k==0){
    			ret=0;
    			break;//有1~i-1间的因数就可以判断i不是素数了,break节约时间
    		}
    	}
    	return ret;
    }
    

    我们之前从第一个程序printf开始就是在用函数。只不过这次我们在自己定义了一个函数。
    这样main里面很简洁,而且isPrime可以很方便地多次使用
    PS: 程序中出现几段几乎完全相似的代码(除了字母不同),“代码复制”是程序不良的表现。因为将来做修改、维护的时候要维护很多处。
    (吓得我立刻就去改大作业了)我们用函数替换,可以把重复的代码提出。

    //闭区间内所有数求和的函数
    void sum(int begin,int end)
    {
    	int i,sum=0;
    	for(i=begin;i<=end;i++){
    		sum+=i;
    	}
    	printf("%d到%d的和是%d\n",begin,end,sum);
    }
    
    int main()
    {
       sum(1,10);
       return 0;
    }
    

    7.1.2 函数的定义和调用

    函数是一块代码,接收0个/多个参数做一件事情,并返回0个/1个值。
    可以先想象成数学中的函数y=f(x)。
    调用函数时要写出 函数名(参数值);
    ()起到了表示函数调用的重要作用,即使没有参数我们也需要()。
    不给括号的话,会给warning,而且函数不会被调用。
    如果有参数,参数的数量、顺序都必须正确。而且函数知道每一次是哪里调用它,还会返回到正确的地方。

    7.1.3 从函数中返回

    int函数会有返回值return
    return停止函数的执行,并返回一个值
    return;
    return 一个值;
    可以写c=函数();这样c=函数的返回值。
    可以把这个值赋给变量/传给函数/丢掉。
    没有返回值的函数:void,不能使用带值的return ,可以没有return。调用的时候也不可以做返回值的赋值。

    7.2.1 函数原型:用来告诉编译器这个函数长什么样

    使用函数的先后顺序:先写函数再调用。在这里插入图片描述
    可以先想象成数学函数y=f(x)
    C的编译器自上而下分析你的代码,它会记住函数sum()长什么样子要几个参数,每个参数的类型如何,返回什么类型
    如果把函数放在后面,C语言可能会猜,你在main函数里使用的这个函数是什么类型。如果后面发现后面的函数类型和它猜测的不符,就输出error
    因此也可以先不用写完函数,光把一句函数头放到前面编译也能通过。
    (事先声明了函数的样子)
    下面函数还会判断一下和你之前的声明是否一致

    void sum(int begin,int end);//声明
    
    int main()
    {
    	//略去不表,其中用到了sum(a,b)
    }
    
    void sum(int begin,int end)//定义
    {
    	int i,sum=0;
     	for(i=begin;i<=end;i++){
     		sum+=i;
     	}
     	printf("%d到%d的和是%d\n",begin,end,sum);
    }
    

    函数头以分号结尾,就构成了函数的原型;
    在函数里定义的参数类型与输入的变量的类型不一样,会发生自动类型转换。

    double max(double a,double b);
    

    在以前是把函数原型写在调用它的函数(main())里面的。
    函数的原型里可以不写参数名,但一般仍然写上。

    Void sum(int ,int)
    

    不过对于人类读者,一般来说写上会更好理解。

    7.2.2 参数传递:调用哪个函数的时候,是用表达式的值来初始化函数的参数

    传递给函数的值可以是表达式的结果(max(a+b,c))
    包括字面量、变量、函数返回值(函数调用里有函数)、计算结果
    当调用函数的值与参数类型不匹配:编译器总是悄悄替你把类型转换好,但是这很可能不是你所期望的。这是C语言传统上最大的漏洞。
    后续语言如c++/java在这方面会很严格。
    这样的函数代码能交换a,b的值吗?

     void swap(int a,int b)//形参
    
    int main()
    {
       int a=5;
       b=6;
       swap(a,b);//实参
    }
    void swap(int a,int b)//形参
    {
    	int t=a;
    	a=b;
    	b=t;
    }
    

    不能。
    C语言在调用函数时,永远只能传值给函数。
    每个函数有它自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系。
    后面递归还会再提。
    过去对于函数参数表中的参数叫做形式参数。调用函数时给的数值叫做实际参数。
    (见上图)
    由于易误会,我们不再这么称呼。我们把参数表中叫做参数,调用函数的叫做值。

    7.2.3 本地变量:定义在函数内部的变量是本地变量,参数也是本地变量

    函数每次运行都会产生一个独立的变量空间,其中的变量是函数这一次运行所独有的。(本地变量)
    所有我们定义在函数内部的变量就是本地变量。(我们现在学过的都是定义在函数内部的)我们写在函数参数表里的参数也是本地变量。

    变量的生存期和作用域

    生存期:这个变量出现和消亡的时间
    作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用)
    对于本地变量,这两个答案都是:大括号内(块)

    void swap(int a,int b)//形参
    
    int main()
    {
      int a=5;
      b=6;
      swap(a,b);//实参
    }
    void swap(int x,int y)//形参
    {
      int t=x;
      x=y;
      y=t;
    }
    

    比如仍然是上例,进入swap函数之后(离开了自己的变量空间)a,b就没了。还在生存,但是不在当前的作用域了(显示:Not found in current context)
    而回到原函数中之后,x,y,t就不存在了
    所以在swap函数里交换a,b不会影响到原函数
    本地变量的规则

    1. 本地变量定义在块内。可以是函数的块内,语句的块内,如:
      if (a<b)int i=10;
      离开if语句之后未定义使用i编译错误。(没声明)
      程序运行进入这个块前,其中的变量不存在。离开后就消失了。
      如果在块里定义了块外已经定义过的变量,就把块外的变量掩盖了;出来之后又回到块外的值。(C语言)
      但是不能在块里面定义同名变量(多次定义)
      本地变量不会被默认初始化,不会得到一个初始值。而参数进入函数的时候被初始化了。

    7.2.4 函数庶事:一些细节,main()是啥

    函数没有参数的时候写void f(void)。而写void f()在传统C语言中表示f的函数的参数表未知,并不表示没有参数。(编译器可能会猜测什么类型)
    所以不要写空的括号
    调用函数时的逗号和逗号运算符怎么区分?再加一层括号就是逗号运算符f((a,b))
    C语言不允许函数嵌套定义。
    (也最好不要写return (i);虽然意思上没变,但是会让人误会return 是个函数)
    int main(void)也是个函数,所以return 0;也是有意义的。

    cs