How to pass [*c][*c] pointer to c function?

When accessed the second element, the program reported a Segmentation fault at address error, so I guess it should be my wrong of the [*c][*c] pointer input parameter?

// get devices
//
var playback_devices: [cap]*ma.ma_device_info = undefined;
var playback_devices_count: u32 = undefined;
var capture_devices: [cap]*ma.ma_device_info = undefined;
var capture_devices_count: u32 = undefined;
//
// fn ma_context_get_devices(pContext: [*c]ma_context, ppPlaybackDeviceInfos: [*c][*c]ma_device_info, pPlaybackDeviceCount: [*c]ma_uint32, ppCaptureDeviceInfos: [*c][*c]ma_device_info, pCaptureDeviceCount: [*c]ma_uint32) ma_result
//
if (ma.ma_context_get_devices(self.ma_context, @ptrCast(&playback_devices), &playback_devices_count, @ptrCast(&capture_devices), &capture_devices_count) != ma.MA_SUCCESS) {
    return error.MAContextGetDevices;
}

// init result
//
var result = try DeviceInfoList.init(self.allocator, cap);
errdefer result.deinit();

// every DeviceInfoList own an arena_allocator
//
const arena_allocator = result.arena.allocator();

// append playback device info into result
// 
for (playback_devices[0 .. playback_devices_count]) |dev| {
    log.info("dev name: {s}",.{dev.name});  // ERROR: access 2nd dev 
    const device_info = try arena_allocator.create(DeviceInfo);
    try result.playbacks.append(device_info);
    try device_info.copyFrom(arena_allocator, dev);
}

error info:

info: dev name: 扬声器 (Realtek(R) Audio)
info: nativeFormatCount: 0
Segmentation fault at address 0xffffffffffffffff
E:\code\me\test-zig\miniaudio.zig\src\main.zig:1003:44: 0xf87c04 in getDevic
eInfoList__anon_7125 (miniaudio.zig.exe.obj)
            log.info("dev name: {s}", .{dev.name});

probably you want [cap]?* instead of [cap]*. it looks like your library is filling the remaining pointers with null, which is the cause of your segfault, since the print statement isn’t prepared to handle null pointers (since Zig pointers may not be null).

as a tip, if you need to @ptrCast into a [*c] pointer to get your code to compile, you’re probably not quite correctly using your types

1 Like

I changed it to [cap]?*, But the same error.

Lets look at this c example miniaudio - A single file audio playback and capture library. scroll down a bit:

ma_context context;
if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) {
    // Error.
}

ma_device_info* pPlaybackInfos;
ma_uint32 playbackCount;
ma_device_info* pCaptureInfos;
ma_uint32 captureCount;
if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) {
    // Error.
}

// Loop over each device info and do something with it. Here we just print the name with their index. You may want
// to give the user the opportunity to choose which device they'd prefer.
for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) {
    printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name);
}

pPlaybackInfos is not an array it is just a c pointer then &pPlaybackInfos turns it into a pointer to a pointer, presumably the method then sets that pointer to something that can be indexed into with pPlaybackInfos[iDevice].name.

I think the zig equivalent is taking the reference to a multi-item pointer:

var playback_devices: [*:null]?ma.ma_device_info = undefined;
var playback_devices_count: u32 = undefined;
var capture_devices: [*:null]?ma.ma_device_info = undefined;
var capture_devices_count: u32 = undefined;
//
// fn ma_context_get_devices(pContext: [*c]ma_context, ppPlaybackDeviceInfos: [*c][*c]ma_device_info, pPlaybackDeviceCount: [*c]ma_uint32, ppCaptureDeviceInfos: [*c][*c]ma_device_info, pCaptureDeviceCount: [*c]ma_uint32) ma_result
//
if (ma.ma_context_get_devices(self.ma_context, &playback_devices, &playback_devices_count, &capture_devices, &capture_devices_count) != ma.MA_SUCCESS) {
    return error.MAContextGetDevices;
}
...
for (playback_devices[0 .. playback_devices_count]) |maybe_dev| {
    const dev = maybe_dev.?;
    log.info("dev name: {s}",.{dev.name});  // ERROR: access 2nd dev 
    const device_info = try arena_allocator.create(DeviceInfo);
    try result.playbacks.append(device_info);
    try device_info.copyFrom(arena_allocator, dev);
}

I am not 100% sure on the types, because I haven’t worked that much with using these kinds of c types from zig, but this is my first guess.

1 Like

thanks, you are right :wink:

but it requires @ptrCast(&playback_devices) to pass compilation

1 Like

i think you can safely replace [*:null]? with ?[*]—if the function is filling the length pointer, it seems like the many pointer does not need to be null-terminated; besides, to express that in C would require three stars.

2 Likes

Yes, it seems better, I learn a lot from it, thanks :wink: