对于VS中一些较严谨的(_s)函数如strcpy_s的第二个参数的问题

本文探讨了C/C++中strcpy_s函数的正确用法,并通过一个野指针实例展示了如何合理设置目标缓冲区大小以避免运行时错误。文章纠正了常见的误解,并提供了正确的参数设置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在学习中发现strcpy_s中第二个参数不能随便写。开始我碰到这个比较严谨的函数时我故作聪明,将第二个参数使用strlen()代替。但拿不严谨的小聪明去解决严谨的函数问题必然问题一大堆。
今天这一个问题烦了我很久,后来才发现是不懂strcpy_s函数第二个参数的含义。下面这程序是野指针的例子。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main()
{
    char *p1 = NULL;
    p1 = (char *)malloc(100);
    if (p1 == NULL)
    {
        return;
    }
    strcpy_s(p1, sizeof("11112222") + 1, "11112222");
    //1.strcpy_s(char *strDestination,size_t numberOfElements,const char *strSource);第二个参数表示目标缓冲区大小,大于或等于源串的长度+1(存放结束符'/0')。如果没有第二个参数,就没有方法来保证有效的缓冲区尺寸,不然只能假定缓冲足够大来容纳要拷贝的字符串(strcpy函数)。在程序运行时,这将导致不可预料的行为。
    //2.如果缓冲区过小会报错:buffer is too small。只需把第二个参数设置成合适的就好了。
    //之前我错误地一贯写成「strcpy_s(p1,strlen(p1),"11112222")」。后来改成sizeof("11112222")就报错2了。

    printf("p1:%s\n", p1);

    if (p1 != NULL)
    {
        free(p1);
        p1 = NULL;
    }

    if (p1 != NULL)
    {
        free(p1);
    }

    system("pause");
}

小生不才,若有错误,不吝赐教。

