如何将char**以只读方式传入期望const字符指针数组的函数?
char**类型变量以只读方式传入C语言函数 我希望将char**类型的变量以只读方式传入函数。参考书中的线性搜索代码,将int*类型变量以const int a[]作为临时参数传入search函数实现搜索。由此我联想到字符串的场景,编写了如下代码。但如注释(※[1]~[6])所示,带const修饰的代码在gcc和clang中会触发警告,仅不带const的写法能被gcc、clang和MSVC兼容。现在需要解决的问题是:在要求search函数必须接收char**类型变量作为只读参数的前提下,应如何编写代码?
// 线性搜索 #include <stdio.h> #include <stdlib.h> #include <string.h> int scanf_s(const char *format ,...); // 适配gcc和clang环境 // int search(const char **a, int n, char *key) { //※[1]不符合C语言规范⇒NG // int search(char * const *a, int n, char *key) { //※[2]⇒NG // int search(const char * const *a, int n, char *key) { //※[3]gcc和clang会报警告!! // int search(char const * const *a, int n, char *key) {//※[4]与上述情况相同 // int search(const char * const a[], int n, char *key) {//※[5]与上述情况相同 // int search(const char *a[], int n, char *key) { //※[6]我本以为没问题,但gcc和clang仍报警告!! int search(char **a, int n, char *key) { //最终结论:仅该写法可被gcc、clang和MSVC兼容!! int i = 0; for (i = 0; i < n; i++) { if (strcmp(a[i], key) == 0) return i; } return -1; /* 也可使用while循环!!参考书中展示的是该写法!! while (1) { if (i == n) return -1; if (strcmp(a[i], key) == 0) return i; i++; } */ } int main(void) { char **x; char *ky; int nx; int idx; int i; puts("Linear Search"); printf("元素个数?:"); scanf_s("%d", &nx); x = malloc(sizeof(char*) * nx); if (x == NULL) { printf("二级指针x内存分配失败!!\n"); exit(EXIT_FAILURE); } for (i = 0; i < nx; i++) { printf("x[%d]:", i); x[i] = malloc(sizeof(char) * 35); if (x[i] == NULL) { printf("指针数组x[%d]内存分配失败!!\n", i); exit(EXIT_FAILURE); } scanf_s("%s", x[i], 35); } printf("目标值:"); ky = malloc(sizeof(char) * 35); if (ky == NULL) { printf("目标值内存分配失败!!\n"); exit(EXIT_FAILURE); } // 或者 // ky = calloc(35, sizeof(char)); scanf_s("%s", ky, 35); idx = search(x, nx, ky); if (idx == -1) puts("未找到目标值。"); else printf("%s 在 x[%d] 中!!\n", ky, idx); free(ky); for (i = 0; i < nx; i++) { free(x[i]); } free(x); system("pause"); return 0; }
问题根源
C语言中,char**无法隐式转换为const char**,这是因为这种转换会带来const安全性漏洞——如果允许转换,就可以通过const char**修改原本char**指向的char*所指向的内容,违反const的只读约定。而const char* const*是正确的只读修饰方式:它表示指向const char的指针的const指针,既不能修改指针数组本身的元素(即a[i]不能被赋值),也不能通过a[i]修改字符串内容。
兼容三个编译器的正确写法
要让search函数接收char**作为只读参数,同时兼容gcc、clang和MSVC,有两种可行方案:
方案1:显式转换参数类型
保留search函数的参数为const char* const*,调用时将char**显式转换为该类型,同时建议把key参数也改为const char*(符合strcmp的参数语义,且函数不会修改key内容):
int search(const char* const* a, int n, const char* key) { int i = 0; for (i = 0; i < n; i++) { if (strcmp(a[i], key) == 0) return i; } return -1; } // main函数中调用时 idx = search((const char* const*)x, nx, ky);
方案2:函数内部转换指针
如果不想在调用时显式转换,可以在函数内部将char**转换为const char* const*,通过内部指针确保只读访问:
int search(char** a, int n, const char* key) { const char* const* const_a = (const char* const*)a; int i = 0; for (i = 0; i < n; i++) { if (strcmp(const_a[i], key) == 0) return i; } return -1; }
补充说明
- 注释中的
char * const *a(※[2])仅限制了指针数组本身不能被修改(即不能给a[i]赋值),但允许修改a[i]指向的字符串内容,不符合“只读”的完整要求。 - MSVC允许隐式转换
char**到const char**是它的非标准扩展,而gcc和clang遵循C标准,因此会触发警告。
内容的提问来源于stack exchange,提问作者user-polymetis

