【有条件执行语句】

if esle 语句

if else 语句根据一个条件确定是否执行一段代码,执行条件是一个布尔值,布尔值为true则执行,为false则不执行,同时可以设置不符合条件时执行的语句。

if(执行条件)
{
    符合条件时执行的代码;
}
else
{
    不符合条件时执行的代码;
}

使用事项:
1.执行条件可以直接使用一个布尔数据,也可以是返回布尔值的运算式、函数,此时会首先执行一遍运算式或函数,之后使用其返回值当做执行条件。
2.若不需要在不符合条件时执行其它语句,可以省略else。
3.if和else内部代码可以有多个语句,若只有一个语句可以省略{}符号。

#include <stdio.h>
int main()
{
    int a, b;
    scanf("%d%d", &a, &b);    //输入变量a、b的值     if(a < b)
    {
        printf("a小于b\n");    //输出判断结果
    }
    else
    {
        printf("a不小于b\n");
    }
    
    return 0;
}

if else 语句对应汇编代码如下:

  401154:    mov    edx,DWORD PTR [rbp-0x4]    ;变量a写入edx
  401157:    mov    eax,DWORD PTR [rbp-0x8]    ;变量b写入eax
  40115a:    cmp    edx,eax                    ;比较a与b的关系
  40115c:    jge    40116a                     ;大于等于则跳转到else语句部分,小于则顺序执行if语句部分   40115e:    mov    edi,0x402009               ;if语句部分,将"a小于b\n"字符串地址写入edi传参
  401163:    call   401030                     ;跳转到输出函数
  401168:    jmp    401174                     ;if语句代码末尾,跳过else语句,执行之后的指令   40116a:    mov    edi,0x402012               ;else语句部分,将"a不小于b\n"字符串地址写入edi传参
  40116f:    call   401030                     ;跳转到输出函数   401174:    mov    eax,0x0                    ;return 0,设置main函数返回值
  401179:    leave                             ;还原栈空间
  40117a:    ret                               ;返回指令

某些情况下,编译器会进行以空间换时间的优化,此时if语句部分无需执行jmp指令跳过else语句部分,相当于转换为如下C代码:

#include <stdio.h>
int main()
{
    int a, b;
    scanf("%d%d", &a, &b);     if(a < b)
    {
        printf("a小于b\n");
        return 0;             //return用于终止函数
    }
    else
    {
        printf("a不小于b\n");
        return 0;
    }
}

嵌套 if else

if else 语句可以嵌套使用,从而进行多重条件判断,嵌套可以在if语句内、也可以在else语句内。

#include <stdio.h>
int main()
{
    int a,b;
    scanf("%d%d", &a, &b);
    
    if(a<b)
    {
        printf("a小于b\n");
    }
    else
    {
        if(a>b)
        {
            printf("a大于b\n");
        }
        else
        {
            printf("a等于b\n");
        }
    }
    
    return 0;
}

上述这种 if else 语句嵌套使用方式,可以使用如下简写形式:

#include <stdio.h>
int main()
{
    int a,b;
    scanf("%d%d", &a, &b);
    
    if(a<b)
    {
        printf("a小于b\n");
    }
    else if(a>b)
    {
        printf("a大于b\n");
    }
    else
    {
        printf("a等于b\n");
    }
    
    return 0;
}

switch case 语句

switch case 语句用于在多组语句中选择一组符合条件的执行,找到符合条件的语句后不再判断其它条件,功能类似多个if语句嵌套并在每个if内使用goto语句跳出嵌套,但是使用 switch case 会让代码更简洁,并且编译器会对 switch case 进行一些优化,执行速度更快。

switch (整数变量)
{
    case 整数常量:
    执行代码段;
    break;     case 整数常量:
    执行代码段;
    break;     case 整数常量:
    执行代码段;
    break;     default:
    执行代码段;
    break;
}

switch关键词之后设置一个变量,之后定义多个case语句,每个case语句设置一个常量,switch设置的变量会与case设置的常量进行比较,与哪个case常量相同就执行哪个case语句内代码段,若没有符合条件的case语句,则执行default语句内代码段,若不需要在不满足条件时执行另一段代码,default也可以省略。

另外case和default语句内代码段以break结尾,break用于跳出switch语句,若没有设置break则会顺序执行此语句之后的代码,这将会导致逻辑混乱。

#include <stdio.h>
int main()
{
    unsigned int a;
    scanf("%u",&a);    //输入a的值
    
    switch(a)
    {
    case 0:
        printf("输入值为0\n");
        break;
    case 1:
        printf("输入值为1\n");
        break;
    case 2:
        printf("输入值为2\n");
        break;
    case 3:
        printf("输入值为3\n");
        break;
    default:
        printf("输入值大于3\n");
        break;
    }
    
    return 0;
}

以上 switch case 语句对应汇编代码如下:

401150:    mov    eax,DWORD PTR [rbp-0x4]    ;变量a写入eax

  401153:    cmp    eax,0x1                    ;a与1比较
  401156:    je     401174                     ;a等于1则跳转到case 1   401158:    test   eax,eax                    ;a与0比较
  40115a:    je     401168                     ;a等于0则跳转到case 0   40115c:    cmp    eax,0x2                    ;a与2比较
  40115f:    je     401180                     ;a等于2则跳转到case 2   401161:    cmp    eax,0x3                    ;a与3比较
  401164:    je     40118c                     ;a等于3则跳转到case 3   401166:    jmp    401198                     ;若没有满足条件的case语句,跳转到default   401168:    mov    edi,0x402007               ;case 0 语句
  40116d:    call   401030                     ;终端输出函数
  401172:    jmp    4011a3                     ;跳转到switch case语句之后的代码,此处为跳转到return 0   401174:    mov    edi,0x402015               ;case 1 语句
  401179:    call   401030
  40117e:    jmp    4011a3   401180:    mov    edi,0x402023               ;case 2 语句
  401185:    call   401030
  40118a:    jmp    4011a3   40118c:    mov    edi,0x402031               ;case 3 语句
  401191:    call   401030
  401196:    jmp    4011a3   401198:    mov    edi,0x40203f               ;default 语句
  40119d:    call   401030   4011a3:    mov    eax,0x0                    ;return 0
  4011a8:    leave  
  4011a9:    ret

switch case 优化

上述汇编代码中,编译器使用多个跳转指令分别跳转到对应的case语句,当case语句过多、同时多个case语句设置的常量之间相差不大时,编译器会进行优化,将所有的case语句地址放在一个数组内(称为case地址表),之后使用switch设置的变量当做数组下标调用case地址表中的元素执行跳转,从而减少跳转指令的使用量,降低CPU分支预测失败率,在clang编译器中使用多个嵌套if语句判断一个变量与哪个常量相同时,也会进行与switch case语句相同的优化。

#include <stdio.h>
int main()
{
unsigned int a;
printf("请输入10以内的单数\n");
scanf("%u",&a); switch(a)
{
case 1:
printf("输入值为1\n");
break;
case 3:
printf("输入值为3\n");
break;
case 5:
printf("输入值为5\n");
break;
case 7:
printf("输入值为7\n");
break;
case 9:
printf("输入值为9\n");
break;
default:
printf("输入值不合规\n");
break;
} return 0;
}

gcc -O0 汇编代码如下:

0000000000401132 <main>:
401132: push rbp
401133: mov rbp,rsp
401136: sub rsp,0x10
40113a: mov edi,0x402008
40113f: call 401030
401144: lea rax,[rbp-0x4]
401148: mov rsi,rax
40114b: mov edi,0x402023
401150: mov eax,0x0
401155: call 401040 ;scanf 40115a: mov eax,DWORD PTR [rbp-0x4] ;switch语句,变量a写入eax
40115d: cmp eax,0x9 ;比较变量a
401160: ja 4011aa ;大于9则跳转到default 401162: mov eax,eax ;指令数据地址对齐
401164: mov rax,QWORD PTR [rax*8+0x402080] ;使用变量a调用地址表中的地址
40116c: jmp rax ;跳转 40116e: mov edi,0x402026 ;case 1
401173: call 401030 ;终端输出函数
401178: jmp 4011b5 ;跳出switch 40117a: mov edi,0x402034 ;case 3
40117f: call 401030
401184: jmp 4011b5 401186: mov edi,0x402042 ;case 5
40118b: call 401030
401190: jmp 4011b5 401192: mov edi,0x402050 ;case 7
401197: call 401030
40119c: jmp 4011b5 40119e: mov edi,0x40205e ;case 9
4011a3: call 401030
4011a8: jmp 4011b5 4011aa: mov edi,0x40206c ;default
4011af: call 401030
4011b4: nop ;地址对齐 4011b5: mov eax,0x0 ;return 0
4011ba: leave
4011bb: ret Contents of section .rodata:
402000 01000200 00000000 e8afb7e8 be93e585
402010 a53130e4 bba5e586 85e79a84 e58d95e6
402020 95b00025 7500e8be 93e585a5 e580bce4
402030 b8ba3100 e8be93e5 85a5e580 bce4b8ba
402040 3300e8be 93e585a5 e580bce4 b8ba3500
402050 e8be93e5 85a5e580 bce4b8ba 3700e8be
402060 93e585a5 e580bce4 b8ba3900 e8be93e5
402070 85a5e580 bce4b88d e59088e8 a7840000
402080 aa114000 00000000 6e114000 00000000 ;case 地址表,其中下标0、2、4、6、8的值为4011aa,定位到 default 地址
402090 aa114000 00000000 7a114000 00000000
4020a0 aa114000 00000000 86114000 00000000
4020b0 aa114000 00000000 92114000 00000000
4020c0 aa114000 00000000 9e114000 00000000

编译器将所有 case 语句地址放在一个数组内,组成地址表,case 1 的地址作为数组下标1的元素,case 3 的地址作为数组下标3的元素,中间空缺的的值使用 default 语句地址填充,之后使用 switch 语句设置的变量a作为数组下标调用地址表中的元素,最后使用jmp指令执行跳转。

比如a为3,则 rax*8+0x402080 等于 3*8+数组地址,乘以8是因为数组元素长度为8字节。

若多个 case 语句设置的最小常量与最大常量之间有很多的空缺数据,则编译器不会进行上述方式优化,否则将会填充过多的 default 语句地址,过于浪费内存,以空间换时间的优化会有一个最大限量,超过此量将会得不偿失。

条件运算式

条件运算式类似 if else 语句,但是条件运算式只能根据判断条件返回两个数据之一,此功能编译器可以进行一些优化,比使用 if else 语句执行速度更快。

条件 ? 数据1 : 数据2;

条件是一个布尔值,若为true则返回数据1,若为false则返回数据2。

#include <stdio.h>
int main()
{
int a,b; printf("输入两个整数,判断最大值\n");
scanf("%d%d", &a, &b); a = a>b ? a : b; //若a>b则返回a,否则返回b printf("最大值为%d\n", a); return 0;
}

条件运算式对应汇编代码:

  40116e:    mov    edx,DWORD PTR [rbp-0x8]    ;变量b写入edx
  401171:    mov    eax,DWORD PTR [rbp-0x4]    ;变量a写入eax
  401174:    cmp    edx,eax                    ;比较b与a
  401176:    cmovge eax,edx                    ;若b>=a则将b写入eax,否则eax存储a
  401179:    mov    DWORD PTR [rbp-0x4],eax    ;eax写入变量a

【循环语句】

循环语句用于循环执行一段代码,循环条件是一个布尔值,若为true则循环一遍,为false则退出循环。

while 循环

while循环首先判断循环条件,之后确定是否执行循环。

while(循环条件)
{
    循环代码段;
}
#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    int b = 0;
    
    /* 遍历数组a */
    while(b < 5)
    {
        printf("%d\n", a[b]);
        b++;
    }
    
    return 0;
}

上述循环语句对应汇编代码如下:

  40112a:    mov    DWORD PTR [rbp-0x20],0x1    ;数组a入栈存储
  401131:    mov    DWORD PTR [rbp-0x1c],0x2
  401138:    mov    DWORD PTR [rbp-0x18],0x3
  40113f:    mov    DWORD PTR [rbp-0x14],0x4
  401146:    mov    DWORD PTR [rbp-0x10],0x5   40114d:    mov    DWORD PTR [rbp-0x4],0x0     ;变量b赋值
  401154:    jmp    401177                      ;跳转到循环条件判断语句   401156:    mov    eax,DWORD PTR [rbp-0x4]     ;循环语句内部代码
  401159:    cdqe   
  40115b:    mov    edx,DWORD PTR [rbp+rax*4-0x20]
  40115f:    mov    eax,DWORD PTR [rbp-0x4]
  401162:    mov    esi,eax
  401164:    mov    edi,0x402004
  401169:    mov    eax,0x0
  40116e:    call   401030                      ;输出函数
  401173:    add    DWORD PTR [rbp-0x4],0x1     ;b++   401177:    cmp    DWORD PTR [rbp-0x4],0x4     ;循环条件判断语句
  40117b:    jle    401156                      ;b小于等于4(等同于小于5)则向前跳转,实现循环

