【C】3、条件与循环

一、if条件语句

1、单个if

if 括号里的是条件,只有当括号里面的条件满足了,才会执行大括号里面的。

这里的满足条件,换句话说就是条件为真,这个条件为真我们有时候会用数字来表示,通常使用1和0来分别表示真假。

if括号里面的条件有时候很喜欢跟逻辑关系一起运用,这个里面是存在“短路”的,具体来说,是或逻辑的条件语句中,第一个满足条件了,就不会执行后面的条件。


这是一个普通的简单的if条件语句:

#include <stdio.h>
int main(void)
{
  if(1 + 1 == 2)
  {
    printf("true!\n");
  }
  return 0;
}


条件用数字表示的情况:

#include <stdio.h>

int main()
{
    if (1)
    {
        printf("大帅批!\n");
    }
    if (0)
    {
        printf("啊对对对!\n");
    }
    
    return 0;

}


关于那个短路的情况:

#include <stdio.h>

int main()
{
    if (1 || printf("hello\n"))
    {
        printf("大帅批!\n");
    }
   
    return 0;
}

2、多个if

运用多个 if 条件,使其包含多种可能,这些条件随着程序从上到下顺序依次执行,用的时候区分于if-else,if-else只执行if或者else中的一个。

// 输出分数,并给分数评定等级
#include <stdio.h>
int main(void)
{
  int a;
  printf("请输入分数:\n");
  scanf("%d",&a);

  if(a >= 90 && a <= 100)
  {
    printf("等级:A,优秀\n");
  }

  if (a >= 80 && a <= 90)
  {
    printf("等级:B,良好\n");
  }

  if (a >= 70 && a <= 80)
  {
    printf("等级:C,一般\n");
  }

  if (a >= 60 && a <= 70)
  {
    printf("等级:D,及格\n");
  }

  if (a < 60)
  {
    printf("等级:E,不及格\n");
  }

  if(a > 100)
  {
    printf("输入错误!");
  }
    
  return 0;
}

3、if else

满足if的条件就执行if的大括号里面的,不满足则执行else的,

而一旦形成了if-else ,两个代码块的中间就不能插入其他代码块了,额,你在这中间空格几行其实是没啥问题的,,,

#include <stdio.h>
int main(void)
{
  int a;
  printf("请输入a的值:\n");
  scanf("%d",&a);

  if(1 + 1 == a)
  {
    printf("true!");
  }
  else
  {
    printf("false!");
  }
  return 0;
}

4、else if

else-if 并不是单独使用的,在他前面,一定会有if。而假如后面还跟了一个else的话,那么else后面的条件,默认是相邻的那个 if 的补集。

// 输出分数,并给分数评定等级
#include <stdio.h>
int main(void)
{
  int a;
  printf("请输入分数:\n");
  scanf("%d",&a);

  if(a > 100)
  {
    printf("输入错误!");
  }
  else if(a >= 90)
  {
    printf("等级:A,优秀\n");
  }

  else if(a >= 80)
  {
    printf("等级:B,良好\n");
  }

  else if(a >= 70)
  {
    printf("等级:C,一般\n");
  }

  else if(a >= 60)
  {
    printf("等级:D,及格\n");
  }

  else
  {
    printf("等级:E,不及格\n");
  }

  return 0;
}

5、if的判断条件会被执行一次

之前万万没想到一点,就是if判断条件中的语句,其实会被执行一次。遇见这个问题的时候已经属于被坑了,此时我刚刚从标准IO的fclose中回头来,


错误情况:

fclose(Fd_1);
if (EOF == fclose(Fd_1))  
{
    perror("fclose file failed:");
}

这一个程序段是我计划用来关闭刚刚打开的文件的,在我的设想中,后面的按个if用来检验关闭成功了没,就好比如还有一个if来判断打开成功了没,

但这里不太一样啊,这里直接把关闭文件的函数给放在了if条件里,被执行后也就是进行了一次关闭文件操作,和上面的一个关闭操作冲突了,因为标准IO的不能重复关闭


针对上面的错误情况,这里给出正确的情况:

if (EOF == fclose(Fd_1))  
{
    perror("fclose file failed:");
}

为了检验一下目前得到结论的正确性,我专门试了一下:

#include <stdio.h>

int main()
{
    if (printf("高冷美丽善良小组长今天很开心:\t%d\n"))
    {
        printf("啊对对对,丁某觉得很对!\n");
    }

    return 0;
        
}

果然,在这里的判断条件确实被运行了一次。而且不仅仅如此,我们发现,这里的的printf函数也被认定成了true,这种情况我们在后续的也会遇到,比如if里面除了0之外的所有整数,即使是负数,他此时代表的也是true。

6、if 条件语句的实例

(1)、闰年

