linuxc编程一站式学习笔记4(代码片段)

临风而眠 临风而眠     2023-01-23     496

关键词:

Linux C编程一站式学习笔记 chap4 分支语句

文章目录

这本书在和CS61A一起学,感觉两者教学上有些地方手法类似,都做的很棒

一.if语句

之前的程序中语句是从前到后顺序执行的。除了顺序执行之外,有时我们需要检查一个条件,然后根据检查的结果执行不同的后续代码,在C语言中可以用实现,比如:

if (x != 0) 
	printf("x is nonzero.\\n");

其中x != 0表示“x不等于0”的条件,这个表达式称为控制表达式(Controlling Expression)如果条件成立,则中的语句被执行,否则中的语句不执行,直接跳到后面。if和控制表达式改变了程序的控制流程(Control Flow),不再是从前到后顺序执行,而是根据不同的条件执行不同的语句,这种控制流程称为分支**(Branch)**

  • if后面的括号中是逻辑表达式,如果表达式所表示的比较关系成立则值为真(True),否则为假(False),在C语言中分别用int型的1和0表示,常用的符号有:==和!=称为相等性运算符(Equality Operator),其余四个>,<,<=,>=称为关系运算符(Relational Operator),相等性运算符的优先级低于关系运算符
  • 如果变量x的值是-1,那么x>0这个表达式的值为0,x>-2这个表达式的值为1,这里面有一些易错点
    • ==表示数学中的相等关系,相当于数学中的=号,初学者常犯的错误是在控制表达式中把==写成=,在C语言中=号是赋值运算符
    • 在数学中a<b<c表示b既大于a又小于c,但C语言会先计算a<b的结果得到0或者1,再把0或1继续参与运算
    • 整型或者都是浮点型可以直接做比较,但两个字符串不能用大于小于这些符号直接作比较

语句块

  • 在C语言中,任何允许出现语句的地方既可以是由;号结尾的一条语句,也可以是由括起来的若干条语句或声明组成的语句块(Statement Block),语句块和上一章介绍的函数体的语法相同。

  • 注意语句块的后面不需要加;号。如果后面加了;号,则这个;号本身又是一条新的语句了,在C语言中一个单独的;号表示一条空语句(Null Statement)。上例的语句块中只有一条语句,其实没必要写成语句块,可以简单地写成:

    if (x != 0)
    	printf("x is nonzero.\\n");
    
  • 语句块中也可以定义局部变量,例如:

    void foo(void)
    
    	int i = 0;
    	
    		int i = 1;
    		int j = 2;
    		printf("i=%d, j=%d\\n", i, j);
    	
    	printf("i=%d\\n", i); /* cannot access j here */
    
    

    和函数的局部变量同样道理,每次进入语句块时为变量j分配存储空间,每次退出语句块时释放变量j的存储空间。语句块也构成一个作用域,如果整个源文件是一张大纸,foo函数是盖在上面的一张小纸,则函数中的语句块是盖在小纸上面的一张更小的纸。语句块中的变量i和函数的局部变量i是两个不同的变量,因此两次打印的i值是不同的;语句块中的变量j在退出语句块之后就没有了,因此最后一行的printf不能打印变量j,否则编译器会报错。语句块可以用在任何允许出现语句的地方,不一定非得用在if语句中,单独使用语句块通常是为了定义一些比函数的局部变量更“局部”的变量。

习题

There is a semicolon (;) after the if statement, which causes the following printf statement to be executed unconditionally, regardless of the value of x.

Regarding why the code compiled without error, this is because the semicolon is a valid statement terminator in C. It is commonly used to end simple statements such as if and while statements, and is also used to separate multiple statements on the same line. However, in this case, the semicolon is being used incorrectly and is causing a semantic error.

The C compiler does not check for semantic errors, only for syntax errors. It is the responsibility of the programmer to ensure that their code is semantically correct.

二.if/else语句

