在 C++ 编程练习中,打印九九乘法表是一道非常经典的循环训练题。整张乘法表共有 45 个算式,初看之下好像很复杂,但只要我们看清 “一共有多少行” 以及 “每一行有几个算式”,就能很轻松地通过双层循环把它写出来。
很多初学的小伙伴在写这道题时喜欢死记硬背代码,结果稍微换一种排列形状就不知道该怎么改了。下面我们先来观察九九乘法表的行列规律,理清两层循环各自的分工。
九九乘法表的行列规律
常见的九九乘法表是一个逐行变长的直角三角形。我们先列出前 3 行和最后一行的算式,直观看看行与列的变化规律:
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
......
1*9=9 2*9=18 3*9=27 ... 9*9=81观察上面的算式可以发现,整张表一共有 9 行。其中第 1 行有 1 个算式,第 2 行有 2 个算式……依此类推,第 i 行刚好有 i 个算式。而在同一行内部,乘号后面的数字固定为当前行号 i,乘号前面的数字 j 则从 1 依次递增到 i。
既然同时需要控制 “当前是第几行” 和 “这一行打印几个算式”,我们就可以在一个 for 循环内部再套一个 for 循环来实现。两层循环的具体分工如下:
- 外层循环:用来控制行号
i,让i从 1 循环到 9,每执行一轮就代表打印完整的一行。 - 内层循环:用来控制列号
j,让j从 1 循环到当前的i,依次把第i行的每一个算式打印出来。
C++ 实现九九乘法表
理清两层循环的分工后,代码写起来就很顺畅了。不过在输出时需要注意一个小细节:乘法表里的乘积既有 4、6 这样的一位数,也有 12、16 这样的两位数,直接输出会导致上下列错位。
为了让各列算式对齐,我们可以引入 <iomanip> 头文件,调用 std::setw() 将乘积的显示宽度设为 2 个字符。由于 std::setw(2) 只对紧随其后的那一项输出生效,我们把它直接写在乘积 j * i 前面,遇到一位数时程序就会自动在前面补 1 个空格。下面我们来看这个例子。
示例:打印下三角九九乘法表
#include <iomanip> // 提供 std::setw()
#include <iostream>
int main() {
// 外层循环控制行号 i
for (int i = 1; i <= 9; i++) {
// 内层循环控制列号 j
for (int j = 1; j <= i; j++) {
std::cout << j << "*" << i << "=" << std::setw(2) << j * i << " ";
}
std::cout << '\n'; // 打完一行后换行
}
return 0;
}运行结果如下。
1*1= 1
1*2= 2 2*2= 4
1*3= 3 2*3= 6 3*3= 9
1*4= 4 2*4= 8 3*4=12 4*4=16
1*5= 5 2*5=10 3*5=15 4*5=20 5*5=25
1*6= 6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7= 7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8= 8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9= 9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 6*9=63 8*9=72 9*9=81分析:
在这个例子中,外层循环每执行一轮就负责打印完整的一行。第 1 轮 i 为 1 时,程序进入内层循环,j 取 1 并输出 1*1= 1(等号后面的空格正是 std::setw(2) 自动补上的)。随后 j 自增变成 2,跳出内层循环并执行 std::cout << '\n'; 换行,第 1 行就打印完毕了。
到了第 2 轮,i 变成 2。再次进入内层循环时,j 重新从 1 开始,依次取 1 和 2,在同一行连续输出 1*2= 2 和 2*2= 4。等 j 自增变成 3 时,程序跳出内层循环并换行,后面的第 3 行到第 9 行也是同样的执行过程。
另外在拼接算式时,我们写的是 j << "*" << i 而不是 i << "*" << j。这是因为在第 i 行中,从 1 递增变化的是列号 j,固定不变的是行号 i,把 j 放在乘号前面才能打出 1×2=2、2×2=4 这样符合日常习惯的顺序。
提示 如果不想额外引入 <iomanip> 头文件,我们也可以省略 std::setw(2),直接把算式末尾的空格 " " 换成转义字符 '\t'(水平制表符),同样能让各列算式保持对齐。
编写九九乘法表的常见误区
在亲手敲上面这段代码时,初学的小伙伴最容易在 “内层循环的终止条件” 和 “换行语句的位置” 这两个地方踩坑。下面我们把这两种错误情况拎出来对比看看。
1. 内层循环的终止条件
在编写内层循环时,有些小伙伴会顺着外层循环的习惯,不小心把内层循环条件也写成了 j <= 9。下面列出了正确写法与错误写法的对比:
// 正确方式
for (int j = 1; j <= i; j++) {
std::cout << j << "*" << i << "=" << std::setw(2) << j * i << " ";
}
// 错误方式
for (int j = 1; j <= 9; j++) {
std::cout << j << "*" << i << "=" << std::setw(2) << j * i << " ";
}写成 j <= 9 在编译时虽然不会报错,但不管当前是第几行,内层循环都会固定输出 9 个算式。这样打印出来的是一个 9 × 9 的正方形表格,整张表里还会出现 1×2=2 和 2×1=2 前后重复的算式。因此,内层循环的条件应该写成 j <= i,让每行的算式个数随着行号 i 一起变化。
2. 换行语句的位置
另一个常见错误,是把换行语句 std::cout << '\n'; 误放进了内层循环的花括号里面。下面这种写法就是错误的:
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
std::cout << j << "*" << i << "=" << std::setw(2) << j * i << " ";
std::cout << '\n'; // 误放在内层循环里
}
}如果写成上面这样,程序每打印完一个算式就会立刻换行,最终 45 个算式会排成竖着的一长列。小伙伴们要记住,内层循环负责把当前这一行的算式横着拼完,执行期间不能换行。只有等内层循环结束、整行算式全部输出后,我们才在外层循环末尾执行一次换行。
掌握了正着打印的规律后,如果你想尝试打印倒过来的九九乘法表,也可以把外层循环改成 for (int i = 9; i >= 1; i--),动手运行看看输出形状有什么变化。
