我收到了一些基本上由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;
}
我可以在此代码的函数中找出4个主要问题:
尝试将其重构为小函数时的问题主要与错误处理有关。例如,r试图提取1的逻辑看起来像这样:
int verify_number_of_args(int argc) {
if (argc != 3) {
printf("...");
return -1;
}
return 0;
}
并且调用它将是这样的
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中处理这类事情?
答案 0 :(得分: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&amp; Pike, The Programming of Programming )。只需确保将实际的错误打印例程分解为
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)
,并可能在其他平台上模拟该接口。)
您还可以尝试将那些根本不会出错的操作分解出来,或者只是通过一些简单的设置来调用一些系统调用,因为这些操作可以轻松地重用组件。
答案 1 :(得分:1)
保持原样?在主要开始时的一些设置不构成问题,IMO。在设置完成后开始重构。
答案 2 :(得分:1)
这不是为了重构而重构的标志吗?
无论如何,关于“让我们一起初始化sockfd和s_addr”,你可以随时 创建一个结构,并传递一个指向它的指针:
struct app_ctx {
int init_stage;
int sock_fd;
struct sockaddr_in myaddr;
...
}
然后将指向此结构实例的指针传递给所有“一次做一件事”的函数,并返回错误代码。
在清理时,你做同样的事情并传递相同的结构。