C learning_7

简介: C learning_7

1.for循环


我们已经知道了while循环,这次我们就来介绍for循环呢?


for循环是一种常见的循环结构,它可以重复执行一段代码多次,直到特定的条件满足为止。

它的语法如下:

for (初始化语句; 循环条件; 循环后操作)

{    

       //执行的代码块

}

其中,初始化语句用于初始化循环控制变量,循环条件是一个布尔表达式,如果为true,则继续执行循环,否则退出循环,循环后操作是在每次循环结束时执行的语句。


for循环的执行过程如下:


1. 执行初始化语句。

2. 检查循环条件,如果为false,则退出循环,执行后续语句。

3. 执行代码块。

4. 执行循环后操作。

5. 回到第2步,继续执行循环,直到循环条件为false。


for循环广泛应用于迭代数组、列表、集合等数据结构,以及重复执行固定次数的任务。

#define _CRT_SECURE_NO_WARNINGS 1
//for循环使用
#include<stdio.h>
int main()
{
  int i = 0;
  //打印一次i的值
  i++;
  printf("%d\n", i);
  //当我们要多次打印时,这时就需要用到循环
  //先用我们上次介绍的while
  i = 0;//1.初始化
  while (i < 10)//2.判断
  {
    i++;//3.调整
    printf("%d ", i);
  }
  //我们发现while循环中由三部分
  //1.初始化 2.判断 3.调整
  //而这是组成循环的必要条件
  //接下来我们看看for语句如何完成上面的while语句
  i = 0;
  for (i = 1; i <= 10; i++)
  //   初始化  判断   调整
  {
    printf("%d ", i);
  }
  //我们发现for也有1.初始化 2.判断 3.调整
  //所以for语句也具有while语句一样的循环功能
  return 0;
}

既然我们有了while语句可以实现循环,为什么还要推出一个功能相同的for语句呢?


1.虽然while循环和for循环本质上都可以实现循环,但是它们在使用方法和场合上还是有一些区别的。


      for循环主要用于对一定次数的重复操作,特别适用于遍历数组或集合,将数组或集合中的每一个元素作为一个循环变量使用。同时,for循环的代码结构非常紧凑,for()的括号中包含了循环变量的初始化、循环条件的判断和循环变量的更改,可以在一行代码中完成循环的控制,适用于代码简洁的场景。

       而while循环则主要适用于需要根据一定的条件来重复执行操作的场景,比如用户输入信息的验证和检查等。while循环的代码结构相对比较灵活,可以根据不同的需求设置不同的循环条件,灵活控制循环的执行。


2.while循环中存在循环的三个必须条件,但是由于风格的问题使得三个部分很可能偏离较远,这样 查找修改就不够集中和方便。所以,for循环的风格更胜一筹;for循环使用的频率也最高。


int i = 0; //实现相同的功能,使用while

i=1;//初始化部分

while(i<=10)//判断部分

{

       printf("hehe\n");

       .

       .(省略多行代码)

       .

       i = i+1;//调整部分

}

//实现相同的功能,使用for

for(i=1; i<=10; i++)

{

       printf("hehe\n");

}


2.break和continue在for循环中


//代码1

#include <stdio.h>

int main()

{

   int i = 0;

   for (i = 1; i <= 10; i++)

   {

       if (i == 5)

           break;//跳出循环

       printf("%d ", i);//1 2 3 4

   }

   return 0;

}


//代码2

#include <stdio.h>

int main()

{

   int i = 0;

   for (i = 1; i <= 10; i++)

   {

       if (i == 5)

           continue;//跳出本次循环

       printf("%d ", i);//1 2 3 4 6 7 8 9 10

   }

   return 0;

}


在for循环里,我们要注意两个问题:


1. 不可在for 循环体内修改循环变量,防止 for 循环失去控制。

#include <stdio.h>
int main()
{
  int i = 0;
  for (i = 1; i <= 10; i++)
  {
    if (i = 5)
    //if(i=5)将5赋给i,使得if语句括号的内容恒为真,造成死循环
        //当i为1,进入循环,此时又将i=5,由于为真,执行continue,不执行打印,
          随后i++,i=6,进入循环
      continue;
    printf("%d ", i);
  }
  return 0;
}


2. 建议for语句的循环控制变量的取值采用“前闭后开区间”写法。