输入一个年份,判断他是否为闰年;如果不是则输出平年;

资料:能被4整除且不能被100整除的为闰年,世纪年能被400整除的是闰年

#include <stdio.h>
int main(void)
{
  int a;
  printf("请输入年份:\n");
  scanf("%d",&a);

  if (a % 4 == 0)
  {
    if(a % 100 != 0)
    {
      printf("该年份为闰年!");
    }
    else if(a % 400 == 0)
    {
      printf("该年份为闰年!");
    }
    else
    {
      printf("该年份为平年!");
    }
  }
  else
  {
    printf("该年份是平年!");
  }
  return 0;
}

输入年月日,判断闰年平年后判断这是一年中的第几天

# include <stdio.h>
int main(void)
{
    int a , b , c;
    int d = 0;

    printf("请输入年, 月, 日 :\n");
    scanf("%d %d %d" , &a , &b , &c);

    if(a % 100 != 0 && a % 4 == 0 || a % 400 == 0)
    {
      printf("该年份为闰年!\n");
      d = 1;
    }
    else
    {
      printf("该年份是平年!\n");
    }
    
    if(b == 1)
    {  
    }
    else if(b == 2)
    {
        c = 31 + c;
    }
    else if(b == 3)
    {
        c = 31 + 28 + c;
    }
    else if(b == 4)
    {
        c = 31 + 28 + 31 + c;
    }
    else if(b == 5)
    {
        c = 31 + 28 + 31 + 30 + c;
    }
    else if(b == 6)
    {
        c = 31 + 28 + 31 + 30 + 31 + c;
    }
    else if(b == 7)
    {
        c = 31 + 28 + 31 + 30 + 31 + 30 + c;
    }
    else if(b == 8)
    {
        c = 31 + 28 + 31 + 30 + 31 + 30 + 31 + c;
    }
    else if(b == 9)
    {
        c = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + c;
    }
    else if(b == 10)
    {
        c = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + c;
    }
    else if(b == 11)
    {
        c = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + c;
    }
    else if(b == 12)
    {
        c = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + c;
    }

    if(d = 1)
    {
      c = c + 1;
    }
    
    printf("这一天是 %d 的第 %d 天!\n" , a , c);

    return 0;

}

(2)、三个数的最大值

输入3个整数,输出里面的最大值

// Input 3 integers 
// Output the maximum value inside

#include <stdio.h>
int main(void)
{
  int a , b , c , max ;
  printf("Input 3 integers:\n");
  scanf("%d %d %d", &a , &b , &c);

if (a > b)
{
  max = a;
}
else
{
  max = b;
}
if (max > c)
{
  max = max;
}
else
{
  max = c;
}
printf("Maximum values is : %d" , max);
  return 0;
}

其实这个例子不用 if,直接用三目运算符更简单:

// Input 3 integers 
// Output the maximum value inside

#include <stdio.h>
int main(void)
{
  int a , b , c , max ;
  printf("Input 3 integers:\n");
  scanf("%d %d %d", &a , &b , &c);

max = a > b ? a : b ;

max = max > c ? max : c ;

printf("Maximum values is : %d" , max);
  return 0;
}

且当数的个数更多的时候,用三目运算符肯定比直接if好,当然,太多了肯定就需要用循环来解决

// Input 10 integers 
// Output the maximum value inside

#include <stdio.h>
int main(void)
{
  int a , b , c , d , e , f , g , h , i , j , max ;
  printf("Input 10 integers:\n");
  scanf("%d %d %d %d %d %d %d %d %d %d", &a , &b , &c, &d , &e , &f , &g , &h , &i , &j);

  max = a > b ? a : b ;

  max = max > c ? max : c ;

  max = max > d ? max : d ;

  max = max > e ? max : e ;

  max = max > f ? max : f ;

  max = max > g ? max : g ;

  max = max > h ? max : h ;

  max = max > i ? max : i ;

  max = max > j ? max : j ;


printf("Maximum values is : %d" , max);

  return 0;
}

输入三个变量的值,使用if判断,交换变量中的值,把这三个数据从小到大进行排序,输出

