C编程:如何避免代码重复而又不失清晰

C编程:如何避免代码重复而又不失清晰,c,code-duplication,deduplication,C,Code Duplication,Deduplication,编辑:感谢所有回复者。我应该在我的原始帖子中提到,我不允许更改这些函数的任何规范,因此使用断言和/或允许取消引用NULL的解决方案是不可能的。 考虑到这一点,我认为要么使用函数指针,要么保持复制不变。为了清楚起见,这次我想避免使用函数指针 原件: 我试图避免代码重复而不失清晰。 通常,当我从事一项特定的任务(大学本科)时,我认识到这些函数返回的模式,但并不总是有“出色的工作”解决方案 对于这三个C函数,你们中的任何人都会建议我应该做什么(指向函数、宏等的指针),用同样的方法检查它们的一些参数,以

编辑:感谢所有回复者。我应该在我的原始帖子中提到,我不允许更改这些函数的任何规范,因此使用断言和/或允许取消引用NULL的解决方案是不可能的。 考虑到这一点,我认为要么使用函数指针,要么保持复制不变。为了清楚起见,这次我想避免使用函数指针

原件: 我试图避免代码重复而不失清晰。 通常,当我从事一项特定的任务(大学本科)时,我认识到这些函数返回的模式,但并不总是有“出色的工作”解决方案

对于这三个C函数,你们中的任何人都会建议我应该做什么(指向函数、宏等的指针),用同样的方法检查它们的一些参数,以使检查更模块化(应该更模块化,对吗?)

顺便说一句,这些都是直接从硬件分配中获取的,因此它们的功能细节与我的问题无关,只与函数顶部的参数检查有关

 teamIsDuplicateCoachName(Team team, bool* isDuplicate) {
    TeamResult result = TEAM_SUCCESS;
    if (!team || !isDuplicate) {
        result = TEAM_NULL_ARGUMENT;
    } else if (teamEmpty(team)) {
        result = TEAM_IS_EMPTY;
    } else {
        for (int i = 0; i < team->currentFormations; ++i) {
            if (teamIsPlayerInFormation(team->formations[i], team->coach)) {
                *isDuplicate = true;
                break;
            }
        }
    }
    return result;
}

TeamResult teamGetWinRate(Team team, double* winRate) {
    TeamResult result = TEAM_SUCCESS;
    if (!team || !winRate) {
        result = TEAM_NULL_ARGUMENT;
    } else {
        int wins = 0, games = 0;
        for (int i = 0; i < team->currentFormations; ++i) {
            Formation formation = team->formations[i];
            if (formationIsComplete(formation)) {
                games += formation->timesPlayed;
                wins += formation->timesWon;
            }
        }
        double win = ( games == 0 ) ? 0 : (double) wins / games;
        assert(win >= 0 && win <= 1);
        *winRate = win;
    }
    return result;
}



TeamResult teamGetNextIncompleteFormation(Team team, Formation* formation,
        int* index) {
    TeamResult result = TEAM_SUCCESS;
    if (!team || !formation || !index) {
        result = TEAM_NULL_ARGUMENT;
    } else {
        *formation = NULL; /* default result, will be returned if there are no incomplete formations */
        for (int i = 0; i < team->currentFormations; ++i) {
            Formation formationPtr = team->formations[i];
            if (!formationIsComplete(formationPtr)) {
                *formation = formationPtr;
                *index = i;
                break;
            }
        }
    }
    return result;
}
teamisduplicateCachName(teamteam,bool*isDuplicate){
TeamResult结果=团队成功;
如果(!team | |!isDuplicate){
结果=团队\空\参数;
}else if(teamEmpty(团队)){
结果=团队为空;
}否则{
对于(inti=0;icurrentFormations;++i){
if(团队球员信息(团队->队形[i],团队->教练)){
*isDuplicate=真;
打破
}
}
}
返回结果;
}
TeamResult teamGetWinRate(团队,双倍*winRate){
TeamResult结果=团队成功;
如果(!团队| |!获胜){
结果=团队\空\参数;
}否则{
整胜=0,比赛=0;
对于(inti=0;icurrentFormations;++i){
队形=团队->队形[i];
如果(形成完成(形成)){
游戏+=编队->游戏时间;
wins+=编队->赢得时间;
}
}
双赢=(游戏==0)?0:(双)赢/游戏;
断言(win>=0&&win currentFormations;++i){
编队编队PTR=团队->编队[i];
如果(!formationIsComplete(formationPtr)){
*形成=形成ptr;
*指数=i;
打破
}
}
}
返回结果;
}
任何关于如何(具体地)避免代码重复的建议都将不胜感激