编译器进行了一个优化,将循环条件判断代码放在了末尾,这样可以少使用一个跳转指令,降低CPU分支预测失败率,等于转换成了 do while 循环。

嵌套 while

循环语句可以嵌套定义,比如遍历二维数组时就需要使用嵌套循环语句。

#include <stdio.h>
int main()
{
    int a[3][5] =
    {
        {1,2,3,4,5},
        {11,12,13,14,15},
        {21,22,23,24,25}
    };
    int b = 0, c;
    
    while(b < 3)
    {
        c = 0;
        while(c < 5)
        {
            printf("%d,", a[b][c]);
            c++;
        }
        printf("\n");
        b++;
    }
    
    return 0;
}

有条件执行语句和循环语句也可以互相嵌套,在有条件执行语句内定义循环语句,或在循环语句内定义有条件执行语句。

无限循环

很多程序的工作就是无限循环,比如服务器端程序,不断的接收用户发送的网络数据,之后进行处理。

对于一些循环条件很复杂的有限循环,也可以使用无限循环代替,将循环条件放在循环语句内部,若满足条件则循环,否则执行break语句终止循环。

但是对于循环语句的某些终止条件由外界提供的情况,应该慎用无限循环,若出现意外情况可能会导致循环语句永远不会被终止,为了避免这种情况可以使用一个循环次数很大的有限循环代替无限循环,正常情况下不会执行如此多次的循环,即使循环语句最终没有使用break终止,也会因为循环次数达到上限而终止。

#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    int b = 0;
    
    while(1)    //循环条件固定为1
    {
        if(b > 4) break;    //b>4则执行break语句终止循环
        
        printf("%d\n", a[b]);
        b++;
    }
    
    return 0;
}

for 循环

for循环是while循环的另一种使用方式,for将循环条件变量的定义、判断、修改放在一起,方便查看代码,所有的for循环都可以使用while循环代替。

for(循环条件变量; 循环条件判断; 循环条件修改)
{
    循环代码段;
}

for循环使用说明:
1.循环条件变量,定义循环条件所用的变量,只会执行一次,相当于在for外部定义的一个变量,但是只能在for内部使用。
2.循环条件判断,循环条件变量参与的运算式,返回布尔值,若为true则执行一次循环,若为false则退出循环。
3.循环条件修改,修改循环条件变量的值。

#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    
    /* 变量i只定义一次,每次循环前首先判断i<5,每次循环后执行i++ */
    for(int i = 0; i < 5; i++)
    {
        printf("%d\n", a[i]);
    }
    
    return 0;
}

有些古老的编译器不支持上述用法,需要使用如下形式:

#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    
    int i;
    for(i = 0; i < 5; i++)
    {
        printf("%d\n", a[i]);
    }
    
    return 0;
}

for的无限循环形式:for(;;) { }

do while 循环

do while 循环语句将循环条件放在循环代码段之后,首先执行一遍循环代码,然后判断循环条件,若满足条件则继续循环,循环语句至少执行一次。

do
{
    循环代码段;
}while(循环条件);
#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    int b = 0;
    
    /* while定义在循环语句末尾,首先执行一次循环,之后判断循环条件 */
    do
    {
        printf("%d\n", a[b]);
        b++;
    }while(b < 5);
    
    return 0;
}

while 与 do while 的执行流程区别如下。

while循环语句执行顺序:
1.进入 while =》 满足条件 =》 不执行跳转,顺序执行一遍循环代码 =》 执行跳转,跳转到 while 起始地址
2.进入 while =》 不满足条件 =》 执行跳转,跳过 while

