循环例题:C 语言解决 “数位之和” 问题讲义

数位之和_牛客题霸_牛客网

一、题目解析

1. 题目要求

给定一个整数,计算其所有数位之和。若该整数为负数,需先取其绝对值再进行计算。

2. 输入输出描述

  • 输入:在一行中输入一个整数,整数的取值范围未明确限制,但需满足常规整数存储范围。
  • 输出:输出一个整数,表示输入整数的所有数位之和。

3. 示例分析

示例:输入为 12,12 是正整数,直接将各位数字相加,1+2=3,所以输出 3

二、解题思路

要计算一个整数的数位之和,核心思路是将整数的每一位数字分离出来,然后进行累加。具体步骤如下:

  1. 分离每一位数字:通过循环,利用取余运算(%10)获取整数的最后一位数字,例如对于整数 123,123%10=3,即可得到最后一位数字 3。
  2. 累加数字:将每次分离出的最后一位数字累加到一个总和变量中。
  3. 去除已处理的位:通过整除运算(/10)将整数的最后一位去除,例如 123/10=12,这样下一次循环就能处理新的最后一位数字 2。
  4. 循环终止条件:当整数经过多次整除运算后变为 0 时,说明所有数位都已处理完毕,循环结束,此时总和变量中存储的就是该整数的数位之和。

三、完整代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>

int main()
{
int a = 0;
int b = 0;
int c = 0;

scanf("%d", &a);

for (; a != 0;)
{
b = a % 10;
a /= 10;
c += b;
}

printf("%d\n", c);
return 0;
}

数组例题:矩阵转置

矩阵转置_牛客题霸_牛客网

一、核心概念:什么是矩阵转置

矩阵转置就是把原矩阵的”行”和”列”互换。比如原矩阵是 2行3列,转置后变成 3行2列;原矩阵中第 i行第j列 的数字,转置后会出现在新矩阵的 第j行第i列。

举个例子:

原矩阵(2行3列):

1
2
1 2 3 // 第1行
4 5 6 // 第2行

转置后(3行2列):

1
2
3
1 4 // 原矩阵第1列的两个数
2 5 // 原矩阵第2列的两个数
3 6 // 原矩阵第3列的两个数

二、题目要求拆解

1. 输入规则

第一步:先输入两个整数 n 和 m(n 是原矩阵行数,m 是原矩阵列数),用空格分隔。

第二步:接着输入 n 行数据,每行有m个整数(用空格分隔),这就是原矩阵的内容。

2. 输出规则

输出转置后的矩阵,共m行(原矩阵的列数变行数),每行有 n 个整数(原矩阵的行数变列数)。

每个整数后面必须跟一个空格,每行结束后换行。

三、解题思路

分3步完成,核心是”先存原矩阵,再按转置顺序输出”:

  1. 定义变量和数组:用二维数组存储原矩阵(题目中矩阵规模小,定义 10x10的数组足够用),再定义循环变量 i(控制行)和 j(控制列)。
  2. 读取输入数据:先读 n 和 m,再用双重循环读n行、每行m个数字,存入二维数组。
  3. 转置输出:再用双重循环,按”原矩阵的列优先”输出——外层循环遍历原矩阵的列(从0到 m-1),内层循环遍历原矩阵的行(从0到 n-1),这样就能把原矩阵的”列”变成新矩阵的”行”。

四、完整代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>

int main() {
// 1. 定义变量:n=原行数,m=原列数;arr[10][10]存原矩阵;i/j是循环变量
int n = 0, m = 0;
int arr[10][10] = {0}; // 初始化数组,避免随机值
int i = 0, j = 0;

// 2. 读取输入:先读n和m,再读原矩阵数据
scanf("%d %d", &n, &m); // 读行数和列数

for (i = 0; i < n; i++) { // 遍历原矩阵的每一行(共n行)
for (j = 0; j < m; j++) { // 遍历当前行的每一列(共m列)
scanf("%d", &arr[i][j]); // 把数字存入数组对应位置
}
}

// 3. 转置输出:原列变新行,原行变新列
for (i = 0; i < m; i++) { // 外层循环:遍历原矩阵的列(转置后变成行,共m行)
for (j = 0; j < n; j++) { // 内层循环:遍历原矩阵的行(转置后变成列,共n列)
printf("%d ", arr[j][i]); // 输出原矩阵第j行第i列的数(转置后的位置)
}
printf("\n"); // 每输出一行(原矩阵的一列),换行
}

return 0;
}

函数详解

一、函数的核心概念

函数是C语言中过程封装的核心载体,它将实现特定功能的代码块封装成独立模块,可被重复调用,是结构化程序设计的基本单元。

一个C程序由一个主函数(main函数)和若干个自定义函数组成,main函数是程序的唯一入口,程序从main函数开始执行,其他函数需被调用才能执行。

函数的核心价值体现在三点:

  1. 代码复用:避免重复编写相同功能代码,如多次计算圆面积时,只需编写一次计算函数并反复调用。
  2. 模块化编程:将复杂程序拆解为多个小功能函数,如学生成绩管理系统可拆分为成绩录入、计算、查询等函数,结构更清晰。
  3. 降低维护成本:修改功能时只需调整对应函数,无需改动整个程序,例如修改排序逻辑仅需更新排序函数。

二、函数的定义

1. 定义语法结构

1
2
3
4
5
6
7
返回值类型 函数名(形式参数列表) {
// 函数体:实现功能的代码
语句1;
语句2;
...
[return 返回值;] // 非void类型必须有return
}
  • 返回值类型:指定函数执行后返回数据的类型,如int(整型)、float(浮点型);若无需返回值,需指定为void。
  • 函数名:遵循标识符规则(字母/下划线开头,含字母、数字、下划线),需”见名知意”,如calculateSum表示求和函数。
  • 形式参数列表(形参):接收外部传入的数据,格式为”类型 变量名”,多个参数用逗号分隔;无参数时可写void或空括号。
  • 函数体:用{}包裹的代码块,包含变量声明、逻辑运算等语句,实现函数核心功能。
  • return语句:将结果返回给调用者,非void类型函数必须包含;void类型可省略,或仅写return;用于提前结束函数。

2. 典型定义示例

示例1:无参无返回值函数(输出欢迎信息)

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

// 定义无参、返回值为void的函数
void printWelcome() {
printf("欢迎学习C语言函数!\n");
}

int main() {
printWelcome(); // 调用函数
return 0;
}

示例2:有参有返回值函数(计算两数之和)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

// 定义两个int型参数、返回int型结果的函数
int calculateSum(int num1, int num2) {
int sum = num1 + num2;
return sum; // 返回计算结果
}

int main() {
int a = 5, b = 3;
int result = calculateSum(a, b); // 调用函数,接收返回值
printf("两数之和:%d\n", result);
return 0;
}

三、函数的调用与声明

1. 函数调用

调用语法

1
函数名(实际参数列表);
  • 实际参数(实参):传递给形参的数据,需与形参的数量、类型、顺序完全匹配,可是常量、变量或表达式,如calculateSum(2+3, 5)。

调用场景:

  • 无返回值函数:作为独立语句调用,如printWelcome();。
  • 有返回值函数:可赋值给变量或直接参与运算,如int max = getMax(10, 20);或printf(“最大值:%d”, getMax(10, 20));。

调用过程

  1. 参数传递:将实参的值复制给形参(默认值传递)。
  2. 执行函数体:程序跳转至函数,依次执行代码。
  3. 返回结果:若有返回值,将结果带回调用处;无返回值则直接回到调用处继续执行。

2. 函数声明

当函数定义在调用之后时,需提前声明函数,告知编译器函数的”签名”(返回值类型、函数名、参数列表),否则编译器会报错。

声明语法

1
返回值类型 函数名(形式参数列表);

与函数定义的区别:无函数体,末尾加**;**,参数列表可仅写类型(建议写参数名以增强可读性)。

声明示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

// 函数声明(参数列表可简化为类型)
int calculateMax(int x, int y); // 完整声明
// 或 int calculateMax(int, int); // 简化声明

int main() {
int a = 10, b = 15;
int maxVal = calculateMax(a, b); // 调用声明后的函数
printf("最大值:%d\n", maxVal);
return 0;
}

// 函数定义(在调用之后)
int calculateMax(int x, int y) {
return x > y ? x : y; // 三目运算符返回最大值
}