#include <stdio.h>
int main(void)
{
    // 笨方法
    // 但引入中间量后又无法处置第二大的数

    int a , b , c;
    printf("请输入a、b、c的值:\n");
    scanf("%d %d %d", &a , &b , &c);

    if (a >= b && a >= c && b >= c)
    printf("%d <= %d <= %d\n", c , b , a);
    else if(a >= b && a >= c && c >= b)
    {
        c = c + b;
        b = c - b;
        c = c - b;
        printf("%d <= %d <= %d\n", c , b , a);
    }
    else if(b >= a && b >= c && c >= a)
    {
        a = a + b;
        b = a - b;
        a = a - b;

        c = c + b;
        b = c - b;
        c = c - b;

        printf("%d <= %d <= %d\n", c , b , a);
    }
    else if(b >= a && b >= c && a >= c)
    {
        a = a + b;
        b = a - b;
        a = a - b;

        printf("%d <= %d <= %d\n", c , b , a);
    }
    else if(c >= a && c >= b && a >= b)
    {
        a = a + c;
        c = a - c;
        a = a - c;

        c = c + b;
        b = c - b;
        c = c - b;

        printf("%d <= %d <= %d\n", c , b , a);
    }
    else if(c >= a && c >= b && b >= a)
    {
        a = a + c;
        c = a - c;
        a = a - c;

        printf("%d <= %d <= %d\n", c , b , a);
    }
    

    return 0;

}

(3)、判断奇偶并进行简单计算

// 键盘输入两个非零整数A和B,
// 如果A和B都是偶数, 则输出两个数的和,
// 如果A和B都是奇数, 则输出两个数的差,
// 如果A是奇数B是偶数, 则输出两数积,
// 如果A是偶数B是奇数, 则输出A/B结果

# include <stdio.h>

int main(void)
{
    int a = 0;
    int b = 0;

    printf("请分别输入两个非零正数 a 和 b:");
    scanf("%d %d", &a, &b);

    if(a%2 == 0 && b%2 ==0)
    {
      printf("%d\n", a+b);
    }

    if(a%2 != 0 && b%2 !=0)
    {
      printf("%d\n", a-b);
    }

    if(a%2 != 0 && b%2 ==0)
    {
      printf("%d\n", a*b);
    }

    if(a%2 == 0 && b%2 !=0)
    {
      printf("%d\n", a/b);
    }

    return 0;

}

二、switch 多分支条件语句

1、简单的switch

其使用大体上可以参照 if。不添加 break 的话,满足当前的条件也不会去退出,而是会顺序往下看,这一点利用好了的话也算是一种 “累加”

// 可以实现四则运算的一个简易计算器

#include <stdio.h>
int main(void)
{
  int a, b;
  char ch;

  printf("请输入计算的式子:");
  scanf("%d %c %d",&a,&ch,&b);

  switch (ch)
  {
  case '+':
    printf("%d + %d = %d\n", a, b, a + b);
    break;
  
  case '-':
    printf("%d - %d = %d\n", a, b, a - b);
    break;

  case '/':
    printf("%d / %d = %d\n", a, b, a / b);
    break;

    case '*':
    printf("%d * %d = %d\n", a, b, a * b);
    break;

  default:
    printf("暂不支持该种运算,请输入+ - * /\n");
    break;
  }
  return 0;
}

default

在逻辑上,switch语句与多个 if 的语句有点像,都是属于并列关系,所以,那个default组块放在任意一个case块旁边都行

而那个default,跟else if语句中的最后一个else的逻辑也有点相似,都是除了上面被列出情况外的所有情况,也就是“兜底”了。

实际上,即使没有default,编译器也不会报错,因为,在条件都不满足的情况下,C语言自然结束。但这样,你就不知道自己输错了指令了


''

这对单引号,是对于单个字符的
表示你输入的,是char类型的字符


break

break出现在每个case组块的最后,用于执行条件后跳出switch。

我们知道,凡是满足其中一个case,这个switch语句就会结束,而结束的这个操作,就是break做到的,他可以打破一层循环或者镶嵌的束缚,

break为一个控制语句,后面记得加分号,同样的控制语句,还有continue,只不过continue是跳出本次循环,然后继续下次循环


switch与if同样作为条件语句,很多情况下其实是可以转换的,但有些switch的写法,if却不能直接套,就比如switch程序里面带有...

// Outputting scores and assigning grades to them?

#include <stdio.h>
int main(void)
{
  int a;
  printf("score:\n");
  scanf("%d",&a);

  switch (a)
  {
  case 90 ... 100:
    printf("Your scores : A");
    break;
  
  case 80 ... 89:
    printf("Your scores : B");
    break;

  case 70 ... 79:
    printf("Your scores : C");
    break;

  case 60 ... 69:
    printf("Your scores : D");
    break;

  case 0 ... 59:
    printf("Your scores : E");
    break;

  default:
    printf("Input error!");
    break;
  }

  return 0;
}

case 90 ... 100:这句话中的 90 ... 100 代表:90 ,91 , 92 , 93 , 94 , 95 , 96 , 97 , 98 , 99 , 100,即 90 和 100,以及二者中间的数都被包括进去了

2、switch语句的实例

(1)、闰年

// 输入年月日,判断闰年和平年
// 并判断他是一年中的第几天