do while循环语句执行顺序:
1.进入 do while 直接执行循环代码 =》 满足条件 =》 执行跳转,跳转到 do while 起始地址
2.进入 do while 直接执行循环代码 =》 不满足条件 =》 不执行跳转,顺序执行 do while 之后的代码

在不满足循环条件时 while 会比 do while 多执行一个跳转指令,为了减少执行跳转指令,编译器会将 while 和 for 转换为 do while。
实际上之前介绍的 while 语句汇编代码已经转换为 do while,因为循环条件在编译期间即可确定成立,编译器判断循环语句至少会执行一次,若循环条件不能在编译期间确定是否为true,编译器也会将 while 转换为 do while,比如下面的代码。

#include <stdio.h>
int main()
{
    unsigned int a;
    scanf("%u", &a);    //程序执行期间输入a的值     /* 变量a递减,直到a为0 */
    while(a != 0)
    {
        printf("%d\n", a);
        a--;
    }     return 0;
}

编译器转换为如下代码:

#include <stdio.h>
int main()
{
    unsigned int a;
    scanf("%u", &a);
    
    if(a != 0)
    {
        do
        {
            printf("%d\n", a);
            a--;
        }while(a != 0);
    }
    
    return 0;
}

【跳转语句】

break 跳转

break语句有两个作用:
1.终止循环语句,执行循环语句之后的代码,对于嵌套使用的循环,break只终止所在的这层循环。
2.跳过switch case语句,执行switch case之后的代码。

猜数字游戏代码:

#include <stdio.h>
int main()
{
    int a=17, b=0;    //a为用户要猜测的数据,b存储用户输入的数据
    char x=0;         //清空标准输入文件使用
    
    printf("猜数字游戏,数据范围 0 - 100,请输入你认为的数据\n");
    
    /* 无限循环 */
    while(1)
    {
        scanf("%d", &b);    //输入b的值
        
        /* 清空标准输入文件,原因在C语言标准函数库一节中介绍 */
        while(x != 10)
        {
            x = getchar();
        }
        x = 0;
        
        if(b < a)
        {
            printf("太小了,请重新输入\n");
        }
        else if(b > a)
        {
            printf("太大了,请重新输入\n");
        }
        else
        {
            printf("猜对了,正确值是%d\n", a);
            
            break;    //用户输入正确数据后终止循环
        }
    }
    
    return 0;
}

continue 跳转

continue语句用于终止循环语句的本次循环,执行下一次循环,一般与if语句配合使用,在满足条件时放弃本次循环。

#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    
    for(int i = 0; i < 5; i++)
    {
        if(i == 3)
        {
            continue;    //i为3则放弃本次循环
        }
        
        printf("a[%d]=%d\n", i, a[i]);
    }
    
    return 0;
}

goto 跳转

goto语句用于无条件跳转到任意代码处执行,可以向前跳转也可以向后跳转,一般与if语句配合使用,实现有条件任意跳转,常用于复杂逻辑判断中,比如多个if语句和循环语句互相嵌套的情况,若没有goto语句则实现某些功能的代码会非常复杂。

#include <stdio.h>
int main()
{
    int a;
    scanf("%d", &a);      //输入a的值
    
    if(a == 0) goto x;    //x为自定义的地址名称,若a等于0,则跳过功能一代码,执行功能二代码,否则两段代码都执行
    
    //功能一代码 ......
    printf("功能一执行完毕\n");
    
    x:
    //功能二代码 ......
    printf("功能二执行完毕\n");
    
    return 0;
}