四、函数的参数传递

C语言中参数传递有两种核心方式:值传递和地址传递(通过指针实现),核心区别在于是否能修改实参的值。

1. 值传递(默认方式)

原理:将实参的值复制一份给形参,形参和实参占用不同内存空间,形参的修改不会影响实参。

示例:尝试交换两数(值传递失效)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

// 值传递:形参x、y是实参的副本
void swap(int x, int y) {
int temp = x;
x = y;
y = temp;
printf("函数内:x=%d, y=%d\n", x, y);
}

int main() {
int a = 3, b = 5;
printf("交换前:a=%d, b=%d\n", a, b);
swap(a, b); // 传递a、b的值
printf("交换后:a=%d, b=%d\n", a, b); // 实参未改变
return 0;
}

运行结果:

1
2
3
交换前:a=3, b=5
函数内:x=5, y=3
交换后:a=3, b=5

2. 地址传递(指针传递)

原理:将实参的内存地址传递给形参(形参为指针类型),形参指向实参的内存空间,修改形参指向的内容即修改实参。

示例:实现两数交换(地址传递生效)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

// 地址传递:形参x、y是指向实参的指针
void swap(int *x, int *y) {
int temp = *x; // *x表示访问x指向的内存值(实参a)
*x = *y; // 将y指向的值(实参b)赋给x指向的内存
*y = temp; // 将temp的值赋给y指向的内存(实参b)
printf("函数内:*x=%d, *y=%d\n", *x, *y);
}

int main() {
int a = 3, b = 5;
printf("交换前:a=%d, b=%d\n", a, b);
swap(&a, &b); // 传递a、b的地址(&是取地址符)
printf("交换后:a=%d, b=%d\n", a, b); // 实参被修改
return 0;
}

运行结果:

1
2
3
交换前:a=3, b=5
函数内:*x=5, *y=3
交换后:a=5, b=3

五、变量的作用域与存储类型

1. 局部变量与全局变量

局部变量

  • 定义位置:函数内部或代码块(如if、for内部)。
  • 作用域:仅在定义它的函数/代码块内有效,外部无法访问。
  • 生命周期:函数调用时分配内存,函数结束后释放,再次调用重新初始化(值不保留)。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

int sum(int m, int n) {
int i, sum = 0; // i、sum是局部变量,仅在sum函数内有效
for (i = m; i <= n; i++) {
sum += i;
}
return sum;
}

int main() {
int begin = 1, end = 100;
printf("1到100的和:%d\n", sum(begin, end));
// printf("%d", i); // 报错:i是sum函数的局部变量,main中不可见
return 0;
}

全局变量

  • 定义位置:所有函数外部。
  • 作用域:从定义处开始,到整个源文件结束,所有后续函数均可访问。
  • 生命周期:程序运行期间始终存在,内存空间固定,值可长期保留。

注意:全局变量会破坏函数独立性,增加代码耦合度,建议尽量少用。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

int max, min; // 全局变量,所有函数可访问

void findMaxMin(int arr[], int len) {
max = arr[0];
min = arr[0];
for (int i = 1; i < len; i++) {
if (arr[i] > max) max = arr[i];
if (arr[i] < min) min = arr[i];
}
}

int main() {
int data[] = {89, 43, 14, 8, 94};
findMaxMin(data, 5);
printf("最大值:%d,最小值:%d\n", max, min); // 直接访问全局变量
return 0;
}

运行结果:

1
最大值:94,最小值:8

章节习题:求阶乘(函数)

求阶乘_牛客题霸_牛客网

题目要求

定义函数计算n的阶乘(n为非负整数,0! = 1,n! = n×(n-1)×…×1),主函数输入n并输出结果。

解题思路

  1. 分情况处理:n=0时返回1;n≥1时通过循环累乘计算阶乘。
  2. 函数factorial接收int型参数n,返回long long型(避免n较大时整数溢出)。

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

// 计算n的阶乘,返回long long避免溢出
long long factorial(int n) {
if (n == 0) {
return 1; // 0! = 1
}

long long res = 1;
for (int i = 1; i <= n; i++) {
res *= i;
}

return res;
}

int main() {
int n;
scanf("%d", &n);
printf("%lld\n", factorial(n));
return 0;
}