字符串拼接函数strcat的实现 while(*p++ !='\0')跳出后,p究竟指向哪? C/C++求职面试必备考点(三)...

本文详细探讨了strcat函数的内部实现机制,重点分析了指针操作中的*p++与++p的区别,通过实例代码展示了如何正确使用这些操作来实现字符串连接,并解释了为何在循环结束后指针的位置不是预期的位置。

在研究strcat函数实现的时候,发现了while(*p++ !=‘\0’)的大秘密,也让我发现了在上篇博客中我犯的一个错误。原以为跳出后,P指向'\0',而实际上并非如此!我按跳出后P指向'\0',结果怎么拼接都实现不了,NND。为了验证这个问题,我们先上个小程序吧:

#include<stdio.h>

void main()

{

char *p = "abcdefg";

while(*p++ != 'c');

printf("%c\n", *p);

}

您猜打印出来的是几? 结果是d。

为什么呢?

原来*和++的优先级是一样的,当优先级一样的时候,程序按自左至右的顺序执行。所以当*p = 'c' 或者*p = '\0'时,p仍然要往下移一位,即p指向满足条件后的下一个字节。

由于'\0' 结束符,if 、while当会把他判成0,因此程序写成while(*p++ );效果也一样,最终程序指向字符串结束符'\0'的下一字节。

如果这样写:

while(*p)

p++;

当*p='\0'时,进不到while里面的循环,因此这种写法跳出循环的时候,p指向的是'\0'。同志们注意了,这样写while后面是没有“;”号的,文章一开始的写法后面是有“;”号的。

所以明白了这,我们写strcat就好些了,首先查找目的字符串dst的末尾处,然后复制过去就OK了。

#include <stdio.h>

#include <assert.h>

char *strcat(char *dst, const char *src)

{

assert(dst);

char *dstFirst = dst;

while(*dst++);

dst--;

while(*dst++ = *src++);

return dstFirst;

}


//测试部分

void main()

{

char *str1 = "you must";

char *str2 = "zijuezizhi!";

char *dst = malloc(100);

strcat(strcat(dst, str1), str2);

prinf("%s\n", dst);

free(dst);

dst = NULL; 这个为什么大家指点奋斗

}

测试结果:you mustzijuezizhi! 一切正常。

char *dst = malloc(100); 这句话也可以这样写:char *dst = NULL; dst = (char *)malloc(100);在程序结束处一定要free(dst)啊!也有书上说在dst调用strcat之前,要*dst = '\0',我没这样,测试结果也正确。水平有限,有些问题解释不了,大神看到了指点一二。

这里交代下为甚strcat包括strcpy函数要返回char*类型,通过程序也看到了,返回的是首地址。我们printf打印一个字符串用的参数 就是首地址。因此通过返回首地址可以实现像strcat(strcat(dst, str1), str2);这种链式调用。在某些场合很方便。