07. C语言程序执行流程控制的更多相关文章

  1. 第三章 go语言 程序的流程控制

    程序的流程控制主要包括三种不同的结构,分别是顺序结构.选择结构和循环结构. ² 顺序结构最为简单,程序执行时,按照语句的顺序自上而下依次执行. ² 选择结构是根据条件表达式的结果选择执行某些语句. ² ...

  2. 使用events.EventEmitter 控制Node.js 程序执行流程

    使用events.EventEmitter 控制Node.js 程序执行流程 标题写的可能也不太对,大家领会精神: Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台. ...

  3. Go语言 数据类型,流程控制

    Go语言 数据类型,流程控制 人生苦短,Let's Go ! package main // 必须要有一个main包 import "fmt" func main() { fmt. ...

  4. Go语言基础之流程控制

    Go语言基础之流程控制 流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的“经脉”. Go语言中最常用额流程控制有if和for,而switch和goto主要是为了简化代 ...

  5. GO语言学习——Go语言基础之流程控制一

    Go语言基础之流程控制 if else(分支结构) package main import "fmt" // if条件判断 func main(){ // age := 19 // ...

  6. 3.3 Execution Flow of a DDD Based Application 基于DDD的应用程序执行流程

    3.3 Execution Flow of a DDD Based Application 基于DDD的应用程序执行流程 The figure below shows a typical reques ...

  7. Go语言学习笔记-流程控制(二)

    Go语言流程控制 字典类型Map 1.上节遗留:map字典类型 变量声明:var myMap map[string] PersonInfo 其中,myMap是变量名,string是键的类型,Perso ...

  8. 1.4 Go语言基础之流程控制

    流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的"经脉". Go语言中最常用的流程控制有if和for,而switch和goto主要是为了简化代码. ...

  9. GO学习-(7) Go语言基础之流程控制

    流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的"经脉". Go语言中最常用的流程控制有if和for,而switch和goto主要是为了简化代码. ...

  10. 学习Struts--Chap02:Struts2框架各个功能模块和程序执行流程的介绍

    1.Struts2的系统架构: 2.架构中不同Key的作用介绍: servlet Filters:过滤器链,client的全部请求都要经过Filter链的处理. Struts Core:Struts2 ...

随机推荐

  1. Spring---AoP(面向切面编程)原理学习笔记【全】

    1.AOP 1.1.什么是AOP AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延 ...

  2. vue3 快速入门系列 —— 基础

    vue3 快速入门系列 - 基础 前面我们已经用 vue2 和 react 做过开发了. 从 vue2 升级到 vue3 成本较大,特别是较大的项目.所以许多公司对旧项目继续使用vue2,新项目则使用 ...

  3. #dp,vector#AT2567 [ARC074C] RGB Sequence

    题目 分析 一种很正常的想法就是设\(dp[i][j][k]\), 表示前\(i\)个格子,其它两种颜色出现的位置分别为\(j,k,j>k或j=k=0(可取两种颜色)\)的方案数 那么颜色种类限 ...

  4. 【开源三方库】Easyui:基于OpenAtom OpenHarmony ArkUI深度定制的组件框架

      万冬阳 公司:中国科学院软件所 小组:知识体系工作组 简介 Easyui是一套基于ArkTS语言开发的轻量.可靠的移动端组件库,它是对OpenAtom OpenHarmony(以下简称" ...

  5. SQL 数据库语句- 创建和管理数据库

    SQL CREATE DATABASE 语句 SQL CREATE DATABASE 语句用于创建一个新的 SQL 数据库. 语法 CREATE DATABASE 数据库名称; 示例 以下 SQL 语 ...

  6. Go 语言函数、参数和返回值详解

    函数是一组语句,可以在程序中重复使用.函数不会在页面加载时自动执行.函数将通过调用函数来执行. 创建函数 要创建(通常称为声明)一个函数,请执行以下操作: 使用 func 关键字. 指定函数的名称,后 ...

  7. HMS Core手语服务荣获2022中国互联网大会“特别推荐案例”:助力建设数字社会

    11月15日,HMS Core手语服务在2022(第二十一届)中国互联网大会 "互联网助力经济社会数字化转型"案例评选活动中,荣获"特别推荐案例". 经过一年多 ...

  8. HarmonyOS 极客马拉松2023 正式启动,诚邀极客们用键盘码出无限可能!

      原文:https://mp.weixin.qq.com/s/p2yIs0rMmDE2BwhzsAtr7A,点击链接查看更多技术内容. 2023年6月15日, HarmonyOS极客马拉松2023开 ...

  9. Harbor仓库高可用

    一.搭建两台Harbor 搭建方法参考:https://www.cnblogs.com/hanfuming/p/15750031.html 二.两台新建相同项目 三.第二台harbor上仓库管理中新建 ...

  10. Blocks(单调栈)

    题干中说每次选择一个大于k的数,还要选他左右两个数其中之一加上一,最后问你最长的每个数不小于K的子序列. 这些都是障眼法,其实就是问你最长的平均值大于或等于K的最长子序列,这样就明朗了. 接下来就是找 ...