C语言如何从字符串提取多个double值存入动态double数组
核心问题
你之前提取double值始终拿到第一个数,本质是没有正确使用strtod的第二个参数记录解析结束位置,每次都从字符串头部开始解析,自然永远得到第一个浮点数。另外原代码还有几个隐藏bug:ifEnd判断没有做边界检查,输入字符数不足3个时访问index-2、index-3会触发内存越界;getchar()返回值是int类型,直接存入char变量会导致EOF判断失效;用字符串遍历索引当double数组下标,两者长度不对应,必然会出现数组越界或者值重复的问题。
实现思路
- 读取输入时用
int类型变量存储getchar()返回值,避免EOF判断错误,终止符判断加边界检查,不要访问负索引。 - 提取浮点数时不需要手动逐字符遍历字符串挪位置,直接用
strtod的第二个输出参数记录当前解析到的位置:每次解析完一个double,就把当前字符串指针移到解析结束的位置,跳过所有无关字符,直到遇到字符串结束符。 - 单独维护double数组的元素计数,不要和字符串的索引混用,动态扩容时基于实际存入的元素个数判断。
可直接运行的修正代码
#define _CRT_SECURE_NO_WARNINGS #include <stdlib.h> #include <stdio.h> #include <stdbool.h> #include <ctype.h> #include <string.h> // 修正终止符判断,避免数组越界 bool ifEnd(const char* userString, size_t len) { if (len >= 3 && strncmp(userString + len - 3, "END", 3) == 0) return true; if (len >= 4 && strncmp(userString + len - 4, "QUIT", 4) == 0) return true; return false; } int main() { size_t str_buf_size = 10; size_t str_len = 0; char* input_str = malloc(str_buf_size * sizeof(char)); if (!input_str) { printf("内存分配失败,程序退出\n"); return EXIT_FAILURE; } int ch; // 读取所有输入直到EOF或输入END while ((ch = getchar()) != EOF) { ch = toupper(ch); // 输入字符串动态扩容 if (str_len == str_buf_size - 1) { str_buf_size *= 2; char* tmp = realloc(input_str, str_buf_size * sizeof(char)); if (!tmp) { printf("内存不足,程序退出\n"); free(input_str); return EXIT_FAILURE; } input_str = tmp; } input_str[str_len++] = (char)ch; // 换行符替换为空格方便后续解析 if (input_str[str_len - 1] == '\n') input_str[str_len - 1] = ' '; // 判断是否输入了终止符 if (ifEnd(input_str, str_len)) { // 截掉末尾的END标记 str_len -= 3; break; } } input_str[str_len] = '\0'; // 从输入字符串中提取所有double值 size_t dbl_buf_size = 10; size_t dbl_count = 0; double* dbl_arr = malloc(dbl_buf_size * sizeof(double)); if (!dbl_arr) { printf("内存分配失败,程序退出\n"); free(input_str); return EXIT_FAILURE; } char* cur_pos = input_str; char* end_pos = NULL; while (*cur_pos != '\0') { // 跳过当前位置所有和浮点数无关的字符 while (*cur_pos != '\0' && *cur_pos != '.' && *cur_pos != '-' && *cur_pos != '+' && !isdigit((unsigned char)*cur_pos)) { cur_pos++; } if (*cur_pos == '\0') break; // 解析当前位置的浮点数 double val = strtod(cur_pos, &end_pos); // 过滤无效解析,比如单独的+、-、.不能算有效浮点数 if (end_pos == cur_pos) { cur_pos++; continue; } // double数组动态扩容 if (dbl_count == dbl_buf_size) { dbl_buf_size *= 2; double* tmp = realloc(dbl_arr, dbl_buf_size * sizeof(double)); if (!tmp) { printf("内存不足,程序退出\n"); free(input_str); free(dbl_arr); return EXIT_FAILURE; } dbl_arr = tmp; } dbl_arr[dbl_count++] = val; // 将当前解析位置移到本次解析结束的位置,准备下一轮提取 cur_pos = end_pos; } // 以下是测试逻辑,可自行替换为后续业务逻辑 printf("共提取到%zu个double类型数值:\n", dbl_count); for (size_t i = 0; i < dbl_count; i++) { printf("%lf ", dbl_arr[i]); } printf("\n"); free(input_str); free(dbl_arr); return EXIT_SUCCESS; }
关键注意点
strtod的第二个参数是输出参数,函数执行完成后会把「第一个无法解析为浮点数的字符地址」写入该指针,只要每次从这个地址继续向后查找下一个合法浮点数即可,不需要手动维护遍历索引。- 不要把
getchar()的返回值直接存入char类型变量:EOF的值为-1,如果当前环境的char是无符号类型,将永远无法匹配到EOF。 - 判断字符串前后缀直接用
strncmp实现,不要手动写索引遍历,很容易出现越界问题。 - 每次调用
strtod后要校验返回的结束指针是否和传入的起始指针相等,如果相等说明当前位置没有合法浮点数,需要跳过当前字符继续向后查找。
内容的提问来源于stack exchange,提问作者SarcasticLeo
相关产品推荐
相关产品推荐

