Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/66.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 重构过程代码时处理错误_C_Error Handling_Refactoring_Procedural Programming - Fatal编程技术网

C 重构过程代码时处理错误

C 重构过程代码时处理错误,c,error-handling,refactoring,procedural-programming,C,Error Handling,Refactoring,Procedural Programming,我收到了一些基本上由一个大main()函数组成的C代码。我现在正试图将该方法展开为更小的函数,以使代码的意图更清晰。不过,我遇到了一些麻烦: void main(int argc, char *argv[]) { if(argc != 3) { printf("Usage: table-server <port> <n_lists>\n"); return; } int port = atoi(argv[1]

我收到了一些基本上由一个大main()函数组成的C代码。我现在正试图将该方法展开为更小的函数,以使代码的意图更清晰。不过,我遇到了一些麻烦:

void main(int argc, char *argv[])
{
    if(argc != 3)
    {
        printf("Usage: table-server <port> <n_lists>\n");
        return;
    }
    int port = atoi(argv[1]), n_lists = atoi(argv[2]);
    if(port < 1024 || port > 49151 || n_lists < 1)
    {
        printf("Invalid args.\n");
        return;
    }
    signal(SIGPIPE, SIG_IGN);
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    struct sockaddr_in s_addr;
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(port);
    s_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if(bind(sockfd, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0)
    {
        printf("(bind).\n");
        return;
    }
    if(listen(sockfd, SOMAXCONN) < 0)
    {
        printf("(listen).\n");
        return;
    }
打电话是这样的

if (verify_number_of_args(argc) == -1) return;
其实也没那么糟。现在,对于套接字来说,这会更麻烦,因为
sockfd
s_addr
都需要返回,加上状态返回值:

int sockfd;
struct sockaddr_in* s_addr;
if (create_socket(port, &sockfd, s_addr) == -1)
    return;
这样做违背了我尽量保持主要方法简单明了的目的。当然,我可以求助于
.c
文件中的全局变量,但这似乎不是一个好主意


在C语言中,您通常如何处理这类事情?

保持原样?我认为,在main的开头进行一些设置并不构成问题。在设置好之后开始重构。

保持原样?我认为,在main的开头进行一些设置并不构成问题。在设置好之后开始重构。

以下是简单的方法

参数解析和相关的错误检查是
main
关注的问题,所以除非
main
非常长,否则我不会将它们分开

实际工作,即程序的网络部分,可以拆分为一个与
main
非常类似的函数,但它需要正确解析和验证的参数:

int main(int argc, char *argv[])
{
    // handle arguments

    return serve(port, n_lists);
}

int serve(int port, int n_lists)
{
    // do actual work
}
至于错误处理:如果这段代码不是一个库,那么当函数中出现错误时,不管调用链有多深,只要杀死调用进程就可以了;这实际上是推荐的做法(Kernighan&Pike,编程实践)。只需确保在打印例程时考虑到实际的错误,例如

void error(char const *details)
{
    extern char const *progname;  // preferably, put this in a header

    fprintf(stderr, "%s: error (%s): %s\n", progname, details, strerror(errno));
    exit(1);
}
以获取一致的错误消息。(您可能希望在Linux和BSD上查看
err(3)
,并在其他平台上模拟该接口。)


您还可以尝试排除那些不会出错的操作,或者只是通过一些傻瓜式的设置调用一些系统调用,因为这些操作有利于易于重用的组件。

以下是简单的方法

参数解析和相关的错误检查是
main
关注的问题,所以除非
main
非常长,否则我不会将它们分开

实际工作,即程序的网络部分,可以拆分为一个与
main
非常类似的函数,但它需要正确解析和验证的参数:

int main(int argc, char *argv[])
{
    // handle arguments

    return serve(port, n_lists);
}

int serve(int port, int n_lists)
{
    // do actual work
}
至于错误处理:如果这段代码不是一个库,那么当函数中出现错误时,不管调用链有多深,只要杀死调用进程就可以了;这实际上是推荐的做法(Kernighan&Pike,编程实践)。只需确保在打印例程时考虑到实际的错误,例如

void error(char const *details)
{
    extern char const *progname;  // preferably, put this in a header

    fprintf(stderr, "%s: error (%s): %s\n", progname, details, strerror(errno));
    exit(1);
}
以获取一致的错误消息。(您可能希望在Linux和BSD上查看
err(3)
,并在其他平台上模拟该接口。)


您还可以尝试排除那些根本不会出错的操作,或者只是通过一些傻瓜式的设置调用一些系统调用,因为这些都是易于重用的组件。

这难道不是为了重构而重构的标志吗

无论如何,关于“让我们一次性初始化sockfd和s_addr”,您始终可以 创建结构,并向其传递指针:

struct app_ctx {
    int init_stage;
    int sock_fd;
    struct sockaddr_in myaddr;
    ...
}
然后将指向此结构实例的指针传递给所有“一次只做一件事”函数,并返回错误代码


在清理时,您执行相同的操作并传递相同的结构。

这难道不是为了重构而重构的标志吗

无论如何,关于“让我们一次性初始化sockfd和s_addr”,您始终可以 创建结构,并向其传递指针:

struct app_ctx {
    int init_stage;
    int sock_fd;
    struct sockaddr_in myaddr;
    ...
}
然后将指向此结构实例的指针传递给所有“一次只做一件事”函数,并返回错误代码


在清理时,您将执行相同的操作并传递相同的结构。

我添加了一个“错误处理”标记并编辑了标题。在StackOverflow中搜索
[error handling][c]
。我添加了一个“error handling”标签并编辑了标题。在StackOverflow中搜索
[错误处理][c]
。这不是为了重构。这个函数有150行长,基本上是交织在一起的ifs和ELSE。我认为@Ownedelysium可能是对的。有时引入一些函数只是为了给程序段命名是有用的,这不是为了重构。这个函数有150行长,基本上是交织在一起的ifs和ELSE。我认为@Ownedelysium可能是对的。有时引入一些函数只是为了给程序段命名是有用的。