循环结构 (Loop Structures)


章节概述

循环是程序重复执行代码块的核心机制,也是算法的基础(如遍历、搜索、累加)。C 语言提供了三种循环结构:for(计数循环)、while(先判断后执行)、do-while(先执行后判断)。本章逐一讲解三种循环的语法和适用场景,深入讨论 break(跳出循环)和 continue(跳过本次迭代)的控制流,以及无限循环、循环优化(循环提升/循环展开/减少内存访问)等进阶话题。最后从汇编层面展示循环指令模式(cmp + jmp 回跳),帮助建立”C 循环 → 汇编回跳”的心智模型。学完本章后,建议阅读 CPP: 循环算法 了解更丰富的循环算法模式。

核心主题:循环本质是”条件跳转 + 计数器更新”。CPU 没有”循环”这一原语——循环是条件判断和跳转的组合。理解循环的汇编实现是理解”为什么某些循环写法更快”的钥匙。


第一节:for 循环详解


1.1 for 循环语法结构

#include <stdio.h>
 
int main() {
    // for 循环三要素:
    // for (初始化; 条件; 更新) { 循环体 }
 
    // 基本用法:打印 1 到 5
    for (int i = 1; i <= 5; i++) {
        printf("%d ", i);
    }
    printf("\n");  // 1 2 3 4 5
 
    // 三要素的执行顺序:
    // 1. 初始化(仅执行一次,循环开始前)
    // 2. 条件检查(每次迭代前)
    // 3. 循环体(条件为真时执行)
    // 4. 更新(每次循环体执行完后)
    // 5. 回到第 2 步
 
    // 三要素都可以省略(省略条件 = 永远为真)
    // for (;;) { }  等价于 while(1) { } — 无限循环
 
    return 0;
}

1.2 for 循环的执行轨迹可视化

graph TD
    A[初始化: i = 1] --> B{条件: i <= 5?}
    B -->|是| C[执行循环体]
    C --> D[更新: i++]
    D --> B
    B -->|否| E[循环结束]

1.3 for 循环的各种变体

#include <stdio.h>
 
int main() {
    // 变体1:多个初始化变量和更新
    for (int i = 0, j = 10; i < j; i++, j -= 2) {
        printf("i=%d, j=%d\n", i, j);
    }
 
    // 变体2:省省略初始化和更新(在循环体内处理)
    int k = 0;
    for (; k < 5; ) {
        printf("%d ", k);
        k++;
    }
    printf("\n");
 
    // 变体3:倒序循环
    for (int i = 5; i >= 1; i--) {
        printf("%d ", i);
    }
    printf("\n");
 
    // 变体4:步长不为 1
    for (int i = 0; i <= 20; i += 5) {
        printf("%d ", i);  // 0 5 10 15 20
    }
    printf("\n");
 
    // 变体5:C99 中在 for 内声明变量(推荐,限制作用域)
    // C90 不允许,C99 及以后允许
    for (int i = 0; i < 3; i++) {
        printf("%d ", i);
    }
    // i 在这里不可见(C99 语义)
 
    return 0;
}

1.4 for 循环常见错误

#include <stdio.h>
 
int main() {
    // 错误1:分号误用(经典 bug)
    // for 后面多了分号 = 空循环体!
    for (int i = 0; i < 3; i++);  // ← 多了一个分号!
        printf("只输出一次!\n");
 
    // 正确写法
    for (int i = 0; i < 3; i++) {
        printf("输出 %d\n", i + 1);
    }
 
    // 错误2:溢出导致无限循环
    // 如果 i 是 signed char,范围 -128~127
    // for (signed char i = 0; i < 200; i++) { ... }
    // i 永远不会超过 127 → 无限循环
 
    // 错误3:浮点数作为循环变量
    // for (float f = 0.0; f != 1.0; f += 0.1) { ... }
    // 由于浮点精度误差,f 可能永远不会精确等于 1.0
    // 应该使用整数计数:for (int i = 0; i < 10; i++) { float f = i * 0.1f; }
 
    // 错误4:循环中修改循环变量
    for (int i = 0; i < 10; i++) {
        if (i == 3) i = 7;  // 不推荐!会导致难以预料的跳变
        printf("%d ", i);    // 0 1 2 3 8 9
    }
    printf("\n");
 
    return 0;
}

