含数组与非数组成员的结构体数组内存布局及指针偏移段错误问题
段错误核心原因
你对C语言指针算术的规则理解有误:对指向具体类型的指针做加法时,编译器会自动按照目标类型的大小计算偏移,不需要手动乘以sizeof(类型)。
你的db1p是db_mold*类型,当你写db1p + n时,编译器实际计算的地址是(char*)db1p + n * sizeof(db_mold),刚好指向数组的第n个元素。但你手动给n乘了sizedb(也就是你测到的28),实际计算出来的地址是(char*)db1p + n * 28 * 28,访问的位置已经远远超出了db1[4]数组的合法内存范围,写入数据时触发非法访问,自然报段错误。
比如你访问第二个员工(下标1)时,实际访问的地址偏移是1*28*28=784字节,而你的db1数组总共才占4*28=112字节,早就越界到未分配给你的内存区域了。
你提到的其他知识点说明
- 结构体内存填充规则:核心对齐规则有两条
- 结构体每个成员的起始偏移地址,必须是该成员自身对齐值的整数倍(基本类型的对齐值等于自身大小,数组的对齐值等于数组成员类型的对齐值)
- 结构体整体的总大小,必须是结构体中最大对齐值的整数倍
你定义的db_mold大小为28是完全符合规则的:char nombre[20]占0-19字节,char sexo占20字节,之后的unsigned int sueldo对齐值为4,所以编译器会在21-23字节位置插入3个填充字节,让sueldo从24字节位置开始占4字节,总大小28刚好是4的整数倍,填充位置就在sexo和sueldo之间,这部分和你本次的段错误没有关系。
- 同时包含数组成员与非数组成员的结构体数组(你提到的AoSwAaNA)内存是完全连续的:和普通int数组、char数组的排布逻辑没有区别,每个结构体元素按顺序连续排列,结构体内部的数组成员直接存储在结构体对应的内存空间内,不存在额外的指针跳转,不需要特殊处理。
代码里的其他问题
user_load函数中strcpy的参数顺序写反了:strcpy(目标地址, 源地址),你当前写的strcpy(tmp, (db1p+(sizedb*i))->nombre);是把结构体里未初始化的内容拷贝到临时缓冲区tmp里,和你要把用户输入的姓名写入结构体的逻辑完全相反。- 你在函数调用时传入
sizeof(db_mold)作为参数本身是多余的:只要指针带明确的类型信息,编译器会自动完成指针偏移的计算,不需要手动传入类型大小。 - 输入逻辑存在换行符残留问题:
scanf读取数值后会在输入缓冲区留下换行符,不清理的话下一轮fgets会直接读到空内容。
修正参考
最稳妥的写法是直接用数组下标访问,编译器会自动处理偏移,几乎不会写错:
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char nombre[20]; char sexo; unsigned int sueldo; }db_mold; void preload(db_mold *); void user_load(db_mold *); int main(){ db_mold db1[4] = {0}; preload(db1); user_load(db1); return 0; } void preload(db_mold *db1p){ strcpy(db1p[0].nombre, "Franco"); db1p[0].sexo = 'M'; db1p[0].sueldo = 20000; strcpy(db1p[1].nombre, "Romina"); db1p[1].sexo = 'F'; db1p[1].sueldo = 23000; } void user_load(db_mold *db1p){ for (int i = 2; i < 4; i++){ printf("Ingrese el nombre del empleado %d: ", i); // 注意fgets会读入换行符,后续如果要做字符串匹配记得处理 fgets(db1p[i].nombre, sizeof(db1p[i].nombre), stdin); printf("Ingrese el Sexo (M o F) del empleado %d: ", i); db1p[i].sexo = getchar(); getchar(); // 吃掉输入性别后的换行 printf("Ingrese el salaio del empleado %d: ", i); scanf("%u", &db1p[i].sueldo); getchar(); // 吃掉scanf留下的换行,避免下一轮fgets读空 printf("Empleado Cargado!\n"); } }
如果你一定要用指针算术的写法,直接给指针加下标值即可,不要手动乘sizeof:
strcpy((db1p + i)->nombre, "Romina"); // 正确写法
内容的提问来源于stack exchange,提问作者fpp
相关产品推荐
相关产品推荐