引例

  • if语句可以带一个else子句(Clause),例如:

    if (x % 2 == 0)
    	printf("x is even.\\n");
    else
    	printf("x is odd.\\n");
    

    这里的%是取模(Modulo)运算符,x%2表示x除以2所得的余数(Remainder),C语言规定%运算符的两个操作数必须是整型的。%运算符的结果总是与被除数同号

    来尝试证明一下,注意C语言中整数的除法 的 Truncate Toward Zero 规则

    设 a 为被除数,b 为除数,q 为商,r 为余数。

    首先,我们知道有这样一个等式: a = b * q + r

    其中,q 是向下取整的商,即 q = floor(a/b)。

    我们知道a = b * q + r, 且0 <= r < |b|,因此

    • 当 a > 0, b > 0 时,r > 0; # q是向下取整的商,所以b * q肯定够不到 a 那么大
    • 当 a > 0, b < 0 时,r > 0; # 显然
    • 当 a < 0, b > 0 时,r < 0; # 显然
    • 当 a < 0, b < 0 时,r < 0; # q是向下取整的商,所以b * q肯定够不着 a 那么小

    所以,无论 a 和 b 的符号如何,余数 r 的符号总是和被除数 a 的符号相同。

    所以在C语言中,%运算符的结果总是与被除数同号

    取模运算在程序中是非常有用的,例如上面的例子判断x的奇偶性(Parity),看x除以2的余数是不是0,如果是0则打印x is even.,如果不是0则打印x is odd.

    如果在上面的例子中去掉else,则不管x是奇是偶,printf("x is odd.\\n");总是执行。为了让这条语句更有用,可以把它封装(Encapsulate)成一个函数:

    void print_parity(int x)
    
    	if (x % 2 == 0)
    		printf("x is even.\\n");
    	else
    		printf("x is odd.\\n");                      
    
    
  • 把语句封装成函数的基本步骤是:把语句放到函数体中,把变量改成函数的参数。这样,以后要检查一个数的奇偶性只需调用这个函数而不必重复写这条语句了,例如:

    print_parity(17);
    print_parity(18);                                                       
    

if/else语句 语法规则

  • 语句 → if (控制表达式) 语句 else 语句

    • 右边的“语句”既可以是一条语句,也可以是由括起来的语句块。

    • 一条if语句中包含一条子语句,一条if/else语句中包含两条子语句,子语句可以是任何语句或语句块,当然也可以是另外一条ifif/else语句。

    • 根据组合规则,ifif/else可以嵌套使用。例如可以这样:

      if (x > 0)
      	printf("x is positive.\\n");
      else if (x < 0)
      	printf("x is negative.\\n");
      else
      	printf("x is zero.\\n");
      

      也可以这样:

      if (x > 0) 
      	printf("x is positive.\\n");
       else 
      	if (x < 0)
      		printf("x is negative.\\n");
      	else
      		printf("x is zero.\\n");
      
      

if else 的配对原则

  • 很多编程语言的语法都有这个问题,称为Dangling-else问题

  • Dangling-else problem is a problem that arises in languages such as C and C++ when the syntax of the language does not specify the association of an “else” statement with the closest preceding “if” statement. This can lead to confusion about which “if” statement the “else” statement is associated with, and can result in unexpected behavior in the program. To avoid this problem, programmers often use braces(花括号) to explicitly specify the association between an “if” statement and its corresponding “else” statement.

  • C语言规定,else总是和它上面最近的一个if配对

  • 现在有一个问题,类似if (A) if (B) C; else D;形式的语句怎么理解呢?可以理解成

    if (A)
    	if (B)
    		C;
    else
    	D;
    

    也可以理解成

    if (A)
    	if (B)
    		C;
    	else
    		D;
    

    C代码的缩进只是为了程序员看起来方便,实际上对编译器不起任何作用,你的代码不管写成上面哪一种缩进格式,在编译器看起来都是一样的

  • 那么编译器到底按哪种方式理解呢?也就是说,else到底是和if (A)配对还是和if (B)配对?按照C语言的规定,应该理解成elseif (B)配对,也就是按第二种方式理解。如果你写成上面第一种缩进的格式就很危险了:你看到的是这样,而编译器理解的却是那样。

  • 如果希望编译器按第一种方式理解,应该明确加上braces:

    if (A) 
    	if (B)
    		C;
     else
    	D;
    

习题

1、写两个表达式,分别取整型变量x的个位和十位

int y;
y = x % 10; //个位
int z;
z = (x / 10) % 10 //十位

2、写一个函数,参数是整型变量x,功能是打印x的个位和十位

void printX(int x)

	printf("%d",x%10);
	printf("%d",(x/10)%10);

10位也可以是(x%100)/10

三.布尔代数

现在在并行看的书很多,在补大学前两年半欠下的债…
看着看着就会发现很多知识之间的关联性,以及一本好书,一份好的资料对于计算机思维的启迪, 而且环环相扣,很多概念的引入都非常自然

  • 我在想当时大一的时候,学校的C语言这一块可以多讲讲吧,我觉得这个和计算机的01很接近,可以作为计算机启蒙的一块很好的切入点…但是当时emm

  • 引入:

    a<b<c 这个用C语言怎么写

    if (a < b)
    
        if (b < c) 
            printf("b is between a and c.\\n");
        
    
    

    或者使用逻辑与(Logical AND) 运算符表示这两个条件同时成立

    if (a < b && b < c) 
        printf("b is between a and c.\\n");
    
    
  • 逻辑代数的内容

