Pixiv - 蒼ஐ/お仕事募集中
2960 字
15 分钟
2025.11.04 C语言程序设计上机实习三
2025.11.04 C语言程序设计上机实习三
函数的基本定义与调用
程序的完整源代码
#include <stdio.h>#include <math.h>
// 定义圆周率常量,通过反余弦函数计算获得精确值const double pi = acos(-1);
/***计算圆的面积r 圆的半径return 计算得到的圆的面积***/double CircleArea(double r);
/***计算矩形的面积l 矩形的长度w 矩形的宽度return 计算得到的矩形的面积***/double RectangleArea(double l, double w);
/***打印圆和矩形的面积circle 圆的面积rect 矩形的面积return 无返回值***/void PrintAreas(double circle, double rect);
int main(void){ // 初始化变量:圆的半径为5.0,矩形的长为4.0、宽为3.0 double radius=5.0, length=4.0, width=3.0; // 声明变量用于存储计算得到的面积 double circle, rect;
// 调用CircleArea函数计算圆的面积 // 参数传递过程:将radius作为实参传递给形参r,函数内部使用r计算面积后返回结果赋值给circle circle = CircleArea(radius);
// 调用RectangleArea函数计算矩形的面积 // 参数传递过程:将length和width作为实参分别传递给形参l和w,函数内部使用l和w计算面积后返回结果赋值给rect rect = RectangleArea(length, width);
// 调用PrintAreas函数打印面积结果 // 参数传递过程:将circle和rect作为实参分别传递给形参circle和rect,函数内部使用这两个参数进行格式化输出 PrintAreas(circle, rect);
return 0;}
/***计算圆的面积r 圆的半径计算得到的圆的面积,计算公式:π * r * r***/double CircleArea(double r){ double result; // 声明变量存储计算结果 result = pi * r * r; // 使用圆面积公式计算 return result; // 返回计算得到的面积}
/***计算矩形的面积l 矩形的长度w 矩形的宽度return 计算得到的矩形的面积,计算公式:l * w */double RectangleArea(double l, double w){ double result; // 声明变量存储计算结果 result = l * w; // 使用矩形面积公式计算 return result; // 返回计算得到的面积}
/***打印圆和矩形的面积circle 圆的面积,以10位小数精度输出rect 矩形的面积,以2位小数精度输出return 无返回值***/void PrintAreas(double circle, double rect){ // 格式化输出两个面积值,圆面积保留10位小数,矩形面积保留2位小数 printf("Circle Area: %.10f, Rectangle Area: %.2f\n", circle, rect);}程序的实际运行输出
s13665@ubuntu:~/codes/20251104$ gcc 1.c -lm1.c:4:19: warning: initializer element is not a constant expression const double pi = acos(-1); ^s13665@ubuntu:~/codes/20251104$ ./a.outCircle Area: 78.5398163397, Rectangle Area: 12.00程序流程图

参数传递与变量作用域
程序的完整源代码
#include <stdio.h>
// 函数声明int ProcessData(int); // 声明值传递函数(处理数据并返回结果)void UpdateValues(int *, int *); // 声明地址传递函数(通过指针修改外部变量)void UpdateValuesFail(int, int); // 声明值传递函数(尝试修改但无法影响外部变量)
int main() { int a = 10, b = 20; int result = 0;
result = ProcessData(a); // 将a的值(10)传入,接收返回值(15) ProcessData(b); // 将b的值(20)传入,返回值(25)未使用 printf("First: a=%d, b=%d, result=%d\n", a, b, result); // a和b仍为初始值
UpdateValues(&a, &b/* 传入a和b的地址,允许函数修改原变量 */); printf("Second: a=%d, b=%d\n", a, b); // a和b已被修改
UpdateValuesFail(a, b/* 传入当前a和b的值,函数内修改不影响外部 */); printf("Third: a=%d, b=%d\n", a, b); // a和b保持修改后的值(未受影响)
return 0;}
int ProcessData(int num) // num是传入参数的副本(如a的副本为10){ int result; result = num + 5; // 仅修改函数内,不影响原变量(如a仍为10) return result; // 返回处理后的结果}
void UpdateValues(int *t1, int *t2) // t1指向a的地址,t2指向b的地址{ *t1 += 5; // 通过指针解引用(*t1)直接修改a的实际值(a = a + 5) *t2 += 10; // 通过指针解引用(*t2)直接修改b的实际值(b = b + 10)}
void UpdateValuesFail(int t1, int t2) // t1是a的副本,t2是b的副本{ t1 += 5; // 仅修改函数内t1,原变量a不受影响 t2 += 10; // 仅修改函数内t2,原变量b不受影响}- 值传递与地址传递的区别
- 值传递(如函数==ProcessData==和==UpdateValuesFail==):
- 函数接收的是实参的副本(即复制一份变量的值传入函数)。
- 函数内部对参数的修改仅作用于函数内的副本,不会影响外部原变量的值。
- 地址传递(如==UpdateValues==):
- 函数接收的是实参的内存地址(通过指针传递)。
- 函数内部通过指针解引用可以直接访问并修改原变量的内存空间,因此会影响外部原变量。
- 指针操作的实际效果 指针变量存储的是内存地址。 通过&变量名可以获取变量的地址(如&a表示变量 a 的内存地址)。 通过*指针名(解引用操作)可以访问指针指向的内存空间: 在UpdateValues中,*t1 += 5产生了如下效果:找到 t1 所指向的内存(即 a 的地址),将该地址中的值加 5,最终直接修改了原变量 a。 指针的核心作用是间接访问内存,实现函数对外部变量的修改,突破值传递 “仅操作副本” 的限制。
程序的实际运行输出
s13665@ubuntu:~/codes/20251104$ ./a.outFirst: a=10, b=20, result=15Second: a=15, b=30Third: a=15, b=30用Debug工具调试和分析代码的过程
设置断点
与上方所示代码相对应
s13665@ubuntu:~/codes/20251104$ gdb a.outGNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1Copyright (C) 2016 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from a.out...done.(gdb) list1 #include <stdio.h>23 // 函数声明4 int ProcessData(int); // 声明值传递函数(处理数据并返回结果)5 void UpdateValues(int *, int *); // 声明地址传递函数(通过指针修改外部变量)6 void UpdateValuesFail(int, int); // 声明值传递函数(尝试修改但无法影响外部变量)78 int main() {9 int a = 10, b = 20;10 int result = 0;(gdb)1112 result = ProcessData(a); // 将a的值(10)传入,接收返回值(15)13 ProcessData(b); // 将b的值(20)传入,返回值(25)未使用14 printf("First: a=%d, b=%d, result=%d\n", a, b, result); // a和b仍为初始值1516 UpdateValues(&a, &b/* 传入a和b的地址,允许函数修改原变量 */);17 printf("Second: a=%d, b=%d\n", a, b); // a和b已被修改1819 UpdateValuesFail(a, b/* 传入当前a和b的值,函数内修改不影响外部 */);20 printf("Third: a=%d, b=%d\n", a, b); // a和b保持修改后的值(未受影响)(gdb) break 12Breakpoint 1 at 0x4005c2: file 2_comment.c, line 12.(gdb) break 13Breakpoint 2 at 0x4005cf: file 2_comment.c, line 13.(gdb) break 16Breakpoint 3 at 0x4005f3: file 2_comment.c, line 16.(gdb) break 19Breakpoint 4 at 0x40061d: file 2_comment.c, line 19.(gdb) break ProcessDataBreakpoint 5 at 0x400665: file 2_comment.c, line 28.(gdb) break UpdateValuesBreakpoint 6 at 0x40067f: file 2_comment.c, line 34.(gdb) break UpdateValuesFailBreakpoint 7 at 0x4006aa: file 2_comment.c, line 40.开始调试
执行run开始调试
打印a与a的地址
(gdb) runStarting program: /home/s13665/codes/20251104/a.out
Breakpoint 1, main () at 2_comment.c:1212 result = ProcessData(a); // 将a的值(10)传入,接收返回值(15)(gdb) print a$1 = 10(gdb) print &a$2 = (int *) 0x7fffffffe4dc执行 step 进入 ProcessData 函数,打印形参及形参的地址
(gdb) step
Breakpoint 5, ProcessData (num=10) at 2_comment.c:2828 result = num + 5; // 仅修改函数内,不影响原变量(如a仍为10)(gdb) print num$3 = 10(gdb) print &num$4 = (int *) 0x7fffffffe4ac继续执行 continue,打印b与b的地址
(gdb) continueContinuing.
Breakpoint 2, main () at 2_comment.c:1313 ProcessData(b); // 将b的值(20)传入,返回值(25)未使用(gdb) print b$5 = 20(gdb) print &b$6 = (int *) 0x7fffffffe4e0执行 step 进入 ProcessData 函数,打印形参及形参的地址
(gdb) step
Breakpoint 5, ProcessData (num=20) at 2_comment.c:2828 result = num + 5; // 仅修改函数内,不影响原变量(如a仍为10)(gdb) print num$7 = 20(gdb) print &num$8 = (int *) 0x7fffffffe4ac继续执行 continue,打印实参 a与a的地址 b与b的地址
(gdb) continueContinuing.First: a=10, b=20, result=15
Breakpoint 3, main () at 2_comment.c:1616 UpdateValues(&a, &b/* 传入a和b的地址,允许函数修改原变量 */);(gdb) print a$9 = 10(gdb) print &a$10 = (int *) 0x7fffffffe4dc(gdb) print b$11 = 20(gdb) print &b$12 = (int *) 0x7fffffffe4e0执行 step 进入 UpdateValues 函数,打印形参及形参的地址
(gdb) step
Breakpoint 6, UpdateValues (t1=0x7fffffffe4dc, t2=0x7fffffffe4e0) at 2_comment.c:3434 *t1 += 5; // 通过指针解引用(*t1)直接修改a的实际值(a = a + 5)(gdb) print t1$13 = (int *) 0x7fffffffe4dc(gdb) print &t1$14 = (int **) 0x7fffffffe4b8(gdb) print t2$15 = (int *) 0x7fffffffe4e0(gdb) print &t2$16 = (int **) 0x7fffffffe4b0继续执行 continue,打印实参 a与a的地址 b与b的地址
(gdb) continueContinuing.Second: a=15, b=30
Breakpoint 4, main () at 2_comment.c:1919 UpdateValuesFail(a, b/* 传入当前a和b的值,函数内修改不影响外部 */);(gdb) print a$17 = 15(gdb) print &a$18 = (int *) 0x7fffffffe4dc(gdb) print b$19 = 30(gdb) print &b$20 = (int *) 0x7fffffffe4e0执行 step 进入 UpdateValuesFail 函数,打印形参及形参的地址
(gdb) step
Breakpoint 7, UpdateValuesFail (t1=15, t2=30) at 2_comment.c:4040 t1 += 5; // 仅修改函数内t1,原变量a不受影响(gdb) print t1$21 = 15(gdb) print &t1$22 = (int *) 0x7fffffffe4bc(gdb) print t2$23 = 30(gdb) print &t2$24 = (int *) 0x7fffffffe4b8为什么要打印这些值或地址?
-
验证值传递的特性:
- 对于
ProcessData和UpdateValuesFail,形参(num、t1、t2)是实参的副本,打印形参和实参的地址可证明两者存储在不同内存位置(地址不同),以此来证明函数内修改形参并不会影响实参。 - 打印值可验证形参初始值与实参一致(副本正确),但修改后形参与实参值不同,以此来说明“变量作用域”。
- 对于
-
验证地址传递的特性:
- 在函数
UpdateValues中,实参是&a和&b(地址),形参t1和t2是指针,打印形参t1、t2的值可证明它们确实指向实参a和b(t1 == &a,t2 == &b)。 - 结合后续对
*t1、*t2指针指向的内存的修改,可验证通过指针解引用能直接操作实参内存,以此来解释为何实参值会被函数修改。
- 在函数
-
理解函数调用时的参数传递机制:
- 值传递:实参值的副本给形参
- 地址传递:实参地址的副本给形参指针
程序的部分内存图及其解释

UpdateValues函数中t1、t2均为指针变量,存储实参a、b的地址。
UpdateValuesFail函数中t1、t2均为整型变量,先存储实参a、b原来的值,而后分别+5与+10,所得结果仍存储在形参t1、t2,并没有对实参a、b进行改变。
多函数协作与模块化设计
程序的完整源代码
#include <stdio.h>
//函数声明double CalculateAverage(double s1, double s2, double s3);char GetGrade(double avg);void PrintReport(int id, double avg, char grade);
int main() {
int id = 1001; double score1 = 85.5, score2 = 92.0, score3 = 78.5;
// 计算平均分 double avg = CalculateAverage(score1, score2, score3);
// 获取等级 char grade = GetGrade(avg);
// 输出成绩报告 PrintReport(id, avg, grade);
return 0;}
// 计算三门课平均分double CalculateAverage(double s1, double s2, double s3) { return (s1 + s2 + s3) / 3.0;}
// 根据平均分返回等级,使用?: 运算符char GetGrade(double avg) { return (avg >= 90.0) ? 'A' : (avg >= 80.0) ? 'B' : (avg >= 70.0) ? 'C' : 'D';}
// 输出成绩报告void PrintReport(int id, double avg, char grade) { printf("ID:%d, Average:%.2f, Grade:%c\n", id, avg, grade);}程序的实际运行输出
s13665@ubuntu:~/codes/20251104$ ./a.outID:1001, Average:85.33, Grade:B文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
2025.11.04 C语言程序设计上机实习三
https://mjy.js.org/posts/20251104-c语言程序设计上机实习三/