C 重构过程代码时处理错误
我收到了一些基本上由一个大main()函数组成的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]
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可能是对的。有时引入一些函数只是为了给程序段命名是有用的。