小节练习

选择题 1

以下 for 循环执行多少次?

for (int i = 0; i < 5; i++);
  • A. 0 次

  • B. 5 次

  • C. 无限次

  • D. 编译错误

判断题 1

for 循环的三个部分(初始化、条件、更新)都可以省略。 ( )

  • 正确

  • 错误


第二节:while 和 do-while 循环


2.1 while 循环

#include <stdio.h>
 
int main() {
    // while 语法:
    // while (条件) { 循环体 }
    // 先检查条件,后执行循环体(可能一次都不执行)
 
    int count = 1;
    while (count <= 5) {
        printf("%d ", count);
        count++;
    }
    printf("\n");  // 1 2 3 4 5
 
    // while 适用于迭代次数不确定的场景
    // 例如:读取文件直到 EOF
    int sum = 0, num;
    printf("输入数字(0 结束): ");
    scanf("%d", &num);
    while (num != 0) {
        sum += num;
        printf("当前和: %d, 继续输入(0 结束): ", sum);
        scanf("%d", &num);
    }
    printf("最终和: %d\n", sum);
 
    return 0;
}

2.2 do-while 循环

#include <stdio.h>
 
int main() {
    // do-while 语法:
    // do { 循环体 } while (条件);
    // 先执行循环体,后检查条件(至少执行一次)
 
    int attempts = 0;
    int password;
 
    do {
        printf("请输入密码: ");
        scanf("%d", &password);
        attempts++;
    } while (password != 1234 && attempts < 3);
 
    if (password == 1234) {
        printf("密码正确!\n");
    } else {
        printf("尝试次数用完!\n");
    }
 
    // do-while 的典型场景:
    // 1. 至少需要执行一次的操作(如密码输入)
    // 2. 游戏循环(至少运行一帧)
    // 3. 验证用户输入合法性
 
    return 0;
}

2.3 while 与 do-while 的适用场景对比

#include <stdio.h>
 
int main() {
    int x = 10;
 
    // while:先判断后执行
    // 如果条件开始就为假,循环体一次都不执行
    printf("while 循环: ");
    while (x < 5) {
        printf("这行不会被打印\n");
    }
    printf("(无输出)\n");
 
    // do-while:先执行后判断
    // 即使条件开始就为假,循环体至少执行一次
    printf("do-while 循环: ");
    do {
        printf("这行至少被执行一次\n");
    } while (x < 5);
 
    return 0;
}

2.4 循环的汇编表示

// C 代码
int sum_for(int n) {
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += i;
    }
    return sum;
}

对应 x86-64 汇编(简化):

sum_for:
    movl    $0, %eax        ; sum = 0(eax)
    movl    $1, %edx        ; i = 1(edx)
    cmpl    %edi, %edx      ; 比较 i 和 n
    jg      .L_done         ; 如果 i > n,跳转到结束
.L_loop:
    addl    %edx, %eax      ; sum += i
    addl    $1, %edx        ; i++
    cmpl    %edi, %edx      ; 再次比较
    jle     .L_loop         ; 如果 i <= n,跳回循环
.L_done:
    ret

汇编层面的循环就是”条件跳转 + 回跳”。forwhiledo-while 在汇编中结构相同(只是跳转位置不同)。详见 。

小节练习

选择题 1

do-while 循环体至少执行多少次?

  • A. 0 次

  • B. 1 次

  • C. 取决于条件

  • D. 无限次


第三节:break 和 continue


3.1 break —— 跳出循环

#include <stdio.h>
#include <stdbool.h>
 
int main() {
    // break 立即终止整个循环(不再进行本次迭代的剩余代码)
    // 也不进行下一次迭代
 
    printf("查找第一个能被 7 整除的数: ");
    for (int i = 1; i <= 100; i++) {
        if (i % 7 == 0) {
            printf("%d\n", i);  // 打印 7
            break;              // 找到就退出
        }
    }
 
    // break 在嵌套循环中只跳出最内层
    printf("嵌套循环中的 break:\n");
    for (int i = 1; i <= 3; i++) {
        for (int j = 1; j <= 3; j++) {
            if (i == 2 && j == 2) {
                break;  // 只跳出内层 j 循环
            }
            printf("(%d,%d) ", i, j);
        }
        printf("\n");
    }
    // 输出: (1,1) (1,2) (1,3) (2,1) (3,1) (3,2) (3,3)
 
    return 0;
}