习题

1、把代码段

if (x > 0 && x < 10);
else
	printf("x is out of range.\\n");

改写成下面这种形式:

if (____ || ____)
	printf("x is out of range.\\n");

____应该怎么填?

  • 这个应该可以用德摩根律, 啊不看错了可以用也可以不用吧,好家伙,if 那后面就是分号😂,刚发现
    • 那就简单了 if( x <= 0 || x >= 10)
    • emm,理理思路,这些逻辑代数的知识确实可以用来检验if else语句是否达到自己想要的逻辑
      • a = x>0, b = x<10, if条件是a*b,那么else 就是¬(a*b)也就是¬a+¬b

2、把代码段:

if (x > 0)
	printf("Test OK!\\n");
else if (x <= 0 && y > 0)
	printf("Test OK!\\n");
else
	printf("Test failed!\\n");

改写成下面这种形式:

if (____ && ____)
	printf("Test failed!\\n");
else
	printf("Test OK!\\n");

____应该怎么填?

  • if ( x <= 0 && y <= 0) 吧, 但是好像脑子里没有很清晰的处理思路…

    • 两个变量,也许可以画个坐标轴线性规划?这个挺好的感觉

    • if和else if的两个条件可以看为a+¬a*b=a+b, 那么剩下的就应该是这个条件的补集,也就是¬(a+b)=¬a*¬b


3、有这样一段代码:

if (x > 1 && y != 1) 
	...
 else if (x < 1 && y != 1) 
	...
 else 
	...

要进入最后一个else,x和y需要满足条件____ || ____。这里应该怎么填?

  • x==1||y==1

    • 可以画坐标系

    • 也可以用德摩根律

      a为x>1,b为y!=1,c为x<1

      那前两个条件合起来就是 a*b+c*b

      然后德摩根律取反得到(¬a+¬b)*(¬c+¬b)


4、以下哪一个if判断条件是多余的可以去掉?这里所谓的“多余”是指,某种情况下如果本来应该打印Test OK!,去掉这个多余条件后仍然打印Test OK!,如果本来应该打印Test failed!,去掉这个多余条件后仍然打印Test failed!

if (x<3 && y>3)
	printf("Test OK!\\n");
else if (x>=3 && y>=3)
	printf("Test OK!\\n");
else if (z>3 && x>=3)
	printf("Test OK!\\n");
else if (z<=3 && y>=3)
	printf("Test OK!\\n");
else
	printf("Test failed!\\n");
  • 一开始直觉上的判断👇

    • 我感觉第三个和第四个else if 合并起来 , 就等价于 第二个 else if,所以可以把第二个 else if

      else if ( x>=3 && y>=3 )去掉

  • 后来推理了一下

    • 三维坐标系不好画,还是用公式吧

    • 其实这题的意思就是让我们化简逻辑表达式

      • 在这之前要先推导这两个公式 ,多余项定律

        名称公式1公式2化简目的
        多余项定律 A B + A ‾ C + B C = A B + A ‾ C AB+\\overlineAC+BC=AB+\\overlineAC AB+AC+BC=AB+AC ( A + B ) ( A ‾ + C ) ( B + C ) = ( A + B ) ( A ‾ + C ) (A+B)(\\overlineA+C)(B+C)=(A+B)(\\overlineA+C) (A+B)(A+C)(B+C)=(A+B)(A+C)消多余项

        在一个与或表达式中有三项,其中两项中有一因子以原变量和反变量形式分别出现这两项中剩余因子共同构成第三项,则该项多余,可消去

    • 推导出来也是第二个语句是多余的

      • 不过网上有个大佬的笔记里面说第四个是多余的🤔,链接如下

        https://www.zybuluo.com/ChristopherWu/note/72463,感觉他说的也有道理,目前我也不确定了

        不过感觉大佬这里笔误了所以导致做错了?

  • 这几个练习非常有启发性,我感觉在数电的教学里面就可以把C语言的if else 作为引例


四.switch语句

语法格式

switch (控制表达式) 
case 常量表达式: 语句列表
case 常量表达式: 语句列表
...
default: 语句列表

例程

#include <stdio.h>

