实验一:相传国际象棋
相传国际象棋是古印度舍罕王的宰相达依尔发明的。舍罕王十分喜欢象棋,决定让宰相自己选择何种赏赐。这位聪明的宰相指着8×8共64格的象棋盘说:陛下,请您赏给我一些麦子吧,就在棋盘的第一个格子中放1粒,第2格中放2粒,第3格放4粒,以后每一格都比前一格增加一倍,依此放完棋盘上的64个格子,我就感恩不尽了。舍罕王让人扛来一袋麦子,他要兑现他的许诺。 国王能兑现他的许诺吗?
程序1:试编程计算舍罕王共要多少粒麦子赏赐他的宰相,这些麦子合多少立方米?(已知1立方米麦子约1.42e8粒) 总粒数为:sum=1+2+22+23+…+263
#include <stdio.h>
int main() {
unsigned long long sum = 1, tmp = 1, x = 0;
int i = 0;
// 从第二个格子开始算,总共63个格子
for (i = 1; i < 64; i++) {
// 检查tmp是否为0,避免除以0错误
if (tmp == 0) {
printf("Error: Division by zero.\n");
return -1;
}
tmp *= 2;
sum += tmp;
}
printf("国王总共需要将%llu粒麦子赏赐给他的宰相\n", sum);
// 检查sum是否溢出,如果溢出则返回错误代码
if (sum > (unsigned long long)(-1)) {
printf("Error: Integer overflow.\n");
return -1;
}
x = sum / 1.42e8;
printf("这些麦子合%llu立方\n", x);
return 0;
}
这些代码需要注意的地方包括:
- 数据类型选择:使用
unsigned long long
来存储结果是一个好的选择,因为它能够确保在计算过程中不会溢出。- 错误检查:在循环中检查
tmp
是否为0是一个好的做法,以避免除以0的错误。同样,检查sum
是否溢出也是一个重要的步骤,因为如果sum
的值超过了unsigned long long
的最大值,就会发生溢出。- 代码可读性:添加注释可以帮助其他开发者更好地理解代码的意图和功能。
- 返回值:当检测到错误时,返回一个错误代码(如-1)是一种良好的做法,这样调用代码可以知道发生了什么错误。
- 精度问题:虽然
1.42e8
是一个近似值,但在实际应用中,麦子的数量可能是一个更精确的数值。在这种情况下,你可能需要使用一个更精确的单位转换因子。
实验二:求完数
程序2:一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如6的因子为1,2,3,而6=1+2+3,因此6是“完数”。编程找出1000之内的所有完数,输出所有的完数(要求:一行显示6个数);
完数又称完美数或完备数,是一些特殊的自然数。它所有的真因子(即除了自身以外的约数)的和,恰好等于它本身。如果一个数恰好等于它的因子之和,则称该数为“完数”。所有的完全数都是三角形数,且倒数都是调和数。完数非常稀少,例如第一个完数是6,第二个完数是28。
#include <stdio.h>
int main() {
int i, j, sum;
printf("1000以内的完数如下:\n");
for (i = 1; i < 1000; i++) {
sum = 0;
for (j = 1; j < i; j++)
{
if (i % j == 0)
{ //如果i能被j整除,说明j是i的一个因子
sum += j; //将因子j累加到sum中
}
}
if (sum == i) {
//如果sum的值等于i,说明i是一个完数
printf("%d ",i);
}
}
return 0;
}
在上述代码中,i和j的初始化不能为0是因为0不是完数。完数是指一个数恰好等于它的因子之和(不包括它本身),而0的因子只有它自己,因此0不能被定义为完数。在循环中,如果i或j的初始值为0,那么在判断完数的过程中就会导致错误的结果。因此,为了避免这种情况,i和j的初始值应该设置为1。
实验三:打印九九乘法表
程序3:编程输出如下上三角形式的九九乘法表。
1 2 3 4 5 6 7 8 9
- - - - - - - - -
1 2 3 4 5 6 7 8 9
4 6 8 10 12 14 16 18
9 12 15 18 21 24 27
16 20 24 28 32 36
25 30 35 40 45
36 42 48 54
49 56 63
64 72
81
#include <stdio.h>
int main() {
printf(" 1 2 3 4 5 6 7 8 9\n");
printf(" - - - - - - - - -\n");
int i, j, m, k; //定义整数变量
for (i = 1; i <= 9; i++) //外层循环,控制行数
{
for (m = 1; m <= i - 1; m++) { //内层循环,用于打印每行前面的空格
printf(" "); //打印空格
}
//内层循环,计算打印每行的乘法结果
for (j = i; j <= 9; j++) {
k = i * j;//计算乘法结果
printf("%4d", k);
}
putchar('\n');
}
return 0;
}
以下是一些需要注意的地方:
- 变量初始化:在C语言中,如果变量未被初始化,它们的值是未定义的。因此,在使用变量之前,应该确保它们被正确初始化。
- 缩进和格式:代码的缩进和格式应该保持一致,以提高可读性。
- 错误处理:如果需要,可以添加错误处理代码来处理可能出现的错误情况。
- 变量命名:变量名应该清晰明了,能够准确地描述变量的用途。
- 代码注释:适当的代码注释可以提高代码的可读性。
- 类型匹配:在使用
printf
函数时,要确保使用的格式字符串与变量类型匹配。- 结束符:
putchar('\n')
用于在每一行末尾添加换行符。- 边界条件:在循环中,要确保循环条件正确,以避免数组越界或其他错误。
- 内存管理:如果使用了动态内存分配,要确保释放分配的内存,以避免内存泄漏。
- 编译和运行:确保代码能够正确编译和运行,没有编译错误或运行时错误。
这些是一些常见的注意事项,但具体的注意事项可能会根据代码的上下文和需求而有所不同。
实验四:求1!+2!+3!+…+20!;
#include <stdio.h>
int main() {
int i, j;
long long sum = 0, factorial = 1; //声明两个长整型变量
for (i = 1; i <= 20; i++) {
factorial *= i; //计算i的阶乘,存入factorial
sum += factorial; //值累加到sum中
}
printf("1!+2!+3!+...+20!=%lld\n", sum);
return 0;
}
程序中,使用了一个循环来计算1到20的阶乘,并将它们累加到变量sum
中。最后,使用printf
函数输出结果。注意,由于阶乘的结果可能非常大,因此使用了long long
类型来存储结果。
“llu”和“lld”都是用于格式化输出的格式说明符,它们在C/C++编程中经常与
printf
或scanf
等函数一起使用,以处理长整型数据。
- “llu”代表
unsigned long long int
类型的格式说明符,用于格式化输出无符号的长整型数据。在内存中,无符号的长整型数据可以表示的范围是从0到2的64次方减1。- “lld”代表
long long int
类型的格式说明符,用于格式化输出有符号的长整型数据。在内存中,有符号的长整型数据可以表示的范围是从-2的63次方到2的63次方减1。简而言之,“llu”用于无符号长整型,“lld”用于有符号长整型。在格式化输入或输出时,确保使用正确的格式说明符是非常重要的,以避免数据类型的混淆和潜在的错误。
实验五:
(1)输入一行字符,分别统计出其中的英文字母、空格、数字和其它字符的个数。
(2)在得到正确结果后,请修改程序使之能分别统计大小写字母、空格、数字和其它字符的个数。
#include <stdio.h>
int main() {
char c; //定义字符变量
int uppercase = 0, lowercase = 0, digit = 0, space = 0, others = 0;//初始化计时器
//字符串大写字母,字符串小写字母,字符串数字,字符串空格,字符串其他字符
printf("请输入一行字符:\n");
while ((c = getchar()) != '\n') {
if (c >= 'A' && c <= 'Z') {
//大写字母
uppercase++;
}
else if (c >= 'a' && c <= 'z') {
//小写字母
lowercase++;
}
else if (c >= '0' && c <= '9') {
//数字
digit++;
}
else if(c==' ') {
//空格
space++;
}
else {
//其他字符
others++;
}
}
printf("英文字母个数:%d\n", uppercase + lowercase);
printf("空格个数:%d\n", space);
printf("数字个数:%d\n", digit);
printf("其他字符个数:%d\n", others);
printf("\n");
printf("大写字母个数:%d\n", uppercase);
printf("小写字母个数:%d\n", lowercase);
printf("数字个数:%d\n", digit);
printf("其他字符个数:%d\n", others);
return 0;
}
int uppercase = 0, lowercase = 0, digit = 0, space = 0, others = 0;//初始化计时器
在这段代码中,定义了五个整数变量:uppercase
、lowercase
、digit
、space
和 others
,并将它们初始化为0。这些变量通常用于计数目的,具体来说:
uppercase
:用于计数输入字符串中的大写字母数量。lowercase
:用于计数输入字符串中的小写字母数量。digit
:用于计数输入字符串中的数字字符数量。space
:用于计数输入字符串中的空格数量。others
:用于计数输入字符串中的其他字符(即不是大写字母、小写字母、数字或空格的字符)数量。
这段代码可能用于分析或处理输入的字符串,比如统计输入文本中的字母、数字和空格的数量。
在C语言中,
getchar()
是一个标准库函数,用于从标准输入(通常是键盘)读取一个字符。
c = getchar() != '\n'
是一个复合赋值表达式,分为两步:
getchar()
被调用,读取一个字符,并返回该字符的ASCII值。getchar() != '\n'
是一个比较表达式,检查返回的字符是否不等于换行符('\n')。如果
getchar()
返回的字符不是换行符,则getchar() != '\n'
的结果为true
,否则为false
。因此,
c = getchar() != '\n'
这行代码的意思是:如果getchar()
返回的字符不是换行符,则将true
赋值给变量c
。但这个赋值实际上并没有多大意义,因为
c
的值最终将是布尔值true
或false
,而不是原始字符值。这可能是一个编程错误或者不必要的操作。通常,你会直接用if
语句或者类似的逻辑结构来处理这种比较操作,而不是将结果赋值给一个变量。
实验六:输入两个正整数m和n,求其最大公约数和最小公倍数。
最大公约数是指两个或多个整数共有的最大的正整数,例如8和12的最大公约数为4。
最小公倍数是指两个或多个整数的最小公倍数,即除0以外最小的一个公倍数。例如6和24的最小公倍数为24。
#include <stdio.h>
// 计算最大公约数
int gcd(int a, int b) {
if (b == 0) {
return a;
}
return gcd(b, a % b);
}
// 计算最小公倍数
int lcm(int a, int b) {
return a * b / gcd(a, b);
}
int main() {
int m, n;
printf("请输入两个正整数m和n:");
scanf("%d %d", &m, &n);
int gcd_result = gcd(m, n); // 计算最大公约数
int lcm_result = lcm(m, n); // 计算最小公倍数
printf("最大公约数为:%d\n", gcd_result);
printf("最小公倍数为:%d\n", lcm_result);
return 0;
}
gcd
函数使用递归方式计算最大公约数,如果第二个参数为0,则返回第一个参数。否则,递归调用gcd
函数,将第二个参数和第一个参数除以第二个参数的余数作为新的参数传递。lcm
函数使用公式a * b / gcd(a, b)
计算最小公倍数。其中gcd(a, b)
表示a
和b
的最大公约数。- 在
main
函数中,首先提示用户输入两个正整数,然后使用scanf
函数读取输入。接着调用gcd
和lcm
函数计算最大公约数和最小公倍数,并将结果输出到屏幕上。
讨论
break语句和continue语句在循环结构中使用时有何区别?举例说明。
break
和 continue
都是C语言中用于控制循环结构的语句。它们在循环体中的使用有明显的区别。
-
break语句:
break
用于立即退出当前的循环,不管循环条件是否满足。- 当在循环中使用
break
时,它会立即终止当前的循环,并继续执行循环之后的代码。 - 通常用于在某些特定条件下中断循环。
- 示例:
int i; for (i = 0; i < 10; i++) { if (i == 5) { break; } printf("%d\n", i); }`
在上述代码中,当
i
等于5时,break
语句会终止for
循环。因此,输出结果为0到4。 -
continue语句:
continue
用于跳过当前循环的剩余部分,并进入下一次循环。- 当在循环中使用
continue
时,它会结束当前循环的迭代,并开始下一次迭代。 - 通常用于在某些特定条件下跳过当前循环的某些步骤。
int i; for (i = 0; i < 10; i++) { if (i == 5) { continue; } printf("%d\n", i); }`
在上述代码中,当
i
等于5时,continue
语句会跳过printf
语句,并进入下一次循环。因此,输出结果为0到9,但跳过了5。
总结:
break
和continue
在循环结构中的主要区别在于,break
完全终止了当前的循环,而continue
只跳过了当前迭代中的剩余部分。