You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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无法存储读取的文件内容。

修改步骤

  1. 将所有*c_char替换为*c.c_char,通过已有的c导入块引用C字符类型。
  2. 修复zip_name_locate调用中的cast为std.mem.ptrCast。
  3. 使用通用分配器分配足够的内存存储文件内容,避免长度为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 20:42:42