void print_day(int day)

  switch(day) 
    case 1:
      printf("Monday\\n");
      break;
    case 2:
      printf("Tuesday\\n");
      break;
    case 3:
      printf("Wednesday\\n");
      break;
    case 4:
      printf("Thursday\\n");
      break;
    case 5:
      printf("Friday\\n");
      break;
    case 6:
      printf("Saturday\\n");
      break;
    case 7:
      printf("Sunday\\n");
      break;
    default:
      printf("illegal day number!\\n");
      break;
  


int main(void)

  print_day(2);
  return 0;

  • 流程是这样的

    如果传入的参数是2,则从case 2分支开始执行,先是打印相应的信息,然后遇到break语句,它的作用是跳出整个switch语句块。C语言规定各case分支的常量表达式必须互不相同,如果控制表达式不等于任何一个常量表达式,则从default分支开始执行,通常把default分支写在最后,但不是必须的

  • pythontutor NB 啊!

switch语句的注意点

  • case后面跟表达式的必须是常量表达式,这个值和全局变量的初始值一样必须在编译时计算出来。
  • 浮点型不适合做精确比较,所以C语言规定case后面跟的必须是整型常量表达式。
  • 进入case后如果没有遇到break语句就会一直往下执行,后面其它casedefault分支的语句也会被执行到,直到遇到break,或者执行到整个switch语句块的末尾。通常每个case后面都要加上break语句,但有时会故意不加break来利用这个特性,例如
#include <stdio.h>

void print_day(int day)

  switch(day) 
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
      printf("Weekday\\n");
      break;
    case 6:
    case 7:
      printf("Weekend\\n");
      break;
    default:
      printf("illegal day number!\\n");
      break;
  


int main(void)

  print_day(2);
  return 0;

  • 流程如何

    用pythontutor来看看

    • 这一步在switch

    • 下一步跳到print

  • switch语句不是必不可缺的,虽然可以用一组if ... else if ... else if ... else ...代替,但是一方面用switch语句会使代码更清晰,另一方面,有时候编译器会对switch语句进行整体优化,使它比等价的if/else语句所生成的指令效率更高


意外收获一堆宝藏

  • 受到之前pythontutor的启发,在谷歌上面直接搜了一下visualize c code

    又发现宝藏啦

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FjL6kgaI-1674368512156)(null)]

    呃但是有bug

    除了py之外都报下面这个error

  • 这个live模式是真的NB!

    https://pt.jjk.is/live.html#mode=edit

    实现了我之前想过的实时可视化Orz!!!

    Soga,所以这个相比于pythontutor就是,这个只能可视化python,但是可以实时可视化!


我猜Vscode 插件肯定有可视化

于是去搜了一下,果真