3.2 continue —— 跳过本次迭代

#include <stdio.h>
 
int main() {
    // continue 跳过本次迭代的剩余代码,直接进入下一次迭代的条件检查
 
    printf("打印奇数(跳过偶数): ");
    for (int i = 1; i <= 10; i++) {
        if (i % 2 == 0) {
            continue;  // 偶数 → 跳过 printf,直接 i++
        }
        printf("%d ", i);  // 1 3 5 7 9
    }
    printf("\n");
 
    // continue 与 while 配合时的陷阱
    printf("while + continue: ");
    int i = 0;
    while (i < 10) {
        i++;
        if (i % 2 == 0) {
            continue;  // 跳过 printf,但 i++ 在 continue 前面,没问题!
        }
        printf("%d ", i);
    }
    printf("\n");
 
    // 如果在 while 中使用 continue,务必确保更新逻辑在 continue 之前
    // 否则可能产生无限循环:
    printf("危险示例(不要运行): ");
    // int j = 0;
    // while (j < 10) {
    //     if (j % 2 == 0) {
    //         continue;  // 跳过 j++!→ 无限循环
    //     }
    //     j++;
    // }
 
    return 0;
}

3.3 break 和 continue 的可读性考虑

#include <stdio.h>
 
int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int n = 10;
    int target = 7;
 
    // 线性查找 —— break 是自然的用法
    int found_index = -1;
    for (int i = 0; i < n; i++) {
        if (arr[i] == target) {
            found_index = i;
            break;
        }
    }
    printf("找到目标 %d 在索引 %d\n", target, found_index);
 
    // 过滤打印 —— continue 是自然的用法
    printf("跳过 3 的倍数: ");
    for (int i = 0; i < n; i++) {
        if (arr[i] % 3 == 0) {
            continue;
        }
        printf("%d ", arr[i]);
    }
    printf("\n");
 
    return 0;
}

小节练习

选择题 1

在嵌套循环中,break 语句会跳出几层循环?

  • A. 所有循环

  • B. 最内层循环

  • C. 最外层循环

  • D. 两层循环


第四节:循环优化基础


4.1 循环提升(Loop Hoisting)

将循环中不变的计算提到循环外部:

#include <stdio.h>
#include <string.h>
 
int main() {
    char str[] = "Hello, C Language!";
 
    // 不好的写法:每次循环都计算 strlen
    printf("不优化: ");
    for (int i = 0; i < strlen(str); i++) {
        printf("%c", str[i]);
    }
    printf("\n");
    // strlen 在每次迭代都被调用 → O(n²) 复杂度!
 
    // 优化写法:将 strlen 提升到循环外
    printf("优化后: ");
    int len = strlen(str);  // 只计算一次
    for (int i = 0; i < len; i++) {
        printf("%c", str[i]);
    }
    printf("\n");
 
    return 0;
}

其他循环提升示例:

// 不优化:每条循环都做乘法
int sum_bad(int n) {
    int sum = 0;
    int scale = 100;
    for (int i = 0; i < n; i++) {
        sum += i * scale;  // 乘法在循环内
    }
    return sum;
}
 
// 优化后:乘法提到循环外(虽然现代编译器常自动优化)
int sum_good(int n) {
    int sum = 0;
    // 数学等价变换
    return (n - 1) * n / 2 * 100;
}

4.2 循环展开(Loop Unrolling)

#include <stdio.h>
 
// 普通循环
int sum_normal(int arr[], int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += arr[i];
    }
    return sum;
}
 
// 手动 4 路展开(减少循环分支和计数器更新开销)
int sum_unrolled4(int arr[], int n) {
    int sum = 0;
    int i;
 
    // 主循环:每次处理 4 个元素
    for (i = 0; i <= n - 4; i += 4) {
        sum += arr[i] + arr[i + 1] + arr[i + 2] + arr[i + 3];
    }
 
    // 处理剩余不足 4 个的元素
    for (; i < n; i++) {
        sum += arr[i];
    }
 
    return sum;
}

现代编译器(GCC -O2 及以上)会自动进行循环展开,手动展开仅在不允许优化 (-O0) 或确有性能关键代码时才需要。

4.3 减少循环中的内存访问

#include <stdio.h>
 
int arr[1000][1000];
 
// 缓存友好的循环(按行访问 — row-major)
void cache_friendly() {
    int sum = 0;
    for (int i = 0; i < 1000; i++) {
        for (int j = 0; j < 1000; j++) {
            sum += arr[i][j];  // 访问连续内存 → 缓存命中率高
        }
    }
}
 
// 缓存不友好的循环(按列访问)
void cache_unfriendly() {
    int sum = 0;
    for (int j = 0; j < 1000; j++) {
        for (int i = 0; i < 1000; i++) {
            sum += arr[i][j];  // 跳跃访问 → 大量缓存缺失
        }
    }
}
// 在大型数组上,cache_friendly 可能比 cache_unfriendly 快数倍!

循环优化涉及寄存器分配、指令调度、向量化(SIMD)等高级话题。-O2 / -O3 编译器优化已经非常成熟。学习循环优化主要是为了理解编译器对你的代码做了什么,而非手动取代编译器。

小节练习

选择题 1

对于数组 int arr[1000][1000],以下哪个循环的缓存性能最好?

  • A. for (j=0; j<1000; j++) for (i=0; i<1000; i++) sum += arr[i][j];

  • B. for (i=0; i<1000; i++) for (j=0; j<1000; j++) sum += arr[i][j];

  • C. 两者一样

  • D. 取决于编译器


第五节:循环与嵌套循环实战


5.1 打印图形

#include <stdio.h>
 
int main() {
    // 打印矩形
    printf("5×5 矩形:\n");
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            printf("* ");
        }
        printf("\n");
    }
 
    // 打印直角三角形
    printf("\n直角三角形:\n");
    for (int i = 1; i <= 5; i++) {
        for (int j = 1; j <= i; j++) {
            printf("* ");
        }
        printf("\n");
    }
 
    // 打印九九乘法表
    printf("\n九九乘法表:\n");
    for (int i = 1; i <= 9; i++) {
        for (int j = 1; j <= i; j++) {
            printf("%d×%d=%-2d ", j, i, i * j);
        }
        printf("\n");
    }
 
    return 0;
}

5.2 质数判断

#include <stdio.h>
#include <stdbool.h>
#include <math.h>
 
bool is_prime(int n) {
    if (n <= 1) return false;
    if (n <= 3) return true;
    if (n % 2 == 0 || n % 3 == 0) return false;
 
    // 只需检查到 sqrt(n),且跳过偶数
    for (int i = 5; i * i <= n; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0) {
            return false;
        }
    }
    return true;
}
 
int main() {
    printf("1~100 之间的质数: ");
    int count = 0;
    for (int n = 1; n <= 100; n++) {
        if (is_prime(n)) {
            printf("%d ", n);
            count++;
        }
    }
    printf("\n共有 %d 个质数\n", count);
 
    return 0;
}

5.3 最大公约数(辗转相除法)

#include <stdio.h>
 
int gcd(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}
 
int main() {
    printf("GCD(48, 36) = %d\n", gcd(48, 36));   // 12
    printf("GCD(17, 5)  = %d\n", gcd(17, 5));    // 1
    printf("GCD(100, 40) = %d\n", gcd(100, 40)); // 20
    return 0;
}

5.4 数字反转

#include <stdio.h>
 
int reverse_number(int n) {
    int reversed = 0;
    while (n != 0) {
        reversed = reversed * 10 + n % 10;
        n /= 10;
    }
    return reversed;
}
 
int main() {
    // 数字反转
    int num = 12345;
    printf("%d 反转后: %d\n", num, reverse_number(num));
 
    // 判断回文数
    int pal = 12121;
    if (pal == reverse_number(pal)) {
        printf("%d 是回文数\n", pal);
    } else {
        printf("%d 不是回文数\n", pal);
    }
 
    return 0;
}

小节练习

选择题 1

以下代码输出什么?

int x = 0;
for (int i = 0; i < 5; i++) {
    x += i;
    if (i == 3) break;
}
printf("%d\n", x);
  • A. 10

  • B. 6

  • C. 3

  • D. 0


第六节:循环的汇编实现与性能


6.1 三种循环的汇编等价性

// for 循环
int sum_for(int n) {
    int s = 0;
    for (int i = 0; i < n; i++) s += i;
    return s;
}
 
