字符串拼接函数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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值