# include <stdio.h>
int main(void)
{
    int a , b , c;
    int d;

    printf("请输入年,月,日 :\n");
    scanf("%d %d %d" , &a , &b , &c);

    if(a % 100 != 0 && a % 4 == 0 || a % 400 == 0)
    {
      printf("该年份为闰年!\n");
      d = 1;
    }
    else
    {
      printf("该年份是平年!\n");
    }
    
    switch (b)
    {  
    case 12:
        c = c + 30;
    case 11:
        c = c + 31;
    case 10:
        c = c + 30;
    case 9:
        c = c + 31;
    case 8:
        c = c + 31;
    case 7:
        c = c + 30;
    case 6:
        c = c + 31;
    case 5:
        c = c + 30;
    case 4:
        c = c + 31;
    case 3:
        c = c + 28;
    case 2:
        c = c + 31;
    case 1:

    default:
        break;
    }

    if(d = 1)
    {
      c = c + 1;
    }
    printf("这一天是 %d 的第 %d 天!\n" , a , c);

    return 0;

}

三、while循环

1、while

如上图,我们在上文if的部分中遇到了一种情况,若是输入的数字更多,此时对其进行排序,若是采用原来的办法,工作量是巨大的,所以我们打算采用循环的方式来应对,即把重复性的工作扔给了计算机,对于计算机来说,这种重复工作却是他最擅长的工作,效率很高。

while后面的括号中跟的是一个表达式,即一个表示判断,准确来说跟if哪里一样指的是0或者1,表达式为真则继续循环,直至为假

而要写出一个循环,则需要遵守他的三要素:

  • 循环变量的初值,
  • 循环退出条件,
  • 循环控制变量的递增递减

// Printable multiplication table.

#include <stdio.h>
int main(void)
{
  int i = 1;
  while (i <= 9)
  {
    int j = 1;
    while (j <= i)
    {
      printf("%d * %d = %d\t", i , j , i * j);
      j++;
    }
    printf("\n");
    i++;
  }
 
  return 0;

}

2、do while循环

// Guessing game: 
// Enter an integer, 
// end when you get the answer right, 
// or keep guessing.
//
// The answer is 8

#include <stdio.h>
int main(void)
{
  int i = 0;
  printf("Enter an integer:\n");
  do
  {
    scanf("%d", &i);

    if (i == 8)
    {
      printf("True!\n");
      break;
    }
    else
    {
      printf("False!\tEnter again:\n");
    }
  
  } while (i != 8);
// while 中的i == 8只是决定你能不能进入下一次循环,
// 即执行1次后可否能够继续第二次,在本题中也就是第一次就猜对的情况,
// 若是没猜对,也就是while的判断为真,就继续下次循环
  return 0;

}

容易想得到,这个实例的逻辑还适用于 “无限制次数密码登陆”

3、while死循环

这种循环常被用来计数,在单片机中运用的比较多。

// ms级延时
void delay_ms(uint32_t n)
{
	while(n--)
	{
		SysTick->CTRL = 0;
		SysTick->LOAD = 168000-1; // 1ms的计数值
		SysTick->VAL = 0;
		SysTick->CTRL = 5;
		while((SysTick->CTRL & 0x10000) == 0);
	}
	
	SysTick->CTRL = 0;
}

我们运用这种死循环的时候跟if括号中的有一定的相似之处,即只存在真与假之分,但这里我们一般用的是数字,而不是条件,对于数字来说,只有0为假,其他数字包括负数都为真。

四、for循环

相比于while循环,for循环更加直接,因为他在括号里直接把三要素给列出来了,中间用分号隔开,在大括号里,就是需要执行的操作了。

1、for循环的使用

for循环的一般格式:

for (初始化表达式; 循环条件; 更新表达式) 
{
    // 循环体代码块
}

在这个格式中,每个部分的含义如下:

  1. 初始化表达式:用于初始化循环变量,一般是赋初值给循环变量。
  2. 循环条件:指定循环执行的条件,只有当条件为真时,循环体会被执行。如果条件为假,则循环终止。
  3. 更新表达式:用于更新循环变量的值,一般是对循环变量进行增量或减量操作。

下面是一个示例,展示了一个计算1到5之间整数和的for循环:

#include <stdio.h>

int main() {
    int sum = 0;
    int i;

    for (i = 1; i <= 5; i++) { // 初始化表达式为i=1,循环条件为i<=5,更新表达式为i的自增操作
        sum += i; // 循环体代码块,将i累加到sum中
    }

    printf("Sum: %d\n", sum); // 输出结果为Sum: 15

    return 0;
}

在这个示例中,初始化表达式将变量i设置为1,循环条件判断i是否小于等于5,如果满足条件,则执行循环体代码块,并将i累加到sum中。在每次循环结束后,更新表达式将i自增1。当i的值达到6时,循环条件为假,循环终止。最后,使用printf函数打印出sum的值,即1到5之间整数的和。

2、for循环死循环

对于for循环而言,也存在着死循环的情况。死循环发生的条件是控制变量的更新永远不能使循环条件为假,从而使循环条件永远为真。

例如:

  • for(a=1;a>0;):在这种情况下,控制变量a不会被更新,因此a永远大于0,循环条件永远为真。
  • for(a=0; ;a++):在这种情况下,没有循环条件,也就意味着没有退出条件,循环会一直进行下去。
  • for( ; ; ):在这种情况下,三个条件式默认都为真,从而形成死循环。

由此也可以看出,for循环的3条语句不是必填的,但不填有不填的后果:

  • 初始化不填:控制变量的初始值只能在循环之前赋值了
  • 循环条件不填:没有循环条件默认为真,就是死循环
  • 循环更新语句不填:控制变量不更新,循环条件的结果永远不变,也会死循环

另外,要打破死循环的话,用break

3、省略大括号的情况

是的,对于if、while和for等控制流语句,当大括号中的内容只有1行时,可以省略大括号。

不过,这样做可能会导致代码可读性降低,增加出错的可能性。建议在编写代码时保持良好的编码风格,始终使用大括号来明确表示代码块的范围,即使代码块只有1行。这样可以提高代码的可读性和可维护性,并减少潜在的错误。以下是一个示例:

if (condition)
    statement;  // 只有1行,可以省略大括号,但不推荐

while (condition)
    statement;  // 只有1行,可以省略大括号,但不推荐

for (i = 0; i < n; i++)
    statement;  // 只有1行,可以省略大括号,但不推荐

更好的做法是始终使用大括号,即使代码块只有1行:

if (condition) {
    statement;
}

while (condition) {
    statement;
}

for (i = 0; i < n; i++) {
    statement;
}

这种方式可以提高代码的可读性,使代码结构更清晰,减少潜在的错误。同时,如果需要在代码块中添加更多语句,也可以避免忘记添加大括号而引发错误。


话虽然是这样说,但实际中我们省略这个大括号的情况非常常见,而且我个人甚至觉得,省略了这个大括号后,某种程度上代码的可读性其实增强了。

五、goto循环

1、介绍

可以理解为在一个在一条直线上无限运行的飞雷神。就是说,在当前位置做一个标记1,在程序当前位置之前的某个位置做一个标记2,然后goto(飞雷神),从当前的位置(标记1)回到标记2,然后从标记2运行,然后运行到刚刚的位置(标记1),飞雷神again……这两个标记一直存在,所以从标记1-> <-标记2的过程一直持续

goto 是一个在编程中使用的关键字,用于无条件地跳转到代码中的标记位置。它可以让程序直接跳转到代码的指定位置,绕过正常的程序流程。


goto的格式:

label:

需要重复执行的部分

goto label;

label是标记名,叫啥都行,第一个label后面跟的是一个冒号。


#include <stdio.h>

int main() {
    int i = 1;

    start:  // 定义一个标记 "start"
    printf("i: %d\t", i);
    if (i <= 5) 
    {
        printf("%d \n", i);
        i++;
        goto start;  // 无条件跳转到 "start" 标记位置
    }
    printf("\n");

    return 0;
}

2、goto用于页面的返回

我们在做项目的时候,很多时候会遇到一种情况:不断点击弹出窗口的某个选项,最后却想回到最初的位置,要想做到这一点,我们往往会通过整体的设计,让这一系列操作在一个大的循环里面,大循环套中循环,中循环套小循环,多次break来返回指定的窗口页面。

这种情况并不少见,虽然goto很危险,但用在这种情况下是再好不过了的。

int main(int argc, char const *argv[])
{
    int x, y;
    Init_Dev(); // 初始化_1
    Init_Font();		//初始化字库

    // 选择"load/specification"
    int flag_lo_sp; // 此参数用于从specification(说明书)内容页面切换到 "load/specification"页面
    dingwei_1:  // 定义goto的标签1
    flag_lo_sp = 0;

    // "load/specification"页面
    SHOW_BMP_zhengchang("face_2.bmp");
	char *str = "登录";
	Display_characterX(50,120,str,0x00ff0000,7); //显示字符串,红色字体
	char *arr = "说明书";
    char *arr_1 = "左上:删除当前图片";
    char *arr_2 = "右上:退出程序";
    char *arr_3 = "左下:缩小图片";
    char *arr_4 = "右下:放大图片";
    Display_characterX(415,120,arr,0x0000ff00,7); // 显示字符串,绿色文字

    
    Get_Xy(&x, &y); // 触摸屏事件
    if (x >= 0 && x <= 400 && y>=0 && y<=480)
    {
        FUN_LOAD();
        
    }
    else if (x > 400 && x <= 800 && y>=0 && y<=480)
    {
        flag_lo_sp = 1;
        SHOW_BMP_zhengchang("face_2.bmp");
        Display_characterX(240,120,arr_1,0x000000ff,3);
        Display_characterX(240,180,arr_2,0x000000ff,3);
        Display_characterX(240,240,arr_3,0x000000ff,3);
        Display_characterX(240,300,arr_4,0x000000ff,3);
    }

    // 如果处于说明书内容页面,点击屏幕任意位置会返回到"load/specification"页面
    if (1 == flag_lo_sp)
    {
        Get_Xy(&x, &y); // 触摸屏事件
        if (x >= 0 && x <= 800 && y>=0 && y<=480)
            goto dingwei_1;


    }

……(其他部分的代码)
……
……
}

这是第一阶段大项目里主函数的一部分,下面给一下相关的运行效果:

3、破开goto的方法

首先还是要声明:goto 语句本身并不是死循环,它是一种控制流语句,用于无条件地跳转到指定的标记位置。虽然在某些情况下 goto 可能被用于实现循环或跳出循环的效果,但这并不意味着 goto 本质上是死循环。

还要注意,goto 语句的使用应该谨慎,并尽量避免滥用。使用结构化的控制流语句来组织代码是更好的选择,而 if 语句和 break 语句是常见的控制流工具,可根据实际需求进行使用。


我们通常采用的办法,是利用 “if + 标志位” 的办法。

这种破开条件,一般需要提前设出一个标志位(变量),一般用flag,然后就在关键的语句后面对flag进行变化,最后设出if,当flag满足自己if里面给出的条件的时候就破开,理解和使用起来都不算麻烦,

上面的实例_1就是用的这种方法


#include <stdio.h>

int main()
{
    int i = 0;
    int flag = 0; // 标志位初始化为 0

start:
    i++;

    if (1 == flag)
    {
        goto end; // 当标志位为 1 时,跳转到 "end" 标记的位置
    }
    
    if (i < 5)
    {
        printf("%d ", i);

        if (3 == i)
        {
            flag = 1; // 当 i 等于 3 时,将标志位设为 1
        }

        goto start; // 继续循环
        
    }

end:
    printf("\n");
    printf("Loop ended.\n");
    printf("i: %d\n", i);
    printf("flag: %d\n", flag);

    return 0;
}


不能使用 break 语句来直接破解 goto 语句的循环。break 语句用于终止循环结构(例如 for、while 或 do-while 循环),而不是跳出 goto 标记。

goto 语句是无条件地跳转到指定标记位置的控制流语句,而 break 语句用于在循环结构中提前退出循环。它们具有不同的作用和语义。

如果你使用 goto 语句创建了一个带有标记的循环,并且想要在循环内部的某个地方终止循环,你应该使用 goto 语句跳转到循环外部的标记位置,而不是尝试使用 break 语句。

六、综合实例

1、计算交错数列

// 编程计算交错序列 1-2/3+3/5-4/7+5/9-6/11...的前N项之和;
// (哥们,看题目的时候注意优先级)
// 要求结果保留3位小数

# include <stdio.h>

int main(void)
{
    float sum = 0;
    int n = 0, i = 0;
    float j = 0;

    printf("What is the number of terms you want the sum of?\n");
    scanf("%d", &n);

    for(int i = 1 ; i <= n ; i++)
    {
        if((i + 2) % 2 != 0) // if-else 实现正负 1 的交迭
        {
            j = 1 / (i-0.5);
            j =j * 0.25;
            j = j + 0.5;
            // 化简部分,
            // 最后化简的结果不要是分子分母同时含有 i 的,不然列出来会报错
            // 其实这里1/(i-0.5)*0.25+0.25化简一下实际上就等于i/(2i-1)
            sum = sum + j; // 呼应 if 小括号里的内容
        }
        else
        {
            
            j = 1 / (i-0.5);
            j =j * 0.25;
            j = j + 0.5;
            
            sum = sum - j;
        }

    }

    printf("The sum of the first %d terms is %.3f\n", n, sum);
    // 需要注意的一点就是这个打印格式的选择了,
    // 那个点十分容易忽略

    return 0;

}

2、四选三并进行互不相同且无重复的排列

// 有1、2、3、4个数字,
// 能组成多少个互不相同且无重复数字的三位数?
// 都是多少? 

# include <stdio.h>

int main(void)
{
    int i, j, k; // 个位,十位,百位

    for(int i = 1 ; i <= 4 ; i++)
    {
        // 每个位数上都有 4 种可能,但是其中存在重复的情况
        for(int j = 1 ; j <= 4 ; j++)
        {
            for(int k = 1 ; k <= 4 ; k++)
            {
                if(i != j && j != k && i != k) // 去重复
                {
                    printf("%d%d%d\t", i, j, k);
                }
            }
        }
        printf("\n");
    }

    return 0;

}