// while 循环
int sum_while(int n) {
    int s = 0, i = 0;
    while (i < n) { s += i; i++; }
    return s;
}
 
// 这三者在汇编层面生成**几乎完全相同**的代码!
// 差异仅在跳转指令的位置和初始化部分
for 的汇编模式:          while 的汇编模式:
  init                     init
  ┌─────┐                  ┌─────┐
  │ cmp  │  ← 条件检查       │ cmp  │  ← 先检查(可能0次)
  │ jxx  │                  │ jxx  │
  │ body │                  │ body │
  │ inc  │                  │ inc  │
  │ jmp  │  → 回跳          │ jmp  │  → 回跳
  └─────┘                  └─────┘

do-while 汇编模式:
  init
  ┌─────┐
  │ body │  ← 先执行(至少1次)
  │ inc  │
  │ cmp  │  ← 后检查
  │ jxx  │  → 回跳
  └─────┘

6.2 分支预测对循环的影响

#include <stdio.h>
#include <time.h>
 
// CPU 的分支预测器会对循环的"继续"分支产生强预测
// 因为循环在大多数迭代中都是"继续"(而非退出)
 
// 排序数组 vs 随机数组的分支预测差异
// 这个经典示例展示分支预测的性能影响:
int count_above_threshold(int arr[], int n, int threshold) {
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (arr[i] > threshold) {
            count++;
        }
    }
    return count;
}
// 当 arr 已排序时,分支预测准确率极高,性能更好
// 当 arr 随机时,分支预测失败约 50%,性能下降

分支预测器是 CPU 微架构的核心。当循环次数较大时,预测器会”学习”到”跳回”的模式并获得极高准确性。最终的退出跳转会因”意外”导致一次分支预测失败,这在大量迭代中可以忽略。

6.3 无限循环的模式

#include <stdio.h>
#include <stdbool.h>
 
// 几种无限循环写法(在汇编层面等价)
int main() {
    // 写法1: while(1)
    int count = 0;
    while (1) {
        if (count >= 5) break;
        printf("while(1): %d\n", count++);
    }
 
    // 写法2: for(;;)
    count = 0;
    for (;;) {
        if (count >= 5) break;
        printf("for(;;): %d\n", count++);
    }
 
    // 写法3: while(true) — 需要 <stdbool.h>
    count = 0;
    while (true) {
        if (count >= 5) break;
        printf("while(true): %d\n", count++);
    }
 
    // 汇编层面三者完全等价:
    //     jmp .L_loop    ; 无条件回跳
 
    return 0;
}

小节练习

判断题 1

forwhiledo-while 在汇编层面有本质区别。 ( )

  • 正确

  • 错误


章节测试

一、判断题(正确选,错误选)

判断题 1

for (;;) 是 C 语言中合法的无限循环写法。 ( )

  • 正确

  • 错误

判断题 2

continue 语句会终止整个循环。 ( )

  • 正确

  • 错误

判断题 3

while 循环中,循环体可能一次也不执行。 ( )

  • 正确

  • 错误

判断题 4

float 类型变量作为循环计数器是安全可靠的做法。 ( )

  • 正确

  • 错误

判断题 5

break 可以用于跳出 if 语句。 ( )

  • 正确

  • 错误

判断题 6

在嵌套循环中使用 break 会跳出所有嵌套层次。 ( )

  • 正确

  • 错误

判断题 7

for (int i = 0; i < strlen(s); i++) 的时间复杂度是 O(n)。 ( )

  • 正确

  • 错误

判断题 8

C99 中,在 for 语句的初始化部分声明的变量在 for 循环体外仍然可见。 ( )

  • 正确

  • 错误

判断题 9

辗转相除法(欧几里得算法)计算 GCD 的时间复杂度是 O(log n)。 ( )

  • 正确

  • 错误

判断题 10

编译器(-O2)能自动将循环中的不变计算外提(循环提升)。 ( )

  • 正确

  • 错误


二、选择题(单项选择题)

选择题 1

以下哪个循环体至少执行一次?

  • A. for (int i = 0; i < 0; i++) {...}

  • B. while (0) {...}

  • C. do {...} while (0);

  • D. for (; 0;) {...}

选择题 2

以下代码输出什么?

