使用C结构体构建地图位置图时遇只读变量赋值错误求助
问题分析与解决方案
咱们先拆解你遇到的两个核心问题:编译器报错的直接原因,还有隐藏的致命内存风险,然后一步步给出修复方案。
1. 编译器错误:assignment to read only variable
你写的current_location = current_location.portals[0];其实犯了两个小错误:
- 类型不匹配:
portals[0]是location*指针类型,而current_location是location结构体变量,直接把指针赋值给结构体变量完全不对,正确的写法应该是解引用指针拿到结构体内容:current_location = *current_location.portals[0]; - const成员的赋值限制:你的
location结构体里有多个const修饰的成员(比如const point portal_in、const byte width),这些成员一旦初始化就不允许被修改。而结构体赋值本质是逐个成员复制,尝试把另一个结构体的const成员复制过来时,编译器会判定这是修改只读变量,直接报错。
2. 隐藏的致命问题:悬空指针
你的build_locations函数里,loc是局部变量,函数执行完毕后,这个变量占用的内存会被系统回收。但你把&loc赋值给了village.portals[0],这就导致返回的village里的portals[0]指向了一块已经无效的内存——后续访问这个指针会触发未定义行为(比如程序崩溃、出现奇怪的地图数据)。
修复方案
针对这两个问题,最合理的调整是用指针管理所有位置实例,避免结构体复制和局部变量指针失效的问题:
方案A:全局静态变量(适合小型设备,无需内存分配)
因为小型游戏设备通常内存有限,用全局静态变量可以避免动态内存管理的麻烦,同时保证位置实例的内存一直有效:
typedef unsigned char byte; static const byte VILLAGE[] = { /* 你的地图数据 */ }; static const byte HOUSE[] = { /* 你的地图数据 */ }; typedef struct point { int x; int y; } point; typedef struct location { const point portal_in; const point portal_out; const byte *map; const byte width; const byte height; struct location *portals[8]; point player; struct location *return_to; } location; // 定义全局静态的位置实例,内存会在程序运行期间一直存在 static location village = { .portal_in={15, 14}, .portal_out={0, 0}, .map=&VILLAGE[0], .width=32, .height=16, .player={15, 14}, .return_to=NULL }; static location house = { .portal_in={8, 7}, .portal_out={11, 10}, .map=&HOUSE[0], .width=8, .height=8, .player={0, 0}, .return_to=&village }; // 初始化位置间的链接 void init_locations() { village.portals[0] = &house; } int main(void) { init_locations(); // 用指针跟踪当前位置,避免结构体复制 location *current_location = &village; if (current_location->player.x == 5 && current_location->player.y == 4) { // 直接切换指针,完全避开const成员的赋值问题 current_location = current_location->portals[0]; } // 后续访问位置成员用 -> 操作符,比如 current_location->player.x return 0; }
方案B:动态内存分配(适合灵活扩展的场景)
如果你的设备支持malloc,可以用动态分配来创建位置实例,适合需要动态生成地图的场景:
// ... 结构体定义和地图数据与之前一致 ... location* build_village() { location *village = malloc(sizeof(location)); if (!village) return NULL; // 处理内存分配失败的情况 *village = (location){ .portal_in={15, 14}, .portal_out={0, 0}, .map=&VILLAGE[0], .width=32, .height=16, .player={15, 14}, .return_to=NULL }; return village; } location* build_house(location *return_loc) { location *house = malloc(sizeof(location)); if (!house) return NULL; *house = (location){ .portal_in={8, 7}, .portal_out={11, 10}, .map=&HOUSE[0], .width=8, .height=8, .player={0, 0}, .return_to=return_loc }; return house; } int main(void) { location *village = build_village(); location *house = build_house(village); village->portals[0] = house; location *current_location = village; if (current_location->player.x == 5 && current_location->player.y == 4) { current_location = current_location->portals[0]; } // 程序结束前记得释放内存 free(house); free(village); return 0; }
修复逻辑说明
- 用指针管理位置:直接切换指针不需要复制整个结构体,完美避开了
const成员无法被赋值的问题,同时也更高效(节省结构体复制的内存和时间)。 - 解决悬空指针:全局静态变量的内存会一直存在,动态分配的内存直到你调用
free才会被回收,不会出现局部变量销毁后指针失效的问题。
如果坚持要保留结构体赋值的方式,只能去掉location结构体里所有成员的const修饰,但这样会失去const带来的安全性(比如防止意外修改地图尺寸、传送门坐标),所以更推荐用指针的方案。
内容的提问来源于stack exchange,提问作者MalphasWats
相关产品推荐
相关产品推荐