typedef struct { char **matches; // 所有匹配项 int count; // 匹配项总数 char *base_dir; // 当前基础目录 char *original_prefix; // 原始前缀 char *current_text; // 当前输入文本 int last_state; // 上一次使用的state值 int current_level; // 当前目录层级 char *persistent_path; // 持久化路径(同时作为初始化标志) int is_directory; int max_level; // 新增:最大层级限制 int initialized; int flash_mode; } CompletionState; static CompletionState comp_state = { .matches = NULL, .count = 0, .base_dir = NULL, .original_prefix = NULL, .current_text = NULL, .last_state = -1, .current_level = 0, .persistent_path = NULL, .is_directory = 0, .max_level = -1, .initialized = 0, .flash_mode = 0 }; // 重置补全状态 void reset_completion_state() { if (comp_state.matches) { for (int i = 0; comp_state.matches[i]; i++) { XFREE(MTYPE_TMP, comp_state.matches[i]); } XFREE(MTYPE_TMP, comp_state.matches); comp_state.matches = NULL; } if (comp_state.original_prefix) { XFREE(MTYPE_TMP, comp_state.original_prefix); } if (comp_state.base_dir) { XFREE(MTYPE_TMP, comp_state.base_dir); } if (comp_state.current_text) { XFREE(MTYPE_TMP, comp_state.current_text); } if (comp_state.persistent_path) { XFREE(MTYPE_TMP, comp_state.persistent_path); } comp_state.count = 0; comp_state.original_prefix = NULL; comp_state.base_dir = NULL; comp_state.current_text = NULL; comp_state.last_state = -1; comp_state.current_level = 0; comp_state.persistent_path = NULL; comp_state.is_directory = 0; comp_state.max_level = -1; comp_state.initialized = 0; comp_state.flash_mode = 0; } int compare_paths(const void *a, const void *b) { const char *sa = *(const char **)a; const char *sb = *(const char **)b; // 获取第一个字符 char first_a = sa[0]; char first_b = sb[0]; // 1. 处理小写字母开头的情况 int is_lower_a = islower(first_a); int is_lower_b = islower(first_b); if (is_lower_a && !is_lower_b) return -1; // 小写优先 if (!is_lower_a && is_lower_b) return 1; // 小写优先 // 2. 处理大写字母开头的情况 int is_upper_a = isupper(first_a); int is_upper_b = isupper(first_b); if (is_upper_a && !is_upper_b) return -1; // 大写优先于非字母 if (!is_upper_a && is_upper_b) return 1; // 大写优先于非字母 // 3. 同类别比较(都是小写或都是大写或都是非字母) return strcasecmp(sa, sb); // 不区分大小写比较 } // 构建完整路径(自动加 /) char *build_completion_path(const char *base, const char *match, int is_dir) { char *path = (char *)XCALLOC(MTYPE_TMP, strlen(base) + strlen(match) + 2); if (base && strlen(base) > 0) { int needs_slash = base[strlen(base) - 1] != '/'; if (needs_slash) { snprintf(path, strlen(base) + strlen(match) + 2, "%s/%s", base, match); } else { snprintf(path, strlen(base) + strlen(match) + 2, "%s%s", base, match); } } else { strcpy(path, match); } if (is_dir) { char *tmp = (char *)XREALLOC(MTYPE_TMP, path, strlen(path) + 2); strcat(tmp, "/"); XFREE(MTYPE_TMP, path); path = tmp; } return path; } // 优化后的路径解析函数 void parse_input_text(const char *text, char **base_dir, char **prefix, int *level) { *base_dir = NULL; *prefix = NULL; // 处理 flash:/ 虚拟路径 if (strncmp(text, "flash:/", 7) == 0) { const char *path_part = text + 7; const char *last_slash = strrchr(path_part, '/'); if (last_slash) { // 提取目录部分 size_t base_len = last_slash - path_part + 1; *base_dir = (char *)XCALLOC(MTYPE_TMP, base_len + 1); strncpy(*base_dir, path_part, base_len); (*base_dir)[base_len] = '\0'; // 提取前缀部分 *prefix = XSTRDUP(MTYPE_TMP, last_slash + 1); *level = comp_state.current_level + 1; } else { *base_dir = XSTRDUP(MTYPE_TMP, ""); *prefix = XSTRDUP(MTYPE_TMP, path_part); *level = 0; } return; } // 普通路径解析 const char *slash = strrchr(text, '/'); if (slash) { size_t base_len = slash - text + 1; *base_dir = (char *)XCALLOC(MTYPE_TMP, base_len + 1); strncpy(*base_dir, text, base_len); (*base_dir)[base_len] = '\0'; *prefix = XSTRDUP(MTYPE_TMP, slash + 1); *level = comp_state.current_level + 1; } else { *base_dir = XSTRDUP(MTYPE_TMP, ""); *prefix = XSTRDUP(MTYPE_TMP, text); *level = 0; } } // 上下文一致性判断(优化后) int is_same_completion_context(const char *text) { if (!comp_state.persistent_path || comp_state.max_level == -1) { return 0; } char *temp_base = NULL; char *temp_prefix = NULL; int current_level = 0; parse_input_text(text, &temp_base, &temp_prefix, &current_level); printf("is_same_completion_context: text = %s, temp_base = %s, current_level = %d\n", text, temp_base, current_level); int same_path = strncmp(text, comp_state.persistent_path, strlen(comp_state.persistent_path)) == 0; // 只有在当前路径是持久化路径的子目录或匹配时才继续 int within_level = (current_level <= comp_state.max_level); XFREE(MTYPE_TMP, temp_base); XFREE(MTYPE_TMP, temp_prefix); printf("is_same_completion_context: same_path=%d, within_level=%d\n", same_path, within_level); return same_path && within_level; } // 生成当前目录下的路径匹配项 char **generate_current_dir_paths(const char *base_dir, const char *prefix, int add_empty) { const char *scan_dir = base_dir; if (!scan_dir || !*scan_dir) { scan_dir = "."; } printf("[generate] Scanning directory: '%s' with prefix: '%s'\n", scan_dir, prefix ? prefix : "(none)"); DIR *dir = opendir(scan_dir); if (!dir) { printf("\n opendir scan_dir = %s failed.\n", scan_dir); if (add_empty) { char **matches = (char **)XCALLOC(MTYPE_TMP, sizeof(char *)); matches[0] = XSTRDUP(MTYPE_TMP, ""); return matches; } return NULL; } int capacity = 32; int count = 0; char **matches = (char **)XCALLOC(MTYPE_TMP, capacity * sizeof(char *)); struct dirent *entry; while ((entry = readdir(dir)) != NULL) { const char *name = entry->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; // 跳过隐藏文件 if (name[0] == '.') continue; // 跳过软链接(直接判断 d_type) if (entry->d_type == DT_LNK) { printf("跳过软链接: %s\n", name); continue; } if (prefix && *prefix && strncmp(name, prefix, strlen(prefix)) != 0) continue; int is_dir = (entry->d_type == DT_DIR); if (entry->d_type == DT_UNKNOWN) { char full_path[PATH_MAX]; snprintf(full_path, sizeof(full_path), "%s/%s", scan_dir, name); struct stat st; if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) { is_dir = 1; } } char *new_name; if (is_dir) { new_name = (char *)XCALLOC(MTYPE_TMP, strlen(name) + 2); snprintf(new_name, strlen(name) + 2, "%s/", name); } else { new_name = XSTRDUP(MTYPE_TMP, name); } if (count >= capacity) { capacity *= 2; matches = (char **)XREALLOC(MTYPE_TMP, matches, capacity * sizeof(char *)); } matches[count++] = new_name; } closedir(dir); if (count > 0) { qsort(matches, count, sizeof(char *), compare_paths); } matches = (char **)XREALLOC(MTYPE_TMP, matches, (count + 2) * sizeof(char *)); matches[count] = NULL; if (!add_empty) { matches[count] = XSTRDUP(MTYPE_TMP, ""); matches[count + 1] = NULL; } printf("\n[generate] Found %d matches.\n", count); if (matches && matches[0]) { printf("\ngenerate_current_dir_paths: matches = "); for (int i = 0; matches[i]; i++) { printf("%s, ", matches[i]); } printf("\n"); } return matches; } // 主补全函数(关键优化点) char *filename_completion_function(const char *text, int state) { printf("\n[COMP] Entering: text='%s', state=%d, last_state=%d\n", text, state, comp_state.last_state); // 拦截空输入 if (text == NULL || *text == '\0') { printf("用户未输入任何文本,不提供补全\n"); return NULL; } char current_dir[PATH_MAX]; char abs_path[PATH_MAX]; int reset_completion = 0; int is_flash_mode = (strncmp(text, "flash:/", 7) == 0); int is_f_prefix = (strncmp(text, "f", 1) == 0 && strncmp(text, "flash:/", 7) != 0); // 核心优化:添加前缀匹配检查 int is_prefix_of_existing = 0; if (comp_state.matches && comp_state.count > 0) { for (int i = 0; i < comp_state.count; i++) { if (comp_state.count == 1) { is_prefix_of_existing = 0; } if (is_flash_mode) { if (strncmp(text + 7, comp_state.matches[i], strlen(text + 7)) == 0) { is_prefix_of_existing = 1; break; } } else if (strncmp(text, comp_state.matches[i], strlen(text)) == 0) { is_prefix_of_existing = 1; break; } } } printf(" Text is prefix of existing matches, is_prefix_of_existing = %d\n", is_prefix_of_existing); // 仅在需要时初始化新补全 if (state == 0) { printf(" Starting new completion check\n"); // 获取当前工作目录 getcwd(current_dir, sizeof(current_dir)); // 关键优化:特殊处理flash路径拼接 if (is_flash_mode) { // 对于flash模式,直接使用虚拟路径 snprintf(abs_path, sizeof(abs_path), "/mnt/switch/%s", text + 7); } else { // 普通路径拼接 snprintf(abs_path, sizeof(abs_path), "%s/%s", current_dir, text); } // 核心优化:检查是否是现有匹配项的前缀 if (is_prefix_of_existing) { printf(" Text is prefix of existing matches, no reset needed\n"); reset_completion = 0; } else { // 原始重置逻辑 if (access(abs_path, F_OK) != 0) { reset_completion = 1; } } printf("\n111filename_completion_function: reset_completion = %d, abs_path = '%s'\n", reset_completion, abs_path); char *tmp_base = NULL; char *tmp_prefix = NULL; int tmp_level = 0; parse_input_text(text, &tmp_base, &tmp_prefix, &tmp_level); printf("\n222filename_completion_function: text = '%s', tmp_base = '%s', tmp_prefix = '%s'\n", text, tmp_base, tmp_prefix); int same_reset = 0; struct stat st_tmp; char tmp_path[PATH_MAX]; if (comp_state.matches && comp_state.matches[0]) { for (int i = 0; comp_state.matches[i]; i++) { printf("\n333filename_completion_function: comp_state.matches[%d] = '%s'\n", i, comp_state.matches[i]); if (strncmp(comp_state.matches[i], tmp_prefix, strlen(tmp_prefix)) == 0) { char *old_base = comp_state.base_dir ? comp_state.base_dir : ""; snprintf(tmp_path, sizeof(tmp_path), "%s/%s%s", current_dir, old_base, comp_state.matches[i]); printf("\n444filename_completion_function: tmp_path = '%s', current_dir = '%s', comp_state.matches[%d] = '%s'\n", tmp_path, current_dir, i, comp_state.matches[i]); if (stat(tmp_path, &st_tmp) == 0 && S_ISDIR(st_tmp.st_mode)) { same_reset = 1; printf("\nfilename_completion_function: same_reset = %d\n", same_reset); break; } } } } printf("\n555filename_completion_function: current_dir = '%s', text = '%s', abs_path = '%s', reset_completion = %d, same_reset = %d\n", current_dir, text, abs_path, reset_completion, same_reset); printf("\n666filename_completion_function: comp_state.persistent_path = '%s'\n", comp_state.persistent_path); int same_context = is_same_completion_context(text); printf(" Same context: %d\n", same_context); // 核心优化:添加匹配数量和类型判断 int should_reset = 0; if (is_prefix_of_existing) { printf(" Using existing matches for prefix\n"); should_reset = 0; // 新增:检查单目录匹配情况 if (comp_state.matches && comp_state.count == 1) { char *single_match = comp_state.matches[0]; char full_path[PATH_MAX]; // 构建完整路径 if (comp_state.base_dir) { snprintf(full_path, sizeof(full_path), "%s/%s", comp_state.base_dir, single_match); } else { snprintf(full_path, sizeof(full_path), "%s", single_match); } // 检查文件类型 struct stat st; if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) { printf(" Single directory match, forcing reset\n"); should_reset = 1; } } } else if ((!same_context) || (reset_completion && same_reset) || (!comp_state.persistent_path || comp_state.persistent_path[0] == '\0')) { should_reset = 1; } // 统一检查单目录匹配情况 if (!should_reset && comp_state.matches && comp_state.count == 1) { char *single_match = comp_state.matches[0]; char full_path[PATH_MAX]; // 构建完整路径 if (comp_state.base_dir) { snprintf(full_path, sizeof(full_path), "%s/%s", comp_state.base_dir, single_match); } else { snprintf(full_path, sizeof(full_path), "%s", single_match); } // 检查文件类型 struct stat st; if (stat(full_path, &st) == 0) { if (S_ISDIR(st.st_mode)) { printf(" Single directory match, forcing reset\n"); should_reset = 1; } else { printf(" Single file match, no reset needed\n"); } } } if (should_reset) { reset_completion_state(); printf("\n -----------------common reset---------------\n"); char *current_base = NULL; char *current_prefix = NULL; int current_level = 0; parse_input_text(text, &current_base, &current_prefix, &current_level); printf("\n777filename_completion_function: current_base = '%s', current_prefix = '%s', current_level = %d\n", current_base, current_prefix, current_level); // 关键优化:特殊处理flash模式的基础路径 if (is_flash_mode) { comp_state.base_dir = XSTRDUP(MTYPE_TMP, "flash:/"); // 关键修复:确保正确设置当前文本 comp_state.current_text = XSTRDUP(MTYPE_TMP, text); } else { comp_state.base_dir = current_base; comp_state.current_text = XSTRDUP(MTYPE_TMP, text); } comp_state.current_text = XSTRDUP(MTYPE_TMP, text); comp_state.original_prefix = XSTRDUP(MTYPE_TMP, current_prefix); comp_state.current_level = current_level; comp_state.max_level = current_level + 3; // 检查是否用户已经明确进入了子目录 int ends_with_slash = text[strlen(text) - 1] == '/'; if (ends_with_slash) { comp_state.persistent_path = XSTRDUP(MTYPE_TMP, text); comp_state.max_level = current_level + 3; } else { comp_state.persistent_path = XSTRDUP(MTYPE_TMP, current_base); comp_state.max_level = current_level; } // 生成当前目录的补全项 if (is_flash_mode) { comp_state.matches = generate_current_dir_paths("/mnt/switch/", text + 7, 1); comp_state.flash_mode = 1; } else if (is_f_prefix) { comp_state.matches = generate_current_dir_paths(current_base, current_prefix, 1); if (current_level == 0) { if (comp_state.matches == NULL || comp_state.matches[0] == NULL) { comp_state.base_dir = XSTRDUP(MTYPE_TMP, "flash:/"); comp_state.matches = generate_current_dir_paths("/mnt/switch/", text, 1); comp_state.flash_mode = 1; } else { int count = 0; while (comp_state.matches[count]) count++; int flash_already_in_list = 0; for (int i = 0; comp_state.matches[i]; i++) { if (strncmp(comp_state.matches[i], "flash:/", 7) == 0) { flash_already_in_list = 1; break; } } if (!flash_already_in_list && count < 1024 - 1) { char **temp = (char **)XREALLOC(MTYPE_TMP, comp_state.matches, (count + 2) * sizeof(char*)); comp_state.matches = temp; comp_state.matches[count] = XSTRDUP(MTYPE_TMP, "flash:/"); comp_state.matches[count + 1] = NULL; if (comp_state.matches && comp_state.matches[0]) { printf("\n flash: comp_state.matches = "); for (int i = 0; comp_state.matches[i]; i++) { printf("%s, ", comp_state.matches[i]); } printf("\n"); } } } } } else { comp_state.matches = generate_current_dir_paths(current_base, current_prefix, current_level == 0); } comp_state.count = 0; if (comp_state.matches) { while (comp_state.matches[comp_state.count]) { comp_state.count++; } printf(" Generated %d matches\n", comp_state.count); } else { comp_state.count = 0; } comp_state.last_state = -1; // 重置索引 } } printf("\n888filename_completion_function: comp_state.base_dir = '%s', comp_state.current_text = '%s', comp_state.original_prefix = '%s', comp_state.persistent_path = '%s'\n", comp_state.base_dir, comp_state.current_text, comp_state.original_prefix, comp_state.persistent_path); // 没有匹配项 if (!comp_state.matches || comp_state.count == 0) { printf("[COMP] No matches found\n"); return NULL; } // 获取下一个匹配项 int next_index = (comp_state.last_state + 1) % comp_state.count; comp_state.last_state = next_index; char *match = comp_state.matches[next_index]; // 构建完整路径 char *full_path = NULL; if (strncmp(match, "flash:/", 7) == 0) { full_path = XSTRDUP(MTYPE_TMP, match); } else if (comp_state.flash_mode) { // 关键优化:正确处理flash模式下的路径拼接 int base_len = strlen(comp_state.base_dir); int needs_slash = base_len > 0 && comp_state.base_dir[base_len - 1] != '/'; if (needs_slash) { full_path = XCALLOC(MTYPE_TMP, strlen(comp_state.base_dir) + strlen(match) + 2); snprintf(full_path, strlen(comp_state.base_dir) + strlen(match) + 2, "%s/%s", comp_state.base_dir, match); } else { full_path = XCALLOC(MTYPE_TMP, strlen(comp_state.base_dir) + strlen(match) + 1); snprintf(full_path, strlen(comp_state.base_dir) + strlen(match) + 1, "%s%s", comp_state.base_dir, match); } } else { if (comp_state.base_dir && strlen(comp_state.base_dir) > 0) { int base_len = strlen(comp_state.base_dir); int needs_slash = base_len > 0 && comp_state.base_dir[base_len - 1] != '/'; full_path = XCALLOC(MTYPE_TMP, strlen(match) + base_len + 2); if (needs_slash) { snprintf(full_path, strlen(match) + base_len + 2, "%s/%s", comp_state.base_dir, match); } else { snprintf(full_path, strlen(match) + base_len + 2, "%s%s", comp_state.base_dir, match); } } else { full_path = XSTRDUP(MTYPE_TMP, match); } } printf("[COMP] Returning: '%s'\n", full_path); return full_path; } 在上边这个代码的基础上优化,主要是优化filename_completion_function函数,现在的要求是: 1、加入我当前路径下有个etc目录,然后etc目录下有个ssh目录,我现在进入到etc目录中,用s去补全,那只能唯一补全成ssh/,然后后续再tab则是遍历ssh目录下的文件和子目录了 2、如果当前只有一个匹配到输入的前缀的文件,那就直接补全,即使后续tab也是返回这个文件名 3、如果当前只有一个匹配到输入的前缀的目录,那就直接补全,然欧后后边连续tab则是自动遍历这个目录下的文件和子目录 4、不要使用state判断状态,这个值一直是0 5、可以考虑,当匹配项只有一个的时候,那count=1,可以使用count=1联合判断 6、代码中的flash:/虚拟路径的问题,flash:/是映射实际的根路径/mnt/switch/,加入我输入的是f,然后tab补全,当前路径下有fa目录,则是循环显示fa/ flash:/,当显示到flash:/是,我再后边追加字符,则是想补全这个匹配字符的文件或目录,如果又在这个匹配的目录后追加字符,也要成功匹配;而且当前代码中还有另外的问题,判断用户输入的f前缀不严谨,如果用户输入的是fa前缀,那这个代码还是会强行匹配到flash:/ 7、最初的入参text是每次tab都会改变的
最新发布
08-15
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值