#include<stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int i = 0;
  for (i = 0; i < 10; i++)
  //前闭后开容易观察到循环的次数 - 10次
  {
    printf("%d ", arr[i]);
  }
  return 0;
}


接下来,我们来看四个for相关的代码,看看为什么


代码1


#include <stdio.h>

int main()

{

   //代码1

   for (;;)

   {

       printf("hehe\n");

   }

   return 0;

}


 这个程序有一个无限循环,它会不停地输出"hehe",直到程序被手动停止或者出现了某些异常情况。其中的for(;;)这一行是一个无限循环的语法结构,相当于while(true)或者while(1),表示循环条件永远为真,因此永远不会退出循环。如果运行这个程序,它会一直输出"hehe",直到你手动停止它。所以在for语句中,我们一定不要省略判断条件,否则就会导致程序进入死循环。


代码2


#include<stdio.h>

int main()

{

   int i = 0;

   int j = 0;

   //这里打印多少个hehe?

for (i = 0; i < 10; i++)

   {

       for (j = 0; j < 10; j++)

       {

           printf("hehe\n");

       }

   }

   return 0;

}

 

这个程序会打印100次"hehe",因为它有两个嵌套的for循环。外层的循环从i=0开始,每次循环增加1,当i<10时继续执行。内层的循环从j=0开始,每次循环增加1,当j<10时继续执行。因此,内层循环每循环一次,都会输出一次"hehe",外层循环控制内层循环的执行次数,因此内层循环总共会执行10次,输出10次"hehe",而外层循环也会循环10次,因此总共会输出100个"hehe"。


代码3


#include<stdio.h>

int main()

{

   int i = 0;

   int j = 0;

//如果省略掉初始化部分,这里打印多少个hehe?

   for (; i < 10; i++)

   {

       for (; j < 10; j++)

       {

           printf("hehe\n");

       }

   }

   return 0;

}


这个程序会打印10次"hehe",当第一次循环时,i = 0,内循环开始第一次执行进入内循环中,j的值从0到9共10次,会打印10次"hehe",当j<10不满足时,跳到外循环,然后执行i++;i=1,进入内循环,但是此时内循环的j的值已经是10了,j的值没有重新赋值为零,所以j<10不满足,就不打印"hehe",之后随着进行i<10和i++,程序j<10还是不满足,始终不能打印"hehe"。


代码4


#include<stdio.h>

int main()

{

   int x, y;

   //使用多余一个变量控制循环

   for (x = 0, y = 0; x < 2 && y < 5; ++x, y++)

   {

       printf("hehe\n");

   }

   return 0;

}


在这个程序中,使用了两个变量`x`和`y`来控制循环。其中,变量`x`初始化为0,变量`y`也初始化为0,它们分别作为两个测试条件。在每次循环开始前,表达式`++x`和`y++`会分别对它们进行自增1的操作。 循环的循环条件是`x < 2 && y < 5`,也就是当`x`小于2且`y`小于5时,循环继续执行。在每次循环体中,都会输出一次字符串"hehe"。当`x`自增到2时,循环条件`x<2`不再成立,循环退出,本程序就结束打印"hehe"了。


接下来我们就练习一下,看看你有没有掌握


//请问循环要循环多少次?

#include <stdio.h>

int main()

{

   int i = 0;

   int k = 0;

   for (i = 0, k = 0; k = 0; i++, k++)

       k++;

   return 0;

}


在这个程序中,循环的循环条件是`k = 0`,每次循环都会将变量`k`赋值为0并判断赋值结果的真假,因为0被认为是“假”,所以循环条件的结果永远为假,循环不会进入。因此,这个程序不会循环一次,直接返回0。上面的k=0,就是那个循环条件就变为0了,0为假,程序的循环条件i就不会满足。下面举个例子看看这种写法的值


可以看出b=7结果就是7,所以k=0结果就是0。


3.左值和右值


在计算机编程中,每一个变量都有一个存储它的内存位置,我们称之为左值(L-value)。左值可以出现在赋值语句的左边或右边。当左值出现在赋值语句的左边时,我们可以把它看做一个存储值的容器,我们可以给它赋一个新的值,或者修改它的值。

在赋值语句中,右边的值叫做右值(R-value),它是一个可以被赋给左值的值。右值可以是常量、变量、表达式或函数调用的返回值。当右值出现在赋值语句的左边时,它是无效的。

例如,如果我们定义一个变量`a`,那么`a`就是其内存位置的左值。当我们对`a`进行赋值操作时,`a=10`这个赋值语句中的`10`就是右值,它被赋给了变量`a`,`a`的值变成了`10`。

总之,左值是一个标识符,它表示一个存储值的对象或容器,而右值是一个可以被赋给左值的值。

#include<stdio.h>
int main()
{
  int a = 10;
  // a - 左值
  a = 20;//其实是把20放到a的这块空间,这里使用的是a的空间
  // a - 右值
  int b = a;//其实是使用了a中存放的值,把a中的20存到b中
  return 0;
}


接下来我们在介绍一个循环


4.do...while()循环


       do...while()循环是一种循环结构,在循环体至少执行一次后才判断循环条件是否为真。它的语法结构如下:


do {

       循环体语句块

} while (循环条件);


do...while()`循环先执行一次循环体语句块,然后判断循环条件是否为真,如果循环条件为真,则继续执行循环体语句块,直到循环条件为假时退出循环。

和`while`循环和`for`循环不同,`do...while()`循环的循环条件在循环体语句块后面,因此无论循环条件是否为真,循环体至少会被执行一次。

//使用do - while()语句打印1-10
#include <stdio.h>
int main()
{
    int i = 1;//初始化
    do
    {
        printf("%d ", i);
        i = i + 1;//调整
    } while (i <= 10);//判断
    return 0;
}


5.do while循环中的break和continue


#include <stdio.h>

int main()

{

   int i = 1;

   do

   {

       if (5 == i)

           break;

       printf("%d ", i);//1 2 3 4

       i = i + 1;

   } while (i <= 10);

   return 0;

}


#include <stdio.h>

int main()

{

   int i = 1;

   do

   {

       if (5 == i)

           continue;

       printf("%d ", i);//1 2 3 4 死循环

       i = i + 1;

   } while (i <= 10);

   return 0;

}


6.小练习锻炼一下


1. 计算 n的阶乘


/*
  1. 计算 n的阶乘。
  5! = 1*2*3*4*5
  n! = 1-n 累积相乘
*/
#include<stdio.h>
int main()
{
  int n = 0;
  scanf("%d", &n);//不考虑溢出
  int i = 0;
  int num = 1;
  //产生1-n的数字然后进行累积相乘
  for (i = 1; i <= n; i++)
  {
    num *= i;
  }
  printf("%d\n", num);
  return 0;
}


该程序实现了计算输入的正整数n的阶乘。 程序从标准输入中读入一个整数n,然后使用for循环计算1到n的乘积,并将结果存储在变量num中。for循环中的变量i从1开始,每次循环将i乘以num,最后num就是1到n所有数字的乘积,即n的阶乘。 最后,程序使用printf函数将计算结果打印到标准输出中,并返回0表示程序正常结束。 需要注意的是,该程序没有对计算结果进行溢出检查和处理,当n比较大时,计算结果可能会超过int类型的取值范围。需要使用高精度计算或其他方法来避免溢出。


2. 计算 1!+2!+3!+……+10!


//写法一
#include<stdio.h>
int main()
{
  int n = 0;
  scanf("%d", &n);
  int i = 0;
  int sum = 0;
  int num = 1;
  for (i = 1; i <= n; i++)
  {
    num *= i;
    sum += num;
  }
  printf("%d\n", sum);
  return 0;
}
//写法二
#include<stdio.h>
int main()
{
  int n = 0;
  int i = 0;
  int sum = 0;
  int num = 1;
  for(n=1;n<=3;n++)
  {
    for (i = 1; i <= n; i++)
    {
      num *= i;
    }
    sum += num;
  }
  printf("%d\n", sum);//15
  //结果不对,1!+2!+3!=9
  //?
  return 0;
}


    该程序的目标是计算1到3的所有数字的阶乘之和。程序使用两个嵌套的for循环实现了该目标。

       内层循环计算一个数字的阶乘并将其累加到sum中,外层循环控制计算1到3的所有数字。然而,在计算阶乘时,程序没有将变量num重置为1,导致计算每个数字的阶乘时都是在上一个数字的阶乘的基础上进行乘法运算,所以程序计算的结果是错误的。

       例如,第一次循环时,计算n=1时的阶乘,num的值为1,计算1的阶乘时sum加上了1。但是在计算n=2时的阶乘时,num的值已经是1的阶乘结果,即num=1,而不是重新开始计算2的阶乘,导致程序计算的结果错误。

       解决该问题的方法是,在内层循环中,每次计算阶乘之前都将num重置为1。

修改后的程序如下:

#include <stdio.h>
int main() {
    int n = 0;
    int i = 0;
    int sum = 0;
    int num = 1;
    for (n = 1; n <= 3; n++) {
        num = 1; // 重置num的值为1
        for (i = 1; i <= n; i++) {
            num *= i;
        }
        sum += num;
    }
    printf("%d\n", sum); // 输出结果:9
    return 0;
}


现在程序输出的结果是正确的,即1的阶乘+2的阶乘+3的阶乘=1+2+6=9。


3. 在一个有序数组中查找具体的某个数字n。(讲解二分查找)


//这种写法是遍历数组寻找数字,虽然能找到,但是浪费了数字有序这个条件
#include<stdio.h>
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//升序的10个数字
  int k = 7;//要查找的数字
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    if (arr[i] == k)
    {
      printf("找到了,下标是:%d\n", i);
      break;
    }
  }
  if (i == 10)
    printf("找不到!\n");
  return 0;
}
//二分查找法
/*
  1.确定数组的范围的前后下标begin和end
  2.根据begin和end,确定中间的元素的下标end
  3.根据mid锁定的元素,和查找的元素比较,确定新的查找范围
  4.循环上面步骤,逐渐缩小begin和end的范围,确定能否找到k
*/
#include<stdio.h>
int main()
{
  int arr[] = { 8, 17, 26, 32, 40, 72, 87, 99 };//升序的8个数字
  int k = 40;//要查找的数字
  int length = sizeof(arr) / sizeof(arr[0]);//确定数组的元素个数
  int begin = 0;
  int end = length - 1;
  int flag = 0;
  while (begin <= end)
  {
    int mid = (begin + end) / 2;
    if (arr[mid] < k)
    {
      begin = mid + 1;
    }
    else if (arr[mid] > k)
    {
      end = mid - 1;
    }
    else
    {
      printf("找到了,下标是:%d\n", mid);
      flag = 1;
      break;
    }
  }
  if(flag == 0)
    printf("找不到!\n");
  return 0;
}


 该程序实现了二分查找法,用于在一个已知的升序数组中查找一个特定的数k。 程序的基本思路是将查找范围不断缩小,直到找到k或者查找范围变为空。 程序首先定义了一个有序的整型数组arr,并确定数组的元素个数length,以及要查找的数字k。然后定义begin和end表示查找的范围,一开始为数组的第一个和最后一个元素的下标。 程序使用while循环进行查找,每次循环将查找范围缩小一半。在每次循环中,程序使用mid表示查找范围中间元素的下标,并将k与arr[mid]进行比较。如果k小于arr[mid],则说明要查找的数字在mid的左边,将end赋值为mid-1;如果k大于arr[mid],则说明要查找的数字在mid的右边,将begin赋值为mid+1;如果k等于arr[mid],则说明找到了k,打印出mid的值,并设置flag为1表示找到了k。 当查找结束时,程序判断flag的值,如果为0,则说明没有找到k,输出“找不到!”。 需要注意的是,程序的正确性要求数组是有序的,并且每次循环都将查找范围缩小一半,所以时间复杂度为O(log n)。


4. 编写代码,演示多个字符从两端移动,向中间汇聚。


/*
  输出格式:
  w##########################!
  we########################!!
  wel######################!!!
  welc####################a!!!
  welco##################ba!!!
  welcom################oba!!!
  welcome##############aoba!!!
  welcome ############iaoba!!!
  welcome t##########xiaoba!!!
  welcome to########uxiaoba!!!
  welcome to ######huxiaoba!!!
  welcome to b####nhuxiaoba!!!
  welcome to be##enhuxiaoba!!!
  welcome to benbenhuxiaoba!!!
  两组数据
  welcome to benbenhuxiaoba!!!
  ############################
  left                   right
*/
#include<stdio.h>
#include<string.h>
#include<windows.h>
int main()
{
  char arr1[] = "welcome to benbenhuxiaoba!!!";
  char arr2[] = "############################";
  int left = 0;
  int right = strlen(arr1) - 1;
  while (left <= right)
  {
    arr2[left] = arr1[left];
    arr2[right] = arr1[right];
    printf("%s\n", arr2);
    Sleep(1000);//休息1000毫秒
    left++;
    right--;
  }
  return 0;
}

 这段程序的功能是:输出一个类似于悬挂字的效果,将一段字符串从两端向中间逐步显示。 程序首先定义了两个字符数组arr1和arr2,分别为原始字符串和显示效果。同时定义了两个索引left和right,分别表示显示效果的左侧和右侧位置。初始时左侧位置为0,右侧位置为原始字符串长度减一。 然后进入while循环,循环条件是左侧位小置于等于右侧位置。在每次循环中,将arr1的左侧和右侧字符分别赋值给arr2的左侧和右侧位置,输出arr2,并使用Sleep函数暂停1000毫秒(即1秒),使效果更明显。然后分别将左侧位置和右侧位置向中间移动一个位置。由于每次循环都会将显示效果向中间显示一位,所以最终输出的结果为原始字符串从两侧向中间逐渐显示的效果。 需要注意的是,Sleep函数是Windows API提供的函数,可以让程序暂停一定的时间,单位是毫秒。在这段程序中,使用Sleep函数可以让效果看起来更加流畅自然。


5. 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则 提示登录成,如果三次均输入错误,则退出程序。


#include<stdio.h>
int main()
{
  char password[20] = { 0 };
  int i = 0;
  do
  {
    printf("请输入密码:>");
    scanf("%s", password);
    //两个字符串比较不能使用等号
    //需要使用库函数strcmp
    //strcmp返回值 = 0,表示字符串1 = 字符串2
    //strcmp返回值 > 0,表示字符串1 > 字符串2
    //strcmp返回值 < 0,表示字符串1 < 字符串2
    if (strcmp(password, "123456") == 0)
    {
      printf("密码正确\n");
      break;
    }
    else
    {
      printf("密码错误\n");
    }
    i++;
  } while (i < 3);
  if (i == 3)
  {
    printf("登录失败\n");
  }
  return 0;
}

 这段程序的功能是:让用户输入密码,如果输入的密码是123456,则认为密码正确,程序结束;否则,提示密码错误并要求重新输入。当用户连续输入密码错误超过3次时,程序将退出。


       程序首先定义了一个长度为20的字符数组password,用于存储用户输入的密码。然后使用do-while循环,循环条件为i<3,即用户最多可以输入3次密码。在每次循环中,程序会提示用户输入密码,并使用scanf函数将输入的密码保存到password数组中。接着使用strcmp函数比较用户输入的密码和正确的密码是否相同,如果相同则输出"密码正确"并跳出循环;否则输出"密码错误"。


       在每次循环结束后,程序会将i加1。当i等于3时,表示用户已经连续输入3次密码错误,此时程序将输出"登录失败"并结束运行。如果用户在前3次内输入了正确的密码,则程序会在循环中使用break语句跳出循环,然后输出"密码正确"。


       需要注意的是,字符串比较时不能使用等号,需要使用库函数strcmp。strcmp函数的返回值为0表示两个字符串相同,返回值大于0表示第一个字符串大于第二个字符串,返回值小于0表示第一个字符串小于第二个字符串。在本程序中,如果strcmp函数的返回值为0,则表示用户输入的密码是正确的,程序将输出"密码正确"并结束运行。


相关文章
|
6月前
|
机器学习/深度学习 传感器 自然语言处理
Deep Learning
【6月更文挑战第7天】
45 2
|
6月前
|
机器学习/深度学习 数据采集 人工智能
Supervised Learning
【6月更文挑战第7天】
54 2
|
7月前
|
存储 编译器 C语言
C learning_2
C learning_2
|
7月前
|
算法 C语言 数据安全/隐私保护
C learning_6
C learning_6
|
7月前
|
编译器 C语言
C learning_1
C learning_1
|
7月前
|
存储 缓存 安全
C learning_4
C learning_4
|
7月前
|
C语言
|
7月前
|
存储 C语言 C++
|
7月前
|
存储 算法 Unix
C learning_8
C learning_8
|
7月前
|
存储 安全 程序员
C learning_5
C learning_5