int i = 10;
while (i --> 0) {
    printf("%d", i);
}
  • A. 109876543210

  • B. 9876543210

  • C. 10987654321

  • D. 编译错误

选择题 3

for (int i = 0; i < 5; i++); printf("done"); 这段代码会输出几次 done?

  • A. 0 次

  • B. 1 次

  • C. 5 次

  • D. 编译错误

选择题 4

判断质数的最优化方法是?

  • A. 循环遍历 2 到 n-1

  • B. 循环遍历 2 到 n/2

  • C. 循环遍历 2 到 sqrt(n)

  • D. 循环遍历 2 到 sqrt(n),跳步 6k±1

选择题 5

以下关于 continue 的说法正确的是?

  • A. continue 退出整个循环

  • B. continue 跳过本次迭代剩余部分,进入下一次迭代

  • C. continue 只能在 for 循环中使用

  • D. continue 等同于 break

选择题 6

以下循环能正确计算 1+2+…+100 的是?

  • A. int sum, i; for (i=0; i<=100; i++) sum += i;

  • B. int sum = 0, i = 1; for (; i <= 100; i++) sum += i;

  • C. int sum = 0; while (int i = 1; i <= 100; i++) sum += i;

  • D. int sum = 0; do { sum += i; i++; } while (i <= 100);

选择题 7

辗转相除法求 GCD(48, 18) 的迭代过程是?

  • A. (48,18)→(18,12)→(12,6)→(6,0)→结果 6

  • B. (48,18)→(30,18)→(12,18)→(12,6)→结果 6

  • C. (48,18)→(18,30)→(30,12)→结果 12

  • D. (48,18)→(30,12)→(18,12)→结果 6

选择题 8

以下哪种循环写法最容易导致无限循环?

  • A. for (int i = 0; i < n; i++)

  • B. while (n--)

  • C. for (unsigned int i = n; i >= 0; i--)

  • D. do { x++; } while (x < n);

选择题 9

在 x86-64 汇编中,for 循环的”回跳”通常使用什么指令?

  • A. call

  • B. ret

  • C. jmpjxx

  • D. int

选择题 10

for (int i = 0; i < 10; i++) { if (i == 5) break; } 执行后 i 的值是?

  • A. 4

  • B. 5

  • C. 6

  • D. i 不在作用域中


️ 动手练习题

练习题 1:水仙花数

难度:

编写程序找出所有的”水仙花数”(三位数,各位数字的立方和等于自身,如 153 = 1³+5³+3³)。
使用 for 循环遍历 100-999,用 while 循环提取每一位。

力扣练习:力扣循环遍历题

练习题 2:斐波那契数列

难度:

编写程序输出斐波那契数列的前 n 项:

  • 用 while 循环实现迭代版本
  • 用 for 循环实现数组缓存版本
  • 对比两种实现在 n=40 时的执行时间
  • 实现一个利用变量交换的 O(n) 空间 O(1) 版本

力扣练习:力扣斐波那契题

练习题 3:数字规律探索

难度:

力扣练习:

  • 力扣模拟算法题
  • 力扣简单数学题
  • 力扣数字操作题

每题用 C 语言完成,要求:

  1. 注释标注循环的不变量
  2. 确认没有使用浮点数作为循环变量
  3. 验证 unsigned 和 signed 的使用是否正确

练习题 4:循环与汇编

难度:

编写以下函数,使用 gcc -S -O2 生成汇编代码:

  1. int sum_for(int n) — for 循环累加
  2. int sum_while(int n) — while 循环累加
  3. int sum_goto(int n) — 用 goto 模拟循环

对比三者的汇编代码:

  • 它们是否生成了相同的指令序列?
  • 哪一个少了一条条件检查指令?
  • 观察编译器如何选择寄存器(eax/edx/esi)

参考 。

练习题 5:循环中的性能陷阱

难度:

编写两个版本的矩阵乘法(1000×1000 int 矩阵):

  1. 版本 A:标准三层嵌套,i-k-j 顺序
  2. 版本 B:标准三层嵌套,i-j-k 顺序

clock() 计时比较两种写法的性能差异:

  • 为什么相差数倍?(缓存行/局部性原理)
  • 思考编译器 -O2 优化能否逆转这种差异

参考 CPP: 循环算法 了解更多循环优化模式。