谢谢你抽出时间!:)

如果您担心在每个函数开始时重复空检查,我不会担心。它向用户清楚地表明,您只是在做任何工作之前进行输入验证。不用担心这几行


一般来说,不要担心这样的小事。

我会创建一个函数来检查团队对象:

TeamResult TeamPtrCheck(Team *team)
{
    if (team == NULL)
        return TEAM_NULL_ARGUMENT;
    else if (teamEmpty(team))
        return TEAM_IS_EMPTY;
    else
        return TEAM_SUCCESS;
}
然后在每个函数的顶部引用它+其他检查,例如

TeamResult = TeamPtrCheck(team);
if (TeamResult != TEAM_SUCCESS)
    return TeamResult;
if (winRate == NULL)
    return TEAM_NULL_ARGUMENT;

否则,如果每个函数都不同,则将检查保留为不同

有几种技术可以减少您所感觉到的冗余,哪种技术适用在很大程度上取决于您正在检查的条件的性质。在任何情况下,我建议不要使用任何(预处理器)技巧来减少重复,因为重复会隐藏实际发生的事情

如果您有一个不应该发生的条件,一种简单的检查方法是使用断言。对于断言,您基本上会说:这个条件必须为真,否则我的代码有错误,请检查我的假设是否为真,如果不是,请立即杀死我的程序。这通常是这样使用的:

#include <assert.h>

void foo(int a, int b) {
    assert((a < b) && "some error message that should appear when the assert fails (a failing assert prints its argument)");
    //do some sensible stuff assuming a is really smaller than b
}
TeamResult teamIsDuplicateCoachName(Team team, bool* isDuplicate) {
    if(!team || !isDuplicate) return TEAM_NULL_ARGUMENT;
    if(teamEmpty(team)) return TEAM_IS_EMPTY;

    for (int i = 0; i < team->currentFormations; ++i) {
        if (teamIsPlayerInFormation(team->formations[i], team->coach)) {
            *isDuplicate = true;
            break;
        }
    }
    return TEAM_SUCCESS;
}
这是毫无意义的,因为取消对空指针的引用将在任何sane平台上安全地隔离您的程序,因此以下操作将同样安全地停止您的程序:

void foo(int* bar) {
    *bar = 3;
}
语言律师可能对我说的不满意,因为根据标准,取消对空指针的引用是未定义的行为,从技术上讲,编译器可以生成格式化硬盘的代码。但是,取消对空指针的引用是一种常见错误,您可以期望编译器不会对其执行愚蠢的操作,并且您可以期望您的系统会特别小心,以确保在尝试执行此操作时硬件会发出尖叫声。这个硬件检查是免费的,断言需要几个周期来检查

然而,assert(和segfaulting空指针)仅适用于检查致命条件。如果您只是检查一个条件,使得函数中的任何进一步工作都毫无意义,我会毫不犹豫地使用提前返回。它通常更具可读性,尤其是因为语法高亮显示可以向读者轻松地显示返回语句:

void foo(int a, int b) {
    if(a >= b) return;
    //do something sensible assuming a < b
}
void foo(int a,int b){
如果(a>=b)返回;
//假设a
使用此范例,您的第一个函数如下所示:

#include <assert.h>

void foo(int a, int b) {
    assert((a < b) && "some error message that should appear when the assert fails (a failing assert prints its argument)");
    //do some sensible stuff assuming a is really smaller than b
}
TeamResult teamIsDuplicateCoachName(Team team, bool* isDuplicate) {
    if(!team || !isDuplicate) return TEAM_NULL_ARGUMENT;
    if(teamEmpty(team)) return TEAM_IS_EMPTY;

    for (int i = 0; i < team->currentFormations; ++i) {
        if (teamIsPlayerInFormation(team->formations[i], team->coach)) {
            *isDuplicate = true;
            break;
        }
    }
    return TEAM_SUCCESS;
}
TeamResult teamisduplicateCachName(Team Team,bool*isDuplicate){
如果(!team | | |!isDuplicate)返回team _NULL_参数;
如果(teamEmpty(team))返回team_为空;
对于(inti=0;icurrentFormations;++i){
if(团队球员信息(团队->队形[i],团队->教练)){
*isDuplicate=真;
打破
}
}
回报团队的成功;
}

我相信,这个版本比在主体周围有if的版本更加清晰和简洁。

这或多或少是一个设计问题。如果上面的函数都是静态函数(或者只有一个是extern),那么整个“函数包”应该为每个对象检查一次条件-执行流-并让低级函数的实现细节假设输入数据有效

例如,如果返回创建
团队的位置