3、水仙花数

// 求三位数的水仙花数
// 个位数的立方 加上 十位数立方 加上 百位数立方 等于原本的这个数 就叫水仙花数

# include <stdio.h>

int main(void)
{
    int i = 100;
    int j, k, l;

    // 个十百三个值不能定义在外面,不然就变成了一个确定的值了

    for(i = 100; i <= 999; i++)
    {
        j = i % 10; // 个位
        k = i % 100 / 10; // 十位
        l = i / 100; // 百位

        if (i == (j*j*j + k*k*k + l*l*l)) // 不能用 ^, 不然识别不出来
        {
            printf("%d\n", i);
        }
       
    }


    return 0;

}

4、由 * 组成的图形

(1)、等腰三角形

// 打印出由 * 组成的三角形,如下:
//     *
//    ***
//   *****
//  *******
// *********

# include <stdio.h>

int main(void)
{
    int i = 1;
    int num, j, k, l;

    printf("num: ");
    scanf("%d", &num);

    for(; i <= num; i++) // 行数列,每一行空格与*共存,可以分别(分先后)写出
    {
        for(j = 1; j <= num-i; j++) // 只需要左半部分的空格就可以定位了
        {
            // (num-i) 是左半部分空格的每一行的数目
            printf(" ");
        }

        for(l = 1; l <= 2*i-1; l++)
        {
            // (2*num-1) 是每一行的 * 数量
            printf("*");
        }

        printf("\n"); // 区分开每一行之间的内容
    }

    return 0;

}

(2)、直角三角形


形状 1:

// 打印出由 * 组成的直角三角形

# include <stdio.h>

int main(void)
{
    int i, j; // 用于循环
    int num; // 层数

    printf("num: ");
    scanf("%d", &num);

    for(i = 1; i <= num; i++) // 行数
    {
        for (j = 1; j <= 2*i-1; j++) // 画*图案
        {
            // 2*i-1 为每一行的 * 数目
            printf("*");
        }

        printf("\n"); // 必须加在两个for之间,目的是断开每一行的*
        
    }

    return 0;

}


形状 2:

// 由等腰三角形的经验,形状2需要先打空格来定位

# include <stdio.h>

int main(void)
{
    int i, j, k; // 用于循环
    int num; // 层数

    printf("num: ");
    scanf("%d", &num);

    for(i = 1; i <= num; i++) // 行数
    {
        for (k = 1; k <= 2*(num-i); k++) //画空格
        {
            // 2*(num-i) 为每一行空格的数目
            printf(" ");
        }
        
        for (j = 1; j <= 2*i-1; j++) // 画*图案
        {
            // 2*i-1 为每一行的 * 数目
            printf("*");
        }

        printf("\n"); // 区分开每一行之间的内容
        
    }

    return 0;

}


形状 3:

// 形状3只需要更换形状1中空格与*模块的要求
// 然后补上*模块即可

# include <stdio.h>

int main(void)
{
    int i, j, k; // 用于循环
    int num; // 层数

    printf("num: ");
    scanf("%d", &num);

    for(i = 1; i <= num; i++) // 行数
    {
        for (j = 1; j <= 2*i-2; j++) // 画空格
        {
            // 2*i-2 为每一行的空格数目
            printf(" ");
        }

        for (k = 1; k <= 2*(num-i)-1  ; k++)
        {
            // 2*(num-i)-1 为每一行的*数目
            printf("*");
        }
        
        printf("\n"); // 区分开每一行之间的内容
        
    }

    return 0;

}


形状 4:

# include <stdio.h>

int main(void)
{
    int i, j; // 用于循环
    int num; // 层数

    printf("num: ");
    scanf("%d", &num);

    for(i = 1; i <= num; i++) // 行数
    {
        

        for (j = 1; j <= 2*(num-i)-1  ; j++)
        {
            // 2*(num-i)-1 为每一行的*数目
            printf("*");
        }
        
        printf("\n"); // 区分开每一行之间的内容
        
    }

    return 0;

}

(3)、菱形

# include <stdio.h>

int main(void)
{
    int i, j, k, l;
    int num;

    printf("num: ");
    scanf("%d", &num);



    for (i = 1; i <= num/2+1; i++) // 上半部分的行数
    {
        for (j = 1; j <= num/2+1-i; j++) // 上半部分的左小部分空格,先写来定位
        {
            // num/2+1-i 为每行空格行数
            printf(" ");
        }
        
        for (k = 1; k <= 2*i-1; k++) // 上半部分的*部分
        {
            // 2*i-1 为每行*内容
            printf("*");
        }
        printf("\n");
    }

    j = k = 0;

    for (l = 1; l <= num/2; l++) // 下半部分的行数
    {
        for (j = 1; j <= l; j++) // 下半部分空格区
        {
            // l 为下半部分左边的每行空格个数
            printf(" ");
        }

        for (k = 1; k <= (num/2-l+1)*2-1; k++) // 下半部分*区
        {
            // (num/2-l+1)*2-1 为下半部分每行*的个数
            printf("*");
        }
        
        printf("\n"); // 区分开每一行之间的内容
    }
    
    

    return 0;

}