debug可视化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BmtjIRcT-1674368504405)(https://cdn.jsdelivr.net/gh/xin007-kong/picture_new/img/20230122132556.png)]

json可视化

自动生成流程图

csv可视化

相关资源、参考资料

linuxc编程一站式学习笔记5(代码片段)

LinuxC编程一站式学习笔记chap5深入理解函数文章目录LinuxC编程一站式学习笔记chap5深入理解函数一.return语句习题二.增量式开发三.递归我猜有递归可视化工具,一搜果真有收获习题GCD(GreatestCommonDivisor)最大公约数Fibonacci相关资... 查看详情

linuxc编程一站式学习笔记1(代码片段)

LinuxC编程一站式学习笔记chap1程序的基本概念打算重学计算机,重学C语言这本书的前言写的真好实在是惭愧…文章目录LinuxC编程一站式学习笔记chap1程序的基本概念一.程序和编程语言1.什么是程序2.程序由指令组成3.编程语言... 查看详情

linuxc编程一站式学习笔记2(代码片段)

LinuxC编程一站式学习笔记chap2常量、变量和表达式本书以C99为标准一.继续helloworld加入更多注释的helloworld可以用ctrl+(shift)+v复制到vim里面#include<stdio.h>/**comment1*main:generatesomesimpleoutput*/intmain(void) printf(/*comment2* 查看详情

linuxc编程一站式学习笔记3(代码片段)

lLinuxC编程一站式学习笔记chap3简单函数文章目录lLinuxC编程一站式学习笔记chap3简单函数一.数学函数C标准库和glibc二.自定义函数三.形参和实参ManPage习题四.全局变量、局部变量和作用域局部变量localvariable全局变量globalvariable全局... 查看详情

《linuxc编程一站式学习》——第一个程序helloworld.c

1、首先确保linux系统下安装了gcc编译器,使用vim编写一个简单的C程序:2、保存退出,用gcc命令编译,默认会生成a.out可执行文件,如果要生成指定名称的文件,需要加入-o appname 参数:3、运行程序;4、对于程序中出现的... 查看详情

linuxc一站式学习习题答案11.6.3求n次方(代码片段)

3、编写一个函数doublemypow(doublex,intn);求x的n次方,参数n是正整数。最简单的算法是:doubleproduct=1;for(i=0;i<n;i++) product*=x;这个算法的时间复杂度是Θ(n)。其实有更好的办法,比如mypow(x,8),第一次循环... 查看详情

一站式学习java网络编程-学习手记(代码片段)

1.概念图解BIO模型:客户端每有一个请求,服务端都要有一个线程来单独处理这个请求,典型的一请求一应答,java1.4版本之前对于聊天室服务器,它有多个线程,其中一个为图上的Acceptor线程(ChatServer&... 查看详情

一站式学习java网络编程-学习手记(代码片段)

1.NIO模型分析在服务器端创建一个Selector,将ServerSocketChannel注册到Selector上,被Selector监听的事件为Accept Client1请求与服务器建立连接,Selector接收到Accept事件,服务器端对其进行处理(handles),服务器... 查看详情

一站式学习java网络编程-学习手记(代码片段)

1.BIO阻塞模型简述BIO模型中服务端与客户端的响应过程服务器serverSocket先要和端口进行绑定绑定完成后,执行accept方法,等待客户端的连接,这个方法是阻塞式调用,也就是说,要一直等待客户端的连接响应&#x... 查看详情

一站式学习java网络编程-学习手记(代码片段)

1.概述1.1什么叫NIO?NIO:我认为翻译成Non-Blocking,更加的通俗直白,相比于BIO,也有一个对比,叫他非阻塞IO最好不过了它和BIO有以下的区别Channel是双向的,即可以读又可以写,相比于Stream,它... 查看详情

并发编程(学习笔记-共享模型之内存)-part4(代码片段)

文章目录并发编程-4-共享模型之内存1.Java内存模型2.可见性2-1退不出的循环2-2可见性vs原子性3.有序性3-1诡异的结果3-2解决方法3-3有序性理解3-4happens-before4.volatile原理4-1如何保证可见性4-2如何保证有序性4-3double-checkedlocking问题并发... 查看详情

学习编程技术之路

...Simpleandreliable——简单可依赖 C语言学习 LinuxC编程一站式学习http://learn.akae.cn/media/ LearningGNU 查看详情

学习笔记(二十)——网络编程(代码片段)

文章目录一、网络通信概述1.1、什么是网络1.2、使用网络的目的二、TCP/IP简介2.1、什么是协议2.2、计算机网络沟通三、端口和端口号3.1、什么是端口3.2、端口号3.3、端口分配3.3.1、知名端口(WellKnownPorts)3.3.2、动态端口&#... 查看详情

计算机网络学习笔记4-tftp编程(代码片段)

TFTP协议简单文件传送协议,是基于UDP的应用层协议,被设计用来传输小文件通信过程:TFTP通信过程总结(无选项)1、服务器在69号端口等待客户端的请求2、服务器若批准此请求,则使用临时端口与客户端进行... 查看详情

c语言学习笔记:编程基础(代码片段)

目录一、冯诺依曼模型二、程序语言发展历史三、进制3.1二进制3.2八进制3.3十六进制3.4进制转换:二进制、八进制、十六进制、十进制之间的转换3.4.1将二进制、八进制、十六进制转换为十进制3.4.2将十进制转换为二进制、八... 查看详情

并发编程(学习笔记-共享模型之无锁)-part5(代码片段)

文章目录并发编程-5-共享模型之无锁1.无锁解决线程安全问题2.CAS与volatile2-1CAS2-2volatile2-3为什么无锁效率高2-4CAS特点3.原子整数4.原子引用4-1原子引用的使用4-2ABA问题及解决5.原子数组6.字段更新器7.原子累加器8.LongAdder详解8-1cas锁8... 查看详情

学习笔记hadoop——mapreduce编程进阶(代码片段)

文章目录一、输出文件格式及序列化文件生成1.1、输出文件格式1.2、设置输出SequenceFileOutputFormat文件格式二、输入文件格式及序列化文件读取2.1、输入数据文件类型2.2、设置输入SequenceFileInputFormat文件格式三、使用Partitioner优化... 查看详情

《golang高级编程》学习笔记(代码片段)

一、数组、字符串、切片1、数组定义方式:vara[3]int//定义长度为3的int型数组,元素全部为0varb=[...]int1,2,3//定义长度为3的int型数组,元素为1,2,3varc=[...]int2:3,1:2//定义长度为3的int型数组,元素为0,2,3vard=[...]int1,2,4:5,6//定义... 查看详情