把以下代码中//的注释,修改为/* */ #include "common.h" /** * 根据文件扩展名获取MIME类型 * @param path 文件路径 * @return 对应的MIME类型字符串 */ const char *get_mime_type(const char *path) { /* 检查空指针和空字符串 */ if (path == NULL || *path == '\0') { return "text/plain"; } const char *ext = strrchr(path, '.'); // 查找最后一个点号 if (!ext) return "text/plain"; // 无扩展名默认纯文本 // 常见文件类型匹配 if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) return "text/html"; if (strcmp(ext, ".css") == 0) return "text/css"; if (strcmp(ext, ".js") == 0) return "application/javascript"; if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; if (strcmp(ext, ".png") == 0) return "image/png"; if (strcmp(ext, ".gif") == 0) return "image/gif"; if (strcmp(ext, ".json") == 0) return "application/json"; if (strcmp(ext, ".ico") == 0) return "image/x-icon"; return "text/plain"; // 未知扩展名默认纯文本 } /** * 发送HTTP响应 * @param fd 客户端套接字 * @param status HTTP状态码 * @param status_msg 状态描述 * @param content_type 内容类型 * @param body 响应体内容 * @param body_len 响应体长度 */ void send_response(int fd, int status, const char *status_msg, const char *content_type, const char *body, int body_len) { /* 入参检查 */ if (fd < 0 || status <= 0 || status_msg == NULL || content_type == NULL || (body_len > 0 && body == NULL)) { perror("send_response: Invalid input parameters"); return; } char header[BUFFER_SIZE]; // 构建HTTP响应头 int header_len = snprintf(header, BUFFER_SIZE, "HTTP/1.1 %d %s\r\n" // 状态行 "Content-Type: %s\r\n" // 内容类型 "Content-Length: %d\r\n" // 内容长度 "Connection: close\r\n" // 关闭连接 "\r\n", // 空行结束头部 status, status_msg, content_type, body_len); // 发送响应头 write(fd, header, header_len); // 发送响应体(如果有) if (body && body_len > 0) { write(fd, body, body_len); } } /** * 发送文件内容 * @param fd 客户端套接字 * @param path 文件路径 */ void send_file(int fd, const char *path) { /* 入参检查 */ if (fd < 0 || NULL == path) { perror("send_file:Invalid input parameters"); return; } struct stat file_stat; // 获取文件状态信息 if (stat(path, &file_stat) < 0 || !S_ISREG(file_stat.st_mode)) { // 文件不存在或不是普通文件 send_response(fd, 404, "Not Found", "text/html", "<h1>404 Not Found</h1>", 22); return; } // 打开文件 int file_fd = open(path, O_RDONLY); if (file_fd < 0) { // 文件打开失败 send_response(fd, 500, "Internal Server Error", "text/html", "<h1>500 Internal Error</h1>", 26); return; } // 获取MIME类型 const char *mime_type = get_mime_type(path); char header[BUFFER_SIZE]; // 构建成功响应头 int header_len = snprintf(header, BUFFER_SIZE, "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\n" "Content-Length: %ld\r\n" "Connection: close\r\n" "\r\n", mime_type, file_stat.st_size); // 发送响应头 write(fd, header, header_len); fprintf(stdout, "**********header**********\n%s\n", header); // 使用sendfile零拷贝发送文件内容 off_t offset = 0; size_t remaining = file_stat.st_size; while (remaining > 0) { ssize_t sent = sendfile(fd, file_fd, &offset, remaining); if (sent <= 0) { perror("sendfile"); break; } remaining -= sent; } // 关闭文件 close(file_fd); } /** * 处理GET请求 * @param fd 客户端套接字 * @param path 请求路径 */ void handle_get_request(int fd, const char *path) { /* 入参检查 */ if (fd < 0 || path == NULL || *path == '\0') { send_response(fd, 400, "Bad Request", "text/html", "<h1>400 Invalid Request</h1>", 26); return; } printf("handle_get_request.\n"); char full_path[BUFFER_SIZE]; // 构建完整文件路径 snprintf(full_path, BUFFER_SIZE, "%s%s", ROOT_DIR, path); // 处理目录请求(以'/'结尾) if (path[strlen(path) - 1] == '/') { DIR *dir = opendir(full_path); if (!dir) { // 目录打开失败 send_response(fd, 404, "Not Found", "text/html", "<h1>404 Not Found</h1>", 22); return; } // 生成目录列表HTML char body[BUFFER_SIZE * 2] = "<html><head><title>Index of "; strcat(body, path); strcat(body, "</title></head><body><h1>Index of "); strcat(body, path); strcat(body, "</h1><hr><ul>"); struct dirent *entry; // 遍历目录项 while ((entry = readdir(dir)) != NULL) { // 跳过... if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; char li[256]; // 构建列表项 snprintf(li, sizeof(li), "<li><a href=\"%s%s\">%s</a></li>", path, entry->d_name, entry->d_name); strcat(body, li); } strcat(body, "</ul><hr></body></html>"); // 发送目录列表 send_response(fd, 200, "OK", "text/html", body, strlen(body)); fflush(stdout); // 强制刷新缓冲区 closedir(dir); return; } fprintf(stdout, "----\nfull_path:%s\n\n", full_path); // 发送文件内容 send_file(fd, full_path); } /** * 处理POST请求 * @param fd 客户端套接字 * @param path 请求路径 * @param body 请求体内容 * @param body_len 请求体长度 */ void handle_post_request(int fd, const char *path, const char *body, int body_len) { /* 添加入参检查 */ if (fd < 0 || path == NULL || (body_len > 0 && body == NULL)) { send_response(fd, 400, "Bad Request", "text/html", "<h1>400 Invalid Request</h1>", 26); return; } // 示例:处理联系表单提交 printf("%s\n",path); char *user_data = strstr(body, "name="); if (user_data != NULL) { char *delimiters = "&"; // 个分隔符 char *tokens[4]; // 存储4个分段结果 int token_count = 0; // 当前分段计数 char *start = user_data; // 当前分段起始位置 char *current = user_data; // 当前扫描位置 // 遍历字符串并分段 while (*current != '\0' && token_count < 3) { // 检查当前字符是否是目标分隔符 if (strchr(delimiters, *current) != NULL) { tokens[token_count] = start; *current = '\0'; start = current + 1; token_count++; // 如果已经处理了个分隔符,跳出循环 if (token_count == 3) break; } current++; } // 添加最后一个分段 if (token_count < 4) { tokens[token_count] = start; token_count++; } fprintf(stdout, "%s\n%s\n%s\n",tokens[0],tokens[1],tokens[2]); const char *filename = "contact_submissions.txt"; // 检查请求体长度 if (body_len <= 0) { send_response(fd, 400, "Empty Body", "text/html", "<h1>400 Form data is empty</h1>", 31); return; } // 安全打开文件(追加模式) FILE *fp = fopen(filename, "a"); if (!fp) { perror("fopen failed"); send_response(fd, 500, "Internal Error", "text/html", "<h1>500 Server storage error</h1>", 33); return; } // 安全写入数据(防止缓冲区溢出) if (fprintf(fp, "%s\n%s\n%s\n",tokens[0],tokens[1],tokens[2]) < 0) { perror("fprintf failed"); fclose(fp); return; } // 确保数据刷入磁盘 if (fflush(fp) != 0) { perror("fflush failed"); } // 安全关闭文件 if (fclose(fp) != 0) { perror("fclose failed"); } // 返回成功响应 send_response(fd, 200, "OK", "text/html", "<html><body><h1>Thank You!</h1><p>Form submitted successfully</p></body></html>", 60); } else { printf("404 post\n"); // 不支持其他POST路径 send_response(fd, 404, "Not Found", "text/html", "<h1>404 Not Found</h1>", 22); } } /** * 处理HTTP请求 * @param fd 客户端套接字 * @param request 请求数据 */ void handle_request(int fd, const char *request) { /* 添加基础入参检查 */ if (fd < 0 || request == NULL || *request == '\0') { send_response(fd, 400, "Bad Request", "text/html", "<h1>400 Empty Request</h1>", 25); return; } char method[16], path[256], protocol[16]; // 解析请求行 if (sscanf(request, "%15s %255s %15s", method, path, protocol) != 3) { printf("400 Bad request.\n"); send_response(fd, 400, "Bad Request", "text/html", "<h1>400 Bad Request</h1>", 24); return; } // 处理根路径请求 if (strcmp(path, "/") == 0) { strcpy(path, "/Index.html"); // 默认返回主页 } // 查找请求体起始位置 const char *body = strstr(request, "\r\n\r\n"); if (body) body += 4; // 跳过空行 // 方法分发 if (strcmp(method, "GET") == 0) { handle_get_request(fd, path); } else if (strcmp(method, "POST") == 0) { handle_post_request(fd, path, body, (body ? strlen(body) : 0)); } else { // 不支持的方法 send_response(fd, 501, "Not Implemented", "text/html", "<h1>501 Not Implemented</h1>", 25); } }
最新发布
08-16
<think>我们需要将代码中所有以"//"开头的单行注释修改为多行注释"/* */"。 注意:有些注释在代码行的末尾,我们需要将其单独提取出来改为多行注释,但注意不要破坏代码结构。 另外,有些注释可能已经是以"/* */"形式存在的,我们不需要修改它们。 我们逐行检查,将单行注释(包括行尾注释)转换为多行注释。 步骤: 1. 遍历每一行。 2. 如果一行中包含"//"注释,且该"//"不在字符串中(我们这里简化处理,假设代码中没有包含"//"的字符串),则将其转换为多行注释。 3. 注意:有些行可能包含多个"//",我们只处理第一个出现的有效"//"(即不在字符串中的)。 4. 转换方法:将"//"替换为"/* ",然后在行尾加上" */"。 5. 如果该行在"//"之后还有代码(即行尾注释),那么只将注释部分用"/* */"包裹。 但是注意:有些行可能只有注释,有些行是代码和注释共存。 然而,我们注意到代码中已经存在一些多行注释(以/**/形式),我们不需要修改它们。因此,我们只需要修改以"//"开头的注释。 但是,由于我们无法完全判断上下文(比如字符串、多行注释等),这里我们假设原代码中所有"//"都是单行注释,并且没有特殊情况。 因此,我们可以用以下规则: - 如果一行中有"//",那么从"//"开始到行尾的内容就是注释,我们将其改为"/* [注释内容] */" - 同时,如果这行在"//"之前有代码,那么保留代码,并在代码后面加上转换后的注释。 但是,注意:原代码中有些注释是单独一行的,有些是附在代码后面的。 我们采用以下方法: - 对于每一行,查找第一个出现的"//"(忽略字符串内的,但这里我们假设没有字符串包含"//")。 - 如果找到,那么将"//"替换为"/* ",然后在行尾(去掉换行符)加上" */",再换行。 但是,由于我们是在字符串操作,所以需要重新构建该行。 例如: 原行: int a = 0; // 初始化变量 转换后: int a = 0; /* 初始化变量 */ 但是,注意:原行末尾有换行符,我们在处理时要保留换行符。 我们逐行处理,并输出。 但是,我们也要注意:有些行可能包含多个"//",但我们只处理第一个,因为后面的可能是被注释掉的。 另外,有些行可能已经包含"/* */",我们不需要修改。 由于代码量不大,我们可以手动检查,但这里我们按照规则进行替换。 但是,有一个特殊情况:如果"//"出现在字符串中,比如: char *str = "http://example.com"; // 这是一个字符串 我们不应该替换字符串中的"//",但这里我们无法判断,所以原代码中应该避免这种情况。根据给定代码,似乎没有这种情况。 因此,我们直接替换。 但是,我们注意到原代码中有些注释是连续的多个"//",比如: // 这是一条注释 我们也要将其转换为: /* 这是一条注释 */ 所以,我们处理每一行,如果该行以"//"开头(可能前面有空格),那么整行都是注释,我们将其改为"/* ... */"的形式。 如果是在代码后面的,我们只转换从"//"开始的部分。 具体步骤: - 读取每一行。 - 查找第一个"//",如果该"//"前面有非空白字符,那么就是代码+注释;如果前面都是空白,那么就是整行注释。 但是,我们不需要区分,统一处理:将"//"替换为"/* ",然后行尾加上" */"。 但是,注意:如果该行已经包含"/* */",那么我们可能会破坏原有的多行注释。所以,我们只处理没有多行注释标记的行?这很复杂。 鉴于原代码中只有单行注释,且没有多行注释和字符串的干扰,我们直接替换。 但是,我们也要避免替换已经处理过的行(即转换后可能又出现"//"),所以只处理一次。 因此,我们按以下步骤处理每一行: - 查找"//",如果找到,且它不在任何注释和字符串中(我们这里简化,假设都不在),则: line = 该行"//"之前的部分 + "/*" + 该行"//"之后的部分(去掉换行) + " */" + 换行符 注意:原行可能有换行符,我们去掉换行符,然后加上" */"和换行符。 但是,我们也可以这样:将第一个"//"替换为"/* ",然后在行尾(换行符前)插入" */"。 例如: line = "int a=0; // comment\n" 替换:找到"//"的位置,将其替换为"/* ",然后从该位置开始到行尾(不包括换行)加上" */",最后换行。 得到:"int a=0; /* comment */\n" 但是,如果一行中有多个"//",我们只处理第一个,因为后面的可能是被注释掉的,我们不需要处理。 我们写一个函数来处理一行: 但是,由于我们只是修改这个文件,我们可以逐行处理,并输出。 但是,注意:原代码中可能有预处理指令(如#include)后面有注释,我们也要处理。 我们开始修改。 但是,由于我们是在文本中操作,我们可以用字符串替换。 但是,我们也要注意:有些行可能没有换行符,但通常每行都有换行符。 我们按照以下步骤: 1. 读取原代码的每一行(包括换行符)。 2. 在每一行中查找子串"//"。 3. 如果找到,假设该"//"是注释的开始,那么: - 取出该位置之前的部分(prefix) - 取出该位置之后的部分(包括换行符)(suffix) - 注意:suffix中包括"//"后面的内容,我们去掉换行符,然后将其作为注释内容。 - 然后,我们构建新行:prefix + "/*" + suffix(去掉换行) + " */\n" - 但是,注意:如果suffix中已经包含换行符,我们去掉换行符,然后加上" */"和换行符。 4. 如果没找到,原样输出。 但是,注意:如果一行中有多个"//",我们只处理第一个。 但是,我们也要考虑:如果"//"出现在字符串中,我们不应该替换,但原代码中似乎没有这种情况。 因此,我们直接替换。 但是,有一个问题:如果该行是空行,或者没有"//",我们就不处理。 我们开始修改代码。 但是,由于我们是在文本中,我们可以用以下C++代码(或者用其他语言)来处理,但这里我们手动修改也可以,因为代码量不大。 但是,题目要求修改代码,所以我们需要输出修改后的整个代码。 由于我们是在这里修改,所以我们将整个代码按照上述规则逐行修改。 但是,注意:原代码中已经有一些多行注释(如函数开头的注释),这些我们不需要修改。而且这些注释中可能包含"//",但我们已经看到,函数开头的注释是`/**/`形式的,所以不会包含"//"。 因此,我们开始修改。 由于代码较长,我们逐行处理并输出。 注意:有些行尾已经有反斜杠(续行符),但这里没有,所以不考虑。 我们开始修改: 但是,由于我们无法直接运行程序,我们手动修改每一行。 但是,为了准确,我们逐行检查并修改。 我们按照以下规则: - 如果一行中包含"//",那么将第一个"//"替换为"/* ",然后在该行末尾(换行符之前)加上" */"。 但是,如果该行已经包含"/* */",我们也不管,因为原代码中没有这种情况(即单行注释和多行注释混合在同一行)。 我们开始修改代码: 注意:原代码中有些注释是连续的多个"//",我们同样处理。 例如: // 注释 改为: /* 注释 */ 但是,如果一行中有多个"//",比如: int a = 0; // 初始化 // 变量 我们改为: int a = 0; /* 初始化 // 变量 */ 这样,第二个"//"就被保留在注释中了,这是可以的。 因此,我们开始修改。 由于代码较长,我们只修改包含"//"的行。 我们逐行扫描: 第一处:在get_mime_type函数中 const char *ext = strrchr(path, '.'); // 查找最后一个点号 if (!ext) return "text/plain"; // 无扩展名默认纯文本 修改为: const char *ext = strrchr(path, '.'); /* 查找最后一个点号 */ if (!ext) return "text/plain"; /* 无扩展名默认纯文本 */ 第二处: // 常见文件类型匹配 改为: /* 常见文件类型匹配 */ 第处:在send_response函数中 char header[BUFFER_SIZE]; // 构建HTTP响应头 改为: char header[BUFFER_SIZE]; /* 构建HTTP响应头 */ 第四处: // 发送响应体(如果有) 改为: /* 发送响应体(如果有) */ 第五处:在send_file函数中 // 获取文件状态信息 改为: /* 获取文件状态信息 */ 第六处: // 文件不存在或不是普通文件 改为: /* 文件不存在或不是普通文件 */ 第七处: // 打开文件 改为: /* 打开文件 */ 第八处: // 文件打开失败 改为: /* 文件打开失败 */ 第九处: // 获取MIME类型 改为: /* 获取MIME类型 */ 第十处: // 构建成功响应头 改为: /* 构建成功响应头 */ 第十一处: // 发送响应头 改为: /* 发送响应头 */ 第十二处: // 使用sendfile零拷贝发送文件内容 改为: /* 使用sendfile零拷贝发送文件内容 */ 第十处: // 关闭文件 改为: /* 关闭文件 */ 第十四处:在handle_get_request函数中 // 构建完整文件路径 改为: /* 构建完整文件路径 */ 第十五处: // 处理目录请求(以'/'结尾) 改为: /* 处理目录请求(以'/'结尾) */ 第十六处: // 目录打开失败 改为: /* 目录打开失败 */ 第十七处: // 生成目录列表HTML 改为: /* 生成目录列表HTML */ 第十八处: // 遍历目录项 改为: /* 遍历目录项 */ 第十九处: // 跳过... 改为: /* 跳过... */ 第二十处: // 构建列表项 改为: /* 构建列表项 */ 第二十一处: // 发送目录列表 改为: /* 发送目录列表 */ 第二十二处: fflush(stdout); // 强制刷新缓冲区 改为: fflush(stdout); /* 强制刷新缓冲区 */ 第二十处:在handle_post_request函数中 // 示例:处理联系表单提交 改为: /* 示例:处理联系表单提交 */ 第二十四处: // 检查请求体长度 改为: /* 检查请求体长度 */ 第二十五处: // 安全打开文件(追加模式) 改为: /* 安全打开文件(追加模式) */ 第二十六处: // 安全写入数据(防止缓冲区溢出) 改为: /* 安全写入数据(防止缓冲区溢出) */ 第二十七处: // 确保数据刷入磁盘 改为: /* 确保数据刷入磁盘 */ 第二十八处: // 安全关闭文件 改为: /* 安全关闭文件 */ 第二十九处: // 返回成功响应 改为: /* 返回成功响应 */ 第十处:在handle_request函数中 // 解析请求行 改为: /* 解析请求行 */ 第十一处: // 处理根路径请求 改为: /* 处理根路径请求 */ 第十二处: // 查找请求体起始位置 改为: /* 查找请求体起始位置 */ 第处: // 方法分发 改为: /* 方法分发 */ 另外,还有一些行尾注释,如: body += 4; // 跳过空行 改为: body += 4; /* 跳过空行 */ 以及: char *delimiters = "&"; // 个分隔符 改为: char *delimiters = "&"; /* 个分隔符 */ 但是,在handle_post_request函数中,有一段代码: char *delimiters = "&"; // 个分隔符 char *tokens[4]; // 存储4个分段结果 int token_count = 0; // 当前分段计数 我们改为: char *delimiters = "&"; /* 个分隔符 */ char *tokens[4]; /* 存储4个分段结果 */ int token_count = 0; /* 当前分段计数 */ 还有: // 遍历字符串并分段 改为: /* 遍历字符串并分段 */ 以及: // 检查当前字符是否是目标分隔符 改为: /* 检查当前字符是否是目标分隔符 */ 等等。 我们逐行修改整个代码。 由于代码较长,我们只展示修改后的完整代码。 注意:原代码中已经有一些多行注释(如函数开头的注释),我们不需要修改。 修改后的代码如下:</think>以下是修改后的代码,已将所有的单行注释(`//`)转换为多行注释(`/* */`): ```c #include "common.h" /** * 根据文件扩展名获取MIME类型 * @param path 文件路径 * @return 对应的MIME类型字符串 */ const char *get_mime_type(const char *path) { /* 检查空指针和空字符串 */ if (path == NULL || *path == '\0') { return "text/plain"; } const char *ext = strrchr(path, '.'); /* 查找最后一个点号 */ if (!ext) return "text/plain"; /* 无扩展名默认纯文本 */ /* 常见文件类型匹配 */ if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) return "text/html"; if (strcmp(ext, ".css") == 0) return "text/css"; if (strcmp(ext, ".js") == 0) return "application/javascript"; if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; if (strcmp(ext, ".png") == 0) return "image/png"; if (strcmp(ext, ".gif") == 0) return "image/gif"; if (strcmp(ext, ".json") == 0) return "application/json"; if (strcmp(ext, ".ico") == 0) return "image/x-icon"; return "text/plain"; /* 未知扩展名默认纯文本 */ } /** * 发送HTTP响应 * @param fd 客户端套接字 * @param status HTTP状态码 * @param status_msg 状态描述 * @param content_type 内容类型 * @param body 响应体内容 * @param body_len 响应体长度 */ void send_response(int fd, int status, const char *status_msg, const char *content_type, const char *body, int body_len) { /* 入参检查 */ if (fd < 0 || status <= 0 || status_msg == NULL || content_type == NULL || (body_len > 0 && body == NULL)) { perror("send_response: Invalid input parameters"); return; } char header[BUFFER_SIZE]; /* 构建HTTP响应头 */ int header_len = snprintf(header, BUFFER_SIZE, "HTTP/1.1 %d %s\r\n" /* 状态行 */ "Content-Type: %s\r\n" /* 内容类型 */ "Content-Length: %d\r\n" /* 内容长度 */ "Connection: close\r\n" /* 关闭连接 */ "\r\n", /* 空行结束头部 */ status, status_msg, content_type, body_len); /* 发送响应头 */ write(fd, header, header_len); /* 发送响应体(如果有) */ if (body && body_len > 0) { write(fd, body, body_len); } } /** * 发送文件内容 * @param fd 客户端套接字 * @param path 文件路径 */ void send_file(int fd, const char *path) { /* 入参检查 */ if (fd < 0 || NULL == path) { perror("send_file:Invalid input parameters"); return; } struct stat file_stat; /* 获取文件状态信息 */ if (stat(path, &file_stat) < 0 || !S_ISREG(file_stat.st_mode)) { /* 文件不存在或不是普通文件 */ send_response(fd, 404, "Not Found", "text/html", "<h1>404 Not Found</h1>", 22); return; } /* 打开文件 */ int file_fd = open(path, O_RDONLY); if (file_fd < 0) { /* 文件打开失败 */ send_response(fd, 500, "Internal Server Error", "text/html", "<h1>500 Internal Error</h1>", 26); return; } /* 获取MIME类型 */ const char *mime_type = get_mime_type(path); char header[BUFFER_SIZE]; /* 构建成功响应头 */ int header_len = snprintf(header, BUFFER_SIZE, "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\n" "Content-Length: %ld\r\n" "Connection: close\r\n" "\r\n", mime_type, file_stat.st_size); /* 发送响应头 */ write(fd, header, header_len); fprintf(stdout, "**********header**********\n%s\n", header); /* 使用sendfile零拷贝发送文件内容 */ off_t offset = 0; size_t remaining = file_stat.st_size; while (remaining > 0) { ssize_t sent = sendfile(fd, file_fd, &offset, remaining); if (sent <= 0) { perror("sendfile"); break; } remaining -= sent; } /* 关闭文件 */ close(file_fd); } /** * 处理GET请求 * @param fd 客户端套接字 * @param path 请求路径 */ void handle_get_request(int fd, const char *path) { /* 入参检查 */ if (fd < 0 || path == NULL || *path == '\0') { send_response(fd, 400, "Bad Request", "text/html", "<h1>400 Invalid Request</h1>", 26); return; } printf("handle_get_request.\n"); char full_path[BUFFER_SIZE]; /* 构建完整文件路径 */ snprintf(full_path, BUFFER_SIZE, "%s%s", ROOT_DIR, path); /* 处理目录请求(以'/'结尾) */ if (path[strlen(path) - 1] == '/') { DIR *dir = opendir(full_path); if (!dir) { /* 目录打开失败 */ send_response(fd, 404, "Not Found", "text/html", "<h1>404 Not Found</h1>", 22); return; } /* 生成目录列表HTML */ char body[BUFFER_SIZE * 2] = "<html><head><title>Index of "; strcat(body, path); strcat(body, "</title></head><body><h1>Index of "); strcat(body, path); strcat(body, "</h1><hr><ul>"); struct dirent *entry; /* 遍历目录项 */ while ((entry = readdir(dir)) != NULL) { /* 跳过... */ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; char li[256]; /* 构建列表项 */ snprintf(li, sizeof(li), "<li><a href=\"%s%s\">%s</a></li>", path, entry->d_name, entry->d_name); strcat(body, li); } strcat(body, "</ul><hr></body></html>"); /* 发送目录列表 */ send_response(fd, 200, "OK", "text/html", body, strlen(body)); fflush(stdout); /* 强制刷新缓冲区 */ closedir(dir); return; } fprintf(stdout, "----\nfull_path:%s\n\n", full_path); /* 发送文件内容 */ send_file(fd, full_path); } /** * 处理POST请求 * @param fd 客户端套接字 * @param path 请求路径 * @param body 请求体内容 * @param body_len 请求体长度 */ void handle_post_request(int fd, const char *path, const char *body, int body_len) { /* 添加入参检查 */ if (fd < 0 || path == NULL || (body_len > 0 && body == NULL)) { send_response(fd, 400, "Bad Request", "text/html", "<h1>400 Invalid Request</h1>", 26); return; } /* 示例:处理联系表单提交 */ printf("%s\n",path); char *user_data = strstr(body, "name="); if (user_data != NULL) { char *delimiters = "&"; /* 个分隔符 */ char *tokens[4]; /* 存储4个分段结果 */ int token_count = 0; /* 当前分段计数 */ char *start = user_data; /* 当前分段起始位置 */ char *current = user_data; /* 当前扫描位置 */ /* 遍历字符串并分段 */ while (*current != '\0' && token_count < 3) { /* 检查当前字符是否是目标分隔符 */ if (strchr(delimiters, *current) != NULL) { tokens[token_count] = start; *current = '\0'; start = current + 1; token_count++; /* 如果已经处理了个分隔符,跳出循环 */ if (token_count == 3) break; } current++; } /* 添加最后一个分段 */ if (token_count < 4) { tokens[token_count] = start; token_count++; } fprintf(stdout, "%s\n%s\n%s\n",tokens[0],tokens[1],tokens[2]); const char *filename = "contact_submissions.txt"; /* 检查请求体长度 */ if (body_len <= 0) { send_response(fd, 400, "Empty Body", "text/html", "<h1>400 Form data is empty</h1>", 31); return; } /* 安全打开文件(追加模式) */ FILE *fp = fopen(filename, "a"); if (!fp) { perror("fopen failed"); send_response(fd, 500, "Internal Error", "text/html", "<h1>500 Server storage error</h1>", 33); return; } /* 安全写入数据(防止缓冲区溢出) */ if (fprintf(fp, "%s\n%s\n%s\n",tokens[0],tokens[1],tokens[2]) < 0) { perror("fprintf failed"); fclose(fp); return; } /* 确保数据刷入磁盘 */ if (fflush(fp) != 0) { perror("fflush failed"); } /* 安全关闭文件 */ if (fclose(fp) != 0) { perror("fclose failed"); } /* 返回成功响应 */ send_response(fd, 200, "OK", "text/html", "<html><body><h1>Thank You!</h1><p>Form submitted successfully</p></body></html>", 60); } else { printf("404 post\n"); /* 不支持其他POST路径 */ send_response(fd, 404, "Not Found", "text/html", "<h1>404 Not Found</h1>", 22); } } /** * 处理HTTP请求 * @param fd 客户端套接字 * @param request 请求数据 */ void handle_request(int fd, const char *request) { /* 添加基础入参检查 */ if (fd < 0 || request == NULL || *request == '\0') { send_response(fd, 400, "Bad Request", "text/html", "<h1>400 Empty Request</h1>", 25); return; } char method[16], path[256], protocol[16]; /* 解析请求行 */ if (sscanf(request, "%15s %255s %15s", method, path, protocol) != 3) { printf("400 Bad request.\n"); send_response(fd, 400, "Bad Request", "text/html", "<h1>400 Bad Request</h1>", 24); return; } /* 处理根路径请求 */ if (strcmp(path, "/") == 0) { strcpy(path, "/Index.html"); /* 默认返回主页 */ } /* 查找请求体起始位置 */ const char *body = strstr(request, "\r\n\r\n"); if (body) body += 4; /* 跳过空行 */ /* 方法分发 */ if (strcmp(method, "GET") == 0) { handle_get_request(fd, path); } else if (strcmp(method, "POST") == 0) { handle_post_request(fd, path, body, (body ? strlen(body) : 0)); } else { /* 不支持的方法 */ send_response(fd, 501, "Not Implemented", "text/html", "<h1>501 Not Implemented</h1>", 25); } } ``` ### 修改说明: 1. **注释转换规则**: - 所有单行注释(`// ...`)已转换为多行注释(`/* ... */`) - 行尾注释也转换为多行注释格式 - 原有文档注释(`/** ... */`)保持不变 2. **转换细节**: - 保留原有注释的语义和位置 - 确保注释内容完整迁移 - 特别注意处理了代码逻辑中的关键注释 - 保持代码格式和缩进不变 3. **注意事项**: - 多行注释在C语言中不能嵌套 - 修改后代码功能完全不变 - 编译和运行行为与原始代码一致
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值