(4)、变种字母类

//    A
//   ABA
//  ABCBA
// ABCDCBA

// 去找了下参考代码,还没看完,大致思路就清楚了
// 原来继续套娃即可,多分一个模块的事情,
// 原本我不想往那个方面想的,因为模块堆积繁杂而无聊
// 但是单独写出一行行回文字符的难度太大,实现有点困难
// 也只好继续写模块了

# include <stdio.h>

int main(void)
{
    int i, num, j, k, l;
    char words[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    printf("Number of layers to output (less than or equal to 26):");
    scanf("%d", &num);

    for (i = 1; i <= num; i++) // 行(层)数
    {
        for (j = 1; j <= num-i; j++) // 左边的定位空格
        {
            printf(" ");
        }

        for (k = 0; k < i; k++) // 左边占了一大半的顺序字符
        {
            printf("%c", words[k]);
        }

        for (l = i-1; l >= 0; l--) // 右边一小半的逆序字符
        {
            printf("%c", words[l-1]); 
            // 这里要注意到右边的小一些,所以要 l-1,
            // 是 l 就一样大了
        }
        
        printf("\n"); // 分行
        
    }
    
    return 0;

}

写完这个,那么写之前见到的异形排列的乘法表也有思路了,但也没必要去写了

七、for 和 while 的区别

1、while中的递增递减是可以中途停下的

while循环中放一个if-else,把递增递减语句放在else里面,然后用if来控制递增递减停止和停止后干什么,if满足后会再次循环一遍才继续开始递增递减。

这种情况在普通情况下不算啥事,因为i可以随时减,只需要在大括号中加一个条件if触发即可,但是在单向链表中就很要命,因为此时只能单向操作,这时候就必须用到while而不是for了

/*for (POS->next; POS->next != NULL; POS=POS->next)
        {
            printf("插眼2:\n");
            printf("此时的TMP->next:%d\n", TMP->next->date);
            printf("此时的POS->next:%d\n", POS->next->date);
            if (TMP->next->date == POS->next->date)
            {
                flag = 1;
                A = POS->next; // 用A来保存要删数据的地址,地址是不会变的,等会直接把A给free掉就好了,这样就不会混淆指向了
                POS->next= A->next;
                A = NULL;
                free(A);
                printf("插眼3:\n");
                //POS->next = POS;
                
            }
        此处用for循环有些问题,因为for里面完成一次后TMP必然往后面遍历,
        但这里需要控制他停下来再次比较一次,这里做不到,
        故而会导致程序出问题
        }*/
        
        while (POS->next != NULL)
        {
            
            if (TMP->next->date == POS->next->date)
            {
                flag = 1;
                A = POS->next; // 用A来保存要删数据的地址,地址是不会变得,等会直接把A给free掉就好了,这样就不会混淆指向了
                POS->next= A->next;
                A = NULL;
                free(A);               
            }
            else
            {
                POS=POS->next;
            }
        }

2、while(n--)计数的坑

while(n--)这个被用的挺广泛的,但是我现阶段还没咋用,于是现在才发现其中的坑:

#include "head.h"

int main()
{
  int n;
  printf("请输入你要输出的个数:\t");
  scanf("%d", &n);

  while (n--)
  {
    printf("n:\t%d\n", n);
  }

  return 0;

}

看得出来,while(n--)循环体中执行的第一个是从n-1开始的;

再看这个斐波拉切数列:

#include "head.h"

int FUN(int n)
{
  if (n == 1 || n == 2)
  {
    return 1;
  }
  else
  {
    return FUN(n-1)+FUN(n-2);
  }

}

int main()
{
  int n, b;
  printf("请输入你要输出的个数:\t");
  scanf("%d", &n);

  // for (int i = n; i>0; i--)
  // {
  //  b = FUN(i);
  //  printf("%d\n", b);
  // }

  while (n--) // 之前还没有注意到这种while(n--)第一次开始执行的是n-1的,所以在循环中要改变一下n
  {
    b = FUN(n+1);
    printf("%d\n", b);
  }

  return 0;

}

由于上面介绍的这个坑,
这里的b = FUN(n);被迫只能写成b = FUN(n+1);

posted @ 2023-07-22 21:51  fhzy  阅读(219)  评论(0)    收藏  举报