C语言运行时候出现栈溢出、段错误(Segmentation fault)、异常码解决?
将下面源代码的MAX_KENNWERTE 值修改成1000就报错了?
报出fail section错误,意思是错误段。
原因是:
最可能的两个原因:
- 栈爆了(Stack overflow)
- 你在 main 里一次性在栈上放了 6 个大数组:
- Kennwert newarr/oldarr/merged(3份)
- Kennlinien newarrien/oldarrien/mergedien(3份)
- 以当前结构体大小粗算:
- sizeof(Kennwert) ≈ 264 字节
- sizeof(Kennlinien) ≈ 520 字节
- MAX_KENNWERTE=1000 时,栈上内存 ≈ 31000264 + 31000520 ≈ 2.35 MB
- Windows/MSVC 默认主线程栈大小常见为 1MB,1000 会溢出,256(约 0.6MB)还能勉强过。
- 解决:
- 把这些大数组改成堆分配(malloc/calloc),或做成 static(放到全局/BSS),或提高栈大小(不推荐作为首选)。
- 示例(推荐堆):
c
Kennwert* newarr = calloc(MAX_KENNWERTE, sizeof(*newarr)); Kennlinien* newarrien = calloc(MAX_KENNWERTE, sizeof(*newarrien)); Kennwert* oldarr = calloc(MAX_KENNWERTE, sizeof(*oldarr)); Kennlinien* oldarrien = calloc(MAX_KENNWERTE, sizeof(*oldarrien)); Kennwert* merged = calloc(MAX_KENNWERTE, sizeof(*merged)); Kennlinien* mergedien = calloc(MAX_KENNWERTE, sizeof(*mergedien)); if (!newarr || !newarrien || !oldarr || !oldarrien || !merged || !mergedien) { /* 处理内存不足 */ } ... free(newarr); free(newarrien); free(oldarr); free(oldarrien); free(merged); free(mergedien);
- 或临时把数组前加 static 关键字,移到静态区(不占栈):
c
static Kennwert newarr[MAX_KENNWERTE]; static Kennlinien newarrien[MAX_KENNWERTE]; ...
- 越界访问(addnamesub/subnamesub 按“遇到 name[0]==0 就停”的错误遍历)
- 你在 write_bin 中对 merged/mergedien 调用了 subnamesub,但这两个数组只有前 n/m 个元素被填充,后面是未初始化的栈/堆内存,未必恰好 name[0]==0。你的循环会越界跑飞,数组越大越容易触发崩溃(1000 更容易出错)。
- 修正:传入计数 n、m,严格按计数遍历;不要用 name[0]==0 当终止条件。写完文件再恢复内存内容。
c
static void addnamesub(Kennwert karr[], int n, Kennlinien karrien[], int m) {for (int i=0; i<n; ++i)for (int j=0; j<MAX_NAME_LEN && karr[i].name[j]; ++j)karr[i].name[j] = (char)((unsigned char)karr[i].name[j] + (j + 5));for (int i=0; i<m; ++i)for (int j=0; j<MAX_NAME_LEN && karrien[i].name[j]; ++j)karrien[i].name[j] = (char)((unsigned char)karrien[i].name[j] + (j + 5)); } static void subnamesub(Kennwert karr[], int n, Kennlinien karrien[], int m) {for (int i=0; i<n; ++i)for (int j=0; j<MAX_NAME_LEN && karr[i].name[j]; ++j)karr[i].name[j] = (char)((unsigned char)karr[i].name[j] - (j + 5));for (int i=0; i<m; ++i)for (int j=0; j<MAX_NAME_LEN && karrien[i].name[j]; ++j)karrien[i].name[j] = (char)((unsigned char)karrien[i].name[j] - (j + 5)); }
c
// read_bin 成功读取后再解码 addnamesub(karr, n, karrien, m);// write_bin 写前编码、写后恢复 static int write_bin(const char* fname, Kennwert karr[], int n, Kennlinien karrien[], int m) {subnamesub(karr, n, karrien, m);FILE* fp = fopen(fname, "wb");if (!fp) { addnamesub(karr, n, karrien, m); return 0; }fwrite(&n, sizeof(int), 1, fp);fwrite(&m, sizeof(int), 1, fp);if (n>0) fwrite(karr, sizeof(Kennwert), n, fp);if (m>0) fwrite(karrien, sizeof(Kennlinien), m, fp);fclose(fp);addnamesub(karr, n, karrien, m);return 1; }
另外几条会让“1000 更容易出错”的隐患(建议一并修):
- main 里把 nold、mold 初始化为 0(否则 binin 为 NULL 时未定义行为):
c
int nold = 0, mold = 0;
- read_bin 同时校验并截断 m,且在 fread 全部成功后再解码:
c
if (r1!=1 || r2!=1 || n<0 || m<0) { fclose(fp); return (TwoInts){0,0}; } if (n > maxn) n = maxn; if (m > maxn) m = maxn; if (n>0 && fread(karr, sizeof(Kennwert), n, fp) != (size_t)n) { fclose(fp); return (TwoInts){0,0}; } if (m>0 && fread(karrien, sizeof(Kennlinien), m, fp) != (size_t)m) { fclose(fp); return (TwoInts){0,0}; } addnamesub(karr, n, karrien, m);
- 解析 Kennlinie 的 test_wertp 时限制 idx,避免越界:
c
int idx = 0; for (char* tok = strtok(valbuf, ";"); tok && idx < MAX_ARRAY_LEN; tok = strtok(NULL, ";")) {karrien[m].test_wertp[idx++] = atof(tok); }
总结
- 直接把 MAX_KENNWERTE 提到 1000 会因为“大的栈数组”+“错误的遍历越界”而更容易崩溃。把大数组移到堆或静态区,并修正 add/sub-name 的遍历方式,基本就能解决你遇到的报错。若仍有问题,提供具体的崩溃信息(栈溢出、段错误、异常码)。
源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_KENNWERTE 10240 // 最大支持的Kennwert数据项数
#define MAX_KENNWERTE_2 10240 // 最大支持的Kennwert数据项数
#define MAX_NAME_LEN 128 // Kennwert中Name字段的最大长度
#define MAX_ARRAY_LEN 32 // 最大数组长度typedef struct {int n;int m;
} TwoInts;
// 描述一个 Kennwert,包含名称和一个数值
typedef struct {char name[MAX_NAME_LEN];double test_wertp;char Umrechnungsformel[MAX_NAME_LEN];
} Kennwert;
typedef struct {char name[MAX_NAME_LEN];double test_wertp[MAX_ARRAY_LEN];char Umrechnungsformel[MAX_NAME_LEN];double anzahl_stuetzstellen_x;
} Kennlinien;/*** 去除字符串s左右的空白字符(如空格、换行等)* 返回结果的起始地址*/
static char* trim(char* s) {char* end;// 跳过前导空白while(isspace((unsigned char)*s)) s++;if(*s == 0) return s; // 空字符串直接返回// 找到尾部非空白的字符end = s + strlen(s) - 1;while(end > s && isspace((unsigned char)*end)) end--;*(end+1) = 0; // 结尾加上串结束符return s;
}/*** 从给定XML字符串(xml)中,查找<tag>标签的内容,放入buf。查找从xml起始处。* 返回找到内容后下一个字符的指针;如果未找到返回NULL。* buf用于输出,长度为bufsize。* 这个函数的作用是定位如 <Name>xxx</Name> 里的xxx内容。*/
static const char* get_tag_text(const char* xml, const char* tag, char* buf, size_t bufsize) {char stag[64], etag[64];snprintf(stag, sizeof(stag), "<%s>", tag); // <标签名> 这个是打印标签的名称么?snprintf(etag, sizeof(etag), "</%s>", tag); // </标签名>const char* ps = strstr(xml, stag); // 找第一个开始标签const char* pe = ps ? strstr(ps, etag) : NULL; // 找结束标签if(ps && pe) {ps += strlen(stag); // 内容实际位置 去除标签头size_t len = pe-ps;if(len >= bufsize) len = bufsize-1; // 安全拷贝strncpy(buf, ps, len);// 拷贝字符串buf[len] = 0;return pe + strlen(etag); // 返回下一个位置及结束的</Name>位置 删除标签尾部}if(buf && bufsize) buf[0]=0; // 没找到返回空串return NULL;
}/*** 在整个xml内容中查找每一个 <Kennwert> ... </Kennwert> 段落,并提取* <Name> 和 <test_wertp> 字段。存入karr数组,返回Kennwert实际数目。*/
static TwoInts parse_kennwerte(const char* xml, Kennwert karr[], Kennlinien karrien[], int maxn) {int n = 0;int m = 0;TwoInts ret; // Kennlinien的计数器const char* p = xml; // 查找指针:每次都从上一次的</Kennwert>后继续找char kbuf[2048]; // 临时保存单个 <Kennwert> ... </Kennwert> 内容while((p = strstr(p, "<Kennwert>")) && n < maxn) {const char* pend = strstr(p, "</Kennwert>");// 找到配套的结束标签位置if(!pend) break; // 未找到配套的结束标签,终止size_t klen = pend - p + strlen("</Kennwert>");if(klen >= sizeof(kbuf)) klen = sizeof(kbuf)-1; // 防止越界strncpy(kbuf, p, klen); kbuf[klen] = 0; // 拷贝本次Kennwert片段// 提取 <Name>xxx</Name> 和 <test_wertp>xxx</test_wertp>char name[MAX_NAME_LEN] = "", valbuf[64] = "";char formel[MAX_NAME_LEN] = "", formelbuf[64] = "";get_tag_text(kbuf, "Name", name, sizeof(name));get_tag_text(kbuf, "Umrechnungsformel", formelbuf, sizeof(formelbuf));get_tag_text(kbuf, "test_wertp", valbuf, sizeof(valbuf));if(strlen(name)>0) {strncpy(karr[n].name, trim(name), MAX_NAME_LEN-1); // 去左右空白, 拷贝karr[n].name[MAX_NAME_LEN-1]=0;karr[n].test_wertp = (strlen(valbuf)>0) ? atof(valbuf) : 0.0; // 转为double// int a =atof("0x15"); // 这里的atof是将字符串转为doublestrncpy(karr[n].Umrechnungsformel, trim(formelbuf), MAX_NAME_LEN-1);karr[n].Umrechnungsformel[MAX_NAME_LEN-1]=0; // 去左右空白++n; // 下一个Kennwert}p = pend + strlen("</Kennwert>"); // 移动到下一个Kennwert的搜索起点}p =xml;while((p = strstr(p, "<Kennlinie>")) && m < maxn) {const char* pend = strstr(p, "</Kennlinie>");// 找到配套的结束标签位置if(!pend) break; // 未找到配套的结束标签,终止size_t klen = pend - p + strlen("</Kennlinie>");if(klen >= sizeof(kbuf)) klen = sizeof(kbuf)-1; // 防止越界strncpy(kbuf, p, klen); kbuf[klen] = 0; // 拷贝本次Kennwert片段// 提取 <Name>xxx</Name> 和 <test_wertp>xxx</test_wertp>char name[MAX_NAME_LEN] = "", valbuf[64] = "";char formel[MAX_NAME_LEN] = "", formelbuf[64] = "";char anzahl_stuetzstellen_x[64] = "", valbuf2[64] = "";get_tag_text(kbuf, "Name", name, sizeof(name));get_tag_text(kbuf, "test_wertp", valbuf, sizeof(valbuf));get_tag_text(kbuf, "Umrechnungsformel", formelbuf, sizeof(formelbuf));get_tag_text(kbuf, "anzahl_stuetzstellen_x", valbuf2, sizeof(valbuf2));if(strlen(name)>0) {strncpy(karrien[m].name, trim(name), MAX_NAME_LEN-1); // 去左右空白, 拷贝karrien[m].name[MAX_NAME_LEN-1]=0;char *array2dou = strtok(valbuf, ";");int idx = 0;while (array2dou != NULL){karrien[m].test_wertp[idx++] = (strlen(array2dou)>0) ? atof(array2dou) : 0.0; // 转为doublearray2dou = strtok(NULL, ";");// 之后都传NULL}strncpy(karrien[m].Umrechnungsformel, trim(formelbuf), MAX_NAME_LEN-1);karrien[m].Umrechnungsformel[MAX_NAME_LEN-1]=0; // 去左右空白karrien[m].anzahl_stuetzstellen_x = (strlen(valbuf2)>0) ? atof(valbuf2) : 0.0; // 转为double++m; // 下一个Kennwert}p = pend + strlen("</Kennlinie>"); // 移动到下一个Kennwert的搜索起点}ret.n = n; // 返回实际读取的Kennwert数量ret.m = m; // 返回实际读取的Kennlinien数量return ret;
}/*** 读取整个文本文件到一个malloc分配的缓冲区* 返回buffer指针。psz可获取文件长度* 调用者用完后须 free(buffer)*/
static char* readfile(const char* fname, size_t* psz) {FILE* fp = fopen(fname, "rb");if(!fp) return NULL;fseek(fp, 0, SEEK_END);//文件尾size_t sz = ftell(fp);//获取文件的大小fseek(fp, 0, SEEK_SET);//文件首char* buf = (char*)malloc(sz+1);//多一个字节存放\0if(!buf) { fclose(fp); return NULL;}fread(buf, 1, sz, fp); buf[sz]=0;//读取内容并加\0fclose(fp);if (psz) *psz = sz;return buf;
}static int addnamesub(Kennwert karr[], Kennlinien karrien[])
{int dex = 0;while (karr[dex].name[0] != '\0'){int dexstr =0;while (karr[dex].name[dexstr] != '\0'){karr[dex].name[dexstr] = karr[dex].name[dexstr] + (dexstr + 5);dexstr++;}dex++;}dex = 0;while (karrien[dex].name[0] != '\0'){int dexstr =0;while (karrien[dex].name[dexstr] != '\0'){karrien[dex].name[dexstr] = karrien[dex].name[dexstr] + (dexstr + 5);dexstr++;}dex++;}return 0;
}
static int subnamesub(Kennwert karr[], Kennlinien karrien[])
{int dex = 0;while (karr[dex].name[0] != '\0'){int dexstr =0;while (karr[dex].name[dexstr] != '\0'){karr[dex].name[dexstr] = karr[dex].name[dexstr] - (dexstr + 5);dexstr++;}dex++;}dex = 0;while (karrien[dex].name[0] != '\0'){int dexstr =0;while (karrien[dex].name[dexstr] != '\0'){karrien[dex].name[dexstr] = karrien[dex].name[dexstr] - (dexstr + 5);dexstr++;}dex++;} return 0;
}/*** 读取旧的二进制(Kennwert)文件到karr里,返回read到的记录数。* 文件不存在或内容异常返回0。*/
static TwoInts read_bin(const char* fname, Kennwert karr[], Kennlinien karrien[],int maxn) {TwoInts ret={0,0};FILE* fp = fopen(fname, "rb");if (!fp) return ret;//(TwoInts){0, 0};int n = 0, m =0, r3,r4;// r1 = fread(&n, sizeof(int), 1, fp); // 先读数量// r2 = fread(&m, sizeof(int), 1, fp);if( fread(&n, sizeof(int), 1, fp)!=1 ||fread(&m, sizeof(int), 1, fp)!=1 ||n > maxn) n = n>maxn ? maxn : 0;r3 = n>0 ? fread(karr, sizeof(Kennwert), n, fp) : 0; // 再读内容r4 = m>0 ? fread(karrien, sizeof(Kennlinien), m, fp) : 0; // 再读fclose(fp);addnamesub(karr, karrien);if (r3==n) ret.n = n;if (r4==m) ret.m = m; // 返回实际读取的数量return ret; // 成功读取就返回n,否则返回0
}/*** 写入Kennwert数组到二进制文件 fname, 共n条* 文件格式: 先int写n, 然后n个Kennwert结构体* 写成功则返回1,否则返回0*/
static int write_bin(const char* fname, Kennwert karr[], int n, Kennlinien karrien[], int m) {subnamesub(karr, karrien);FILE* fp = fopen(fname, "wb");if (!fp) return 0;fwrite(&n, sizeof(int), 1, fp);fwrite(&m, sizeof(int), 1, fp);if(n>0) fwrite(karr, sizeof(Kennwert), n, fp);if(m>0) fwrite(karrien, sizeof(Kennlinien), m, fp);fclose(fp);return 1;
}/*** 在karr[0..n-1]中查找name,若找到返回下标,否则返回-1* 用于判重,防止合并重复*/
static int find_by_name(const Kennwert karr[], int n, const char* name) {for(int i=0; i<n; ++i)if(strcmp(karr[i].name, name)==0)return i;return -1;
}
static int find_by_nameien(const Kennlinien karr[], int n, const char* name) {for(int i=0; i<n; ++i)if(strcmp(karr[i].name, name)==0)return i;return -1;
}/*** 合并两个Kennwert数组:newarr为新(优先级高),oldarr为旧。* merged数组先放全部newarr内容,然后再放oldarr中newarr没有的项。* 返回合并后实际数量,不会超过mergedmax。*/
static int merge_kennwerte(Kennwert oldarr[], int nold,Kennwert newarr[], int nnew,Kennwert merged[], int mergedmax) {int n=0;// 先拷贝所有新的for(int i=0; i<nnew && n<mergedmax; ++i)merged[n++] = newarr[i];// 再加上旧的但新里没有的for(int i=0; i<nold && n<mergedmax; ++i){int a =find_by_name(newarr, nnew, oldarr[i].name);if(find_by_name(newarr, nnew, oldarr[i].name) < 0)merged[n++] = oldarr[i];}return n;
}
static int merge_kennwerteien(const Kennlinien oldarr[], int nold,const Kennlinien newarr[], int nnew,Kennlinien merged[], int mergedmax) {int n=0;// 先拷贝所有新的for(int i=0; i<nnew && n<mergedmax; ++i)merged[n++] = newarr[i];// 再加上旧的但新里没有的for(int i=0; i<nold && n<mergedmax; ++i){int a =find_by_nameien(newarr, nnew, oldarr[i].name);if(find_by_nameien(newarr, nnew, oldarr[i].name) < 0)merged[n++] = oldarr[i];}return n;
}
int main(int argc, char** argv) {// 文件名配置。实际工作中建议用命令行参数,这里演示直接写死char xmlfile1[32] = "Rte_App_Cfile2Xml.xml";const char* xmlfile = xmlfile1; // xml文件名// char binin1[32] ="olddata1.bin";const char* binin = NULL;//binin1; // 旧二进制数据文件(如有)// const char* binin = binin1; // 旧二进制数据文件(如有)char binout1[32] = "newdata1.bin";const char* binout = binout1; // 新的二进制文件名/* STEP1: 读取xml文件内容并解析 */size_t xmlsz;char* xmlbuf = readfile(xmlfile, &xmlsz);if(!xmlbuf) { puts("Error: cannot read XML file."); return 2;}static Kennwert newarr[MAX_KENNWERTE];static Kennlinien newarrien[MAX_KENNWERTE];TwoInts retnm = parse_kennwerte(xmlbuf, newarr, newarrien, MAX_KENNWERTE);int nnew = retnm.n; // 实际读取的Kennwert数量int mnew = retnm.m; // 实际读取的Kennlinien数量free(xmlbuf); // 释放xml缓冲区printf("读取到%d条Kennwert记录.\n", nnew);printf("读取到%d条Kennlinie记录.\n", mnew);/* STEP2: 读取旧bin文件,若有 */static Kennwert oldarr[MAX_KENNWERTE];static Kennlinien oldarrien[MAX_KENNWERTE];TwoInts nmold;int nold = 0, mold = 0;if(binin){nmold = read_bin(binin, oldarr, oldarrien,MAX_KENNWERTE);nold = nmold.n; // 实际读取的Kennwert数量mold = nmold.m;}// 实际读取的Kennlinien数量/* STEP3: 合并,优先用新数据,再添加老数据中未覆盖的 */ static Kennwert merged[MAX_KENNWERTE];static Kennlinien mergedien[MAX_KENNWERTE];int nmerged = merge_kennwerte(oldarr, nold, newarr, nnew, merged, MAX_KENNWERTE);int mmerged = merge_kennwerteien(oldarrien, mold, newarrien, mnew, mergedien, MAX_KENNWERTE);/* STEP4: 写入到新的bin文件 */if(!write_bin(binout, merged, nmerged, mergedien, mmerged)) {puts("Error: failed to write bin.");return 3;}printf("合并完成!新文件%s,Kennwert=%d,Kennlinie=%d。\n", binout, nmerged, mmerged);return 0;
}