学生成绩管系统开发随记

模块化程序设计

复杂的任务,划分为若干个简单的子任务,逐个完成。
自顶向下,逐步求精:首先写出结构简单的主程序段来表达整个问题,主程序段内的多个复杂子问题用子程序完成,直到每个程序都可以用高级语言表达出来。

两个要求:
高聚合:单个模块的功能越简单越好
低耦合:各个模块的关系越简单越好,保持独立

增量测试:

在软件测试过程中,根据现状先测试能测试的部分,将所有能测试的部分采用“一测到底”的方法,即测到无法再测,这种无法再测可能是因为系统开发只到这一步,或者是测试到某个错误不解决就无法继续测试,测试完一轮后,等待程序员修改更新,再进行测试,如此反复,直到系统能上线,暂时无法测出其他错误。从整体上看会成为一个大循环,而其中又包含N个小循环。

程序注释:

  1. 重要程序文件的首部添加注释
/*程序功能:   xxx
编码者:   xxx
日期:  xx/xx/xxxx
版本号:x.x
备注:*/
#include <stdio.h>
  1. 用户自定义函数前添加注释
/*
函数名称:   ReadScore
功能描述:   录入每个学生的学号和成绩,并返回总人数
参数:
    num[]:学号
    score[]:分数
返回:总人数
备注:*/
int ReadScore(long num[], float score[]);
  1. 重要的语句行,变量名,函数调用右方或结束处添加注释,解释作用
  2. 重要的语句块上方添加注释,解释功能

git使用

git工作流程

git工作流程

分支管理

分支管理

解决分支合并冲突

解决分支合并冲突

分支冲突在命令行使用merge命令后,直接回vs,可以直接可视化操作快速解决冲突合并。

push标签到远程

push标签到远程

搜到的教程讲push那里的不讲标签,讲标签那里不讲push…

字符串输入

因为vs19推荐用scanf_s函数,使用原版会报错,虽然可以关掉警报,但还是与时俱进好一点。
但是一直没有了解scanf_s与原版的区别,一直按照老用法在用,结果果然在字符串输入这里,一直报错,我还以为是格式字符串写的有问题,各种找解决方法,花了一个多小时一直不成功,很是挫败。
索性不写了,出去转了转。回来后,想着直接用原版scanf函数算了,果然一跑就成功了,所以不是格式字符串的问题,那就一定是scanf_s用的不对的问题。网上一查scanf_s的用法,果然在这里有差异。
scanf_s在输入字符串时必须指定字符串长度。改了一下终于完美跑起来了。

        //scanf("%ld%s%f", &num[i], name[i], &score[i]);//数值类型与字符串类型交叉输入可用空格隔开
        scanf_s("%ld%s%f", &num[i], name[i],10, &score[i]);//scanf_s函数在输入字符串时必须指定字符串长度

c语言if语句

不知道是不是我的误解,我以为if()语句括号里面大于0表示真,小于等于零表示假。结果今天试了一下,结果发现只有0和非零的区别。if(-1)也是会执行的。
看到有的解释为,逻辑运算时所有数都是当作无符号来看待的,因此if中的负数计算结果实际上是相当于强转成了unsigned int。
#函数指针就是函数名别名
首先明确函数名本质就是个地址,不过这个这个地址比较特殊。
程序运行时,需要把数据和代码加载到内存之中,所以可以大致分为数据区和代码区两部分。所以地址也分两种,比如数组名就属于数据区地址,而函数名属于代码区地址。而指针是纯粹的地址,可以被这两类地址赋值。
对数组名使用*符号,间接寻址可以操作地址上的数据,而对函数名*间接寻址可以找到代码,但运行中更改代码是非常危险的,经过实验,发现c语言也禁止了这一危险做法,你能得到只有代码区地址。

//函数本质是代码区地址,地址所指向的内容是代码区,使用*间接寻址也没用
	printf("函数的本质\n");
	printf("标识符		地址			地址所指向内容\n");
	printf("%s	%p		%d\n", "add", add, *add);
	printf("%s	%p		%d\n", "*add", *add, **add);

函数本质
这时再理解函数指针的使用就要容易很多了,如下图定义函数指针形参时,和我们定义指针时一样,使用(*compare)标明自己是个指针然后代替在函数名的位置。
接着使用此函数时,书上的用法是(*compare)(score[cmp], score[j]),而实际上使用compare(score[cmp], score[j])也完全没有问题,而且后一种用法更加体现出函数名实质上是代码区地址。而代码区地址使不使用*符号解引用没有任何区别。

void SortbyScore(long num[], float score[], int n, int(*compare)(int a, int b))
{
    long nTemp;
    float sTemp;
    //选择排序,第一层循环整个数组,每次归一位
    for (int i = 0; i < n - 1; i++)
    {
        int cmp = i;//比较位索引值,初始为乱序区第一位
        //第二层循环乱序位,每次从乱序列中选择最大一位
        for (int j = i; j < n; j++)
        {
            //if ((*compare)(score[cmp], score[j]))
            if (compare(score[cmp], score[j]))//函数名就是地址,所以直接使用指针也是正常工作
            {
                cmp = j;
            }
        }
        //发生改变,交换
        if (i != cmp)
        {
            LongSwap(&num[i], &num[cmp]);

            FloatSwap(&score[i], &score[cmp]);
        }
    }
}

调用这样使用函数指针形参的函数时,我们传入的也是函数名,其实传来传去就是地址而已。

SortbyScore(num, score, n, Ascending);

c程序的内存映像

c程序的内存映像
c程序内存大致分为三个部分,只读存储区,静态存储区,动态存储区。
其中动态存储区内存放局部变量,函数形参,返回数据等,动态存储区与其他存储区的交流,除了返回数据之外,就是利用指针穿插其中了,熟悉的数组和函数都是指针,变量名实际上也算是被绑定了的指针。

重写函数的步骤

  1. 复制函数声明
  2. 更改注释内容
  3. 更改函数形参
  4. 生成新的函数体
  5. 编写函数体
  6. 注释掉旧函数
  7. 根据报错把所有使用到的地方改成新的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值