Zig开发游戏引擎报错:未声明标识符'c_char'求助
修复Zig调用libzip时
c_char未声明的错误 问题背景
我正在使用Zig与C++开发游戏引擎及对应游戏,编译时出现c_char未声明的错误,目标是打开zip格式的游戏存档包,读取其中的init.hesl初始化脚本(本质为Lua脚本),用于完成窗口参数、默认游戏参数等初始化工作(当settings目录无对应文件时使用该脚本)。
编译错误信息
main.zig:75:54: error: use of undeclared identifier 'c_char'
const zipArchive = zip.zip_open(std.mem.ptrCast(*c_char, zipFilePath.toSlice().ptr), 0, null);
当前代码
const std = @import("std"); // const garchive = @import("archive_files.zig"); const zip = @cImport({ @cInclude("zip.h"); }); const c = @cImport({ @cInclude("SDL.h"); }); pub fn main() anyerror!void { std.log.info("ROOFTOPS 2 PORT.", .{}); std.log.info(" BY LXCHN1v1", .{}); //var gpa = std.heap.GeneralPurposeAllocator(.{}){}; //const allocator = gpa.allocator(); std.log.info("trying to read rooftops.dconf", .{}); //var game_config = dconfparser.readDConfig("rooftops.dconf"); // load_config("rooftops2.dconf"); const main_archive_path = "main/game_00.garc"; const init_script_name = "scripts/init.hesl"; const initData = try loadFileFromZipArchive(main_archive_path, init_script_name); std.debug.print("INIT SCRIPT DATA:\n{}\n", .{initData}); _ = c.SDL_Init(c.SDL_INIT_VIDEO); defer c.SDL_Quit(); var window = c.SDL_CreateWindow("ROOFTOPS2", c.SDL_WINDOWPOS_CENTERED, c.SDL_WINDOWPOS_CENTERED, 800, 600, 0); defer c.SDL_DestroyWindow(window); var renderer = c.SDL_CreateRenderer(window, 0, c.SDL_RENDERER_PRESENTVSYNC); defer c.SDL_DestroyRenderer(renderer); mainloop: while (true) { var sdl_event: c.SDL_Event = undefined; while (c.SDL_PollEvent(&sdl_event) != 0) { switch (sdl_event.type) { c.SDL_QUIT => break :mainloop, else => {}, } } _ = c.SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0xff); _ = c.SDL_RenderClear(renderer); var rect = c.SDL_Rect{ .x = 0, .y = 0, .w = 60, .h = 60 }; const a = 0.001 * @intToFloat(f32, c.SDL_GetTicks()); const t = 2 * std.math.pi / 3.0; const r = 100 * @cos(0.1 * a); rect.x = 290 + @floatToInt(i32, r * @cos(a)); rect.y = 170 + @floatToInt(i32, r * @sin(a)); _ = c.SDL_SetRenderDrawColor(renderer, 0xff, 0, 0, 0xff); _ = c.SDL_RenderFillRect(renderer, &rect); rect.x = 290 + @floatToInt(i32, r * @cos(a + t)); rect.y = 170 + @floatToInt(i32, r * @sin(a + t)); _ = c.SDL_SetRenderDrawColor(renderer, 0, 0xff, 0, 0xff); _ = c.SDL_RenderFillRect(renderer, &rect); rect.x = 290 + @floatToInt(i32, r * @cos(a + 2 * t)); rect.y = 170 + @floatToInt(i32, r * @sin(a + 2 * t)); _ = c.SDL_SetRenderDrawColor(renderer, 0, 0, 0xff, 0xff); _ = c.SDL_RenderFillRect(renderer, &rect); c.SDL_RenderPresent(renderer); } } fn loadFileFromZipArchive(zipFilePath: []const u8, fileName: []const u8) ![]u8 { //const zipArchive = zip.zip_open(cast(*c_char, zipFilePath.toSlice().ptr), 0, null); //const zipArchive = zip.zip_open(zipFilePath.ptr.*c_char, 0, null); const zipArchive = zip.zip_open(std.mem.ptrCast(*c_char, zipFilePath.toSlice().ptr), 0, null); if (zipArchive == null) { return error.FailedToOpenZipArchive; } const index = zip.zip_name_locate(zipArchive, cast(*c_char, fileName.toSlice().ptr), 0); if (index < 0) { zip.zip_close(zipArchive); return error.FileNotFoundInZipArchive; } var fileInfo = zip.zip_stat_t{}; if (zip.zip_stat_index(zipArchive, index, 0, &fileInfo) != 0) { zip.zip_close(zipArchive); return error.FailedToGetFileInfo; } const file = zip.zip_fopen_index(zipArchive, index, 0); if (file == null) { zip.zip_close(zipArchive); return error.FailedToOpenFileInZipArchive; } const fileSize = @intCast(usize, fileInfo.size); var buffer: [0]u8 = undefined; const bytesRead = zip.zip_fread(file, buffer.ptr, fileSize); if (bytesRead < 0) { zip.zip_fclose(file); zip.zip_close(zipArchive); return error.FailedToReadFileFromZipArchive; } zip.zip_fclose(file); zip.zip_close(zipArchive); return buffer[0..bytesRead]; }
解决方案
错误原因
c_char是Zig对C语言char类型的绑定,需要通过标准库的std.c或者已导入的C头文件间接引用。你代码中已经通过c块导入了SDL.h,而SDL.h会包含标准C头文件,因此c.c_char是可用的;直接写c_char会因为未找到声明而报错。另外代码中还有两处语法错误:一是直接使用cast而非Zig标准的std.mem.ptrCast,二是长度为0的buffer无法存储读取的文件内容。
修改步骤
- 将所有
*c_char替换为*c.c_char,通过已有的c导入块引用C字符类型。 - 修复
zip_name_locate调用中的cast为std.mem.ptrCast。 - 使用通用分配器分配足够的内存存储文件内容,避免长度为0的数组导致的错误。
修改后的完整代码
const std = @import("std"); // const garchive = @import("archive_files.zig"); const zip = @cImport({ @cInclude("zip.h"); }); const c = @cImport({ @cInclude("SDL.h"); }); // 使用通用分配器,全局声明以便在函数中使用 var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); pub fn main() anyerror!void { defer _ = gpa.deinit(); // 退出时清理分配器 std.log.info("ROOFTOPS 2 PORT.", .{}); std.log.info(" BY LXCHN1v1", .{}); std.log.info("trying to read rooftops.dconf", .{}); const main_archive_path = "main/game_00.garc"; const init_script_name = "scripts/init.hesl"; const initData = try loadFileFromZipArchive(main_archive_path, init_script_name); defer allocator.free(initData); // 记得释放内存 std.debug.print("INIT SCRIPT DATA:\n{}\n", .{initData}); _ = c.SDL_Init(c.SDL_INIT_VIDEO); defer c.SDL_Quit(); var window = c.SDL_CreateWindow("ROOFTOPS2", c.SDL_WINDOWPOS_CENTERED, c.SDL_WINDOWPOS_CENTERED, 800, 600, 0); defer c.SDL_DestroyWindow(window); var renderer = c.SDL_CreateRenderer(window, 0, c.SDL_RENDERER_PRESENTVSYNC); defer c.SDL_DestroyRenderer(renderer); mainloop: while (true) { var sdl_event: c.SDL_Event = undefined; while (c.SDL_PollEvent(&sdl_event) != 0) { switch (sdl_event.type) { c.SDL_QUIT => break :mainloop, else => {}, } } _ = c.SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0xff); _ = c.SDL_RenderClear(renderer); var rect = c.SDL_Rect{ .x = 0, .y = 0, .w = 60, .h = 60 }; const a = 0.001 * @intToFloat(f32, c.SDL_GetTicks()); const t = 2 * std.math.pi / 3.0; const r = 100 * @cos(0.1 * a); rect.x = 290 + @floatToInt(i32, r * @cos(a)); rect.y = 170 + @floatToInt(i32, r * @sin(a)); _ = c.SDL_SetRenderDrawColor(renderer, 0xff, 0, 0, 0xff); _ = c.SDL_RenderFillRect(renderer, &rect); rect.x = 290 + @floatToInt(i32, r * @cos(a + t)); rect.y = 170 + @floatToInt(i32, r * @sin(a + t)); _ = c.SDL_SetRenderDrawColor(renderer, 0, 0xff, 0, 0xff); _ = c.SDL_RenderFillRect(renderer, &rect); rect.x = 290 + @floatToInt(i32, r * @cos(a + 2 * t)); rect.y = 170 + @floatToInt(i32, r * @sin(a + 2 * t)); _ = c.SDL_SetRenderDrawColor(renderer, 0, 0, 0xff, 0xff); _ = c.SDL_RenderFillRect(renderer, &rect); c.SDL_RenderPresent(renderer); } } fn loadFileFromZipArchive(zipFilePath: []const u8, fileName: []const u8) ![]u8 { // 修复c_char引用为c.c_char,简化ptr获取 const zipArchive = zip.zip_open(std.mem.ptrCast(*c.c_char, zipFilePath.ptr), 0, null); if (zipArchive == null) { return error.FailedToOpenZipArchive; } defer zip.zip_close(zipArchive); // 提前defer,避免重复写close // 修复cast为std.mem.ptrCast,同时修正c_char引用 const index = zip.zip_name_locate(zipArchive, std.mem.ptrCast(*c.c_char, fileName.ptr), 0); if (index < 0) { return error.FileNotFoundInZipArchive; } var fileInfo = zip.zip_stat_t{}; if (zip.zip_stat_index(zipArchive, index, 0, &fileInfo) != 0) { return error.FailedToGetFileInfo; } const file = zip.zip_fopen_index(zipArchive, index, 0); if (file == null) { return error.FailedToOpenFileInZipArchive; } defer zip.zip_fclose(file); // 提前defer const fileSize = @intCast(usize, fileInfo.size); // 使用分配器分配内存 const buffer = try allocator.alloc(u8, fileSize); errdefer allocator.free(buffer); // 读取失败时释放内存 const bytesRead = zip.zip_fread(file, buffer.ptr, fileSize); if (bytesRead < 0) { return error.FailedToReadFileFromZipArchive; } return buffer[0..@intCast(usize, bytesRead)]; }
额外说明
- 新增了
defer语句来自动清理资源(关闭zip、释放内存),避免手动管理遗漏导致的资源泄漏。 - 使用
GeneralPurposeAllocator分配内存,这是Zig中常用的通用内存分配器,适合大多数场景。 - 简化了
zipFilePath.toSlice().ptr为zipFilePath.ptr,因为[]const u8的ptr直接指向底层字节数组,无需额外转换。
内容的提问来源于stack exchange,提问作者Andrew LowLevelCoder
相关产品推荐
相关产品推荐

