C语言读取打印字符串数组:类型不匹配与段错误问题排查
问题描述
需要读取如下格式的输入:
2 string1 string2
第一行代表后续字符串的数量,且所有字符串长度相同。尝试编写代码读取并打印这些字符串,初始代码如下:
#include <stdlib.h> #include <stdio.h> #include <string.h> void print_string_array(char** a, int size){ for (int i=0; i<size; i++){ printf("%s\n", a[i]); } } int main() { int n; scanf("%d", &n); char* s; scanf("%s", s); int l = strlen(s); char input[n][l]; strcpy(s, input[0]); for (int i = 1; i < n; i++) { scanf("%s", s); strcpy(s, input[i]); } print_string_array(input, n); }
运行时出现警告和错误:
main.c: In function ‘main’: main.c:24:24: warning: passing argument 1 of ‘print_string_array’ from incompatible pointer type [-Wincompatible-pointer-types] 24 | print_string_array(input, n); | ^~~~~ | | | char (*)[(sizetype)(l)] main.c:5:32: note: expected ‘char **’ but argument is of type ‘char (*)[(sizetype)(l)]’ 5 | void print_string_array(char** a, int size){ | ~~~~~~~^ Segmentation fault (core dumped)
疑问:
- 为什么数组
input不被识别为char**类型? - 如何在保留程序适配任意长度输入字符串特性的前提下修复问题?
之后修改了代码:
#include <stdlib.h> #include <stdio.h> #include <string.h> void print_string_array(char** a, int size){ for (int i=0; i<size; i++){ printf("%s\n", a[i]); } } int main(){ int n; scanf("%d", &n); char s[100]; // 足够大的缓冲区 scanf("%s", s); int l = strlen(s); char input[n][l+1]; // +1 给 '\0' 留空间? strcpy(input[0], s); for (int i=1; i<n; i++){ scanf("%s", input[i]); } print_string_array(input, n); for (int i=0; i<n; i++){ free(input[i]); } free(input); }
仍存在错误和警告:
main.c: In function ‘main’: main.c:22:24: warning: passing argument 1 of ‘print_string_array’ from incompatible pointer type [-Wincompatible-pointer-types] 22 | print_string_array(input, n); | ^~~~~ | | | char (*)[(sizetype)(n)] main.c:5:32: note: expected ‘char **’ but argument is of type ‘char (*)[(sizetype)(n)]’ 5 | void print_string_array(char** a, int size){ | ~~~~~~~^ Segmentation fault (core dumped)
新增疑问:
- 针对警告,是否要把
a的类型改为char (*)[(sizetype)(n)]? - for循环中的段错误如何解决?
问题解答
1. 为什么char input[n][l]不是char**?
char**是指向字符指针的指针,它指向一块存储多个char*的内存,每个char*又指向独立分配的字符串内存,内存结构是分散的。而char input[n][l]是二维栈数组,内存是连续的:先存input[0]的所有字符,紧接着是input[1]的所有字符,以此类推。当二维数组名传入函数时,会退化为指向一维数组的指针(即char (*)[l]类型),和char**的内存结构完全不同,因此编译器会报类型不匹配警告。
2. 修复方案
方案一:适配二维数组,修改打印函数参数
既然使用了变长二维数组(VLA),可以直接修改打印函数的参数类型,同时修正段错误的根源:
#include <stdlib.h> #include <stdio.h> #include <string.h> // 参数改为指向字符数组的指针,适配二维数组类型 void print_string_array(char (*a)[], int size){ for (int i=0; i<size; i++){ printf("%s\n", a[i]); } } int main(){ int n; scanf("%d", &n); // 用栈上的固定长度数组作为缓冲区,避免野指针 char s[100]; scanf("%s", s); int l = strlen(s); // 字符串需要存储'\0'终止符,所以数组长度要+1 char input[n][l+1]; strcpy(input[0], s); for (int i=1; i<n; i++){ // 直接读入二维数组的对应元素,无需中间缓冲区 scanf("%s", input[i]); } print_string_array(input, n); // 注意:栈上的数组不需要手动free,之前的错误就是释放了栈内存 return 0; }
段错误的根源:
- 初始版本中
char* s;未初始化,是野指针,scanf("%s", s)会往随机内存写入数据,直接触发段错误; - 修改版本中
free(input)和free(input[i])完全错误,input是栈上分配的变长数组,只有malloc/calloc分配的堆内存才需要free,释放栈内存会导致非法内存访问。
方案二:改用char**动态分配内存
如果想保留print_string_array的char**参数,可以用动态分配实现:
#include <stdlib.h> #include <stdio.h> #include <string.h> void print_string_array(char** a, int size){ for (int i=0; i<size; i++){ printf("%s\n", a[i]); } } int main(){ int n; scanf("%d", &n); char s[100]; scanf("%s", s); int l = strlen(s); // 先分配存储n个char*的指针数组 char** input = malloc(n * sizeof(char*)); if (!input) { // 检查内存分配是否成功 perror("malloc failed"); return 1; } // 为每个字符串分配内存(+1存储'\0') input[0] = malloc((l+1) * sizeof(char)); strcpy(input[0], s); for (int i=1; i<n; i++){ scanf("%s", s); input[i] = malloc((l+1) * sizeof(char)); strcpy(input[i], s); } print_string_array(input, n); // 释放内存:先释放每个字符串,再释放指针数组 for (int i=0; i<n; i++){ free(input[i]); } free(input); return 0; }
此方案中input是标准的char**类型,和打印函数参数完全匹配,无类型警告,且所有内存均为堆分配,需按顺序释放。
关于警告的补充说明
不需要把参数改成char (*)[(sizetype)(n)],因为n是运行时变量,函数声明时无法直接使用。可以用char (*a)[](指向字符数组的指针),或者在C99及以后版本中,把字符串长度作为参数传入,写成void print_string_array(int str_len, char (*a)[str_len], int size),这样类型匹配更严谨。
内容的提问来源于stack exchange,提问作者J. Schmidt
相关产品推荐
相关产品推荐

