Handling of Ctypes char ptr

I’m trying to write C library bindings, and could use a few pointers and guidance as LLMs I’ve tried hallucinate a lot on this topic.

What I’m struggling with at the moment is reading a single character at a time from a char ptr type. Currently I’m using the string_from_ptr function, but I could use a “char_from_ptr” equivalent.

let single_char_string = string_from_ptr ~length:1 (my_char_pointer +@ 0)

At the same time, I’d like to know what is the correct representation, in program source code, of the null byte when comparing? LLMs hallucinate it as '\000', which is a syntax error due to the backslash, but it seems to work when I compare with "\000". But that doesn’t look right at all.

Attached is a slice of my code for reference or if anyone can provide suggestions specifically when interfacing with C.


let listxattr: string -> char Ctypes_static.ptr -> int -> int =
  foreign "listxattr" ~check_errno:true (string @-> ptr char @-> int @-> returning int)

let list_attribute_names path =
  (* between calls of listxattr size of buffer may differ  *)
  (* repeat size query and retrieval until lengths match *)
  (* in theory, in code will still need to move the check `names_ln` within the loop *)
  let rec read_reallocate_until_aligned expected_length =
    let buffer = allocate_n char ~count:expected_length in
    let copied_count = listxattr path buffer expected_length in
    if copied_count <> expected_length then read_reallocate_until_aligned copied_count
    else (expected_length, buffer) in
  let names_ln = listxattr path Ctypes.(coerce (ptr void) (ptr char) null) 0 in
  let (len, names) = read_reallocate_until_aligned names_ln in
  for i = 0 to len do
    let c = string_from_ptr ~length:1 (names +@ i) in         <-----------------------
    if c = "\000" then print_endline "" else
      Printf.printf "%s" c;
    ()
  done;
  ()

let () = list_attribute_names "/proc/self/exe"

You can use pointer arithmetic to read one char at a time using ! and @

  let strlen ptr =
    let rec loop i = if is_null !@(ptr +@ i) then i else loop (i + 1) in
    loop 0

Also you can use is_null form Ctypes.

1 Like

Thank you for the help.

I’ve changed the function’s inner loop to the following

  for i = 0 to len do
    let c = !@(names +@ i) in
    if c = '\000' then print_endline "" else begin
      Printf.printf "%s" (String.make 1 c);
    end;
  done;
  • I couldn’t use the is_null function because it compares against a null pointer, and doesn’t match the NUL character byte (although they have the same representation).
  • Seems that the '\000' syntax is correct for NUL character byte representation in OCaml. Unsure of my previous changes that cause the LSP to report a syntax error for '\000'. The syntax error report I got was in fact “Illegal backslash escape in string or character (\0) (ocamllsp)” when '\00' was typed in instead. Probably the error message just got stuck in my mind.

FYI, char values span 1 byte while NULL pointers span probably 8 bytes on your machine.
They are both (probably) 0, but they’re not the same representation because they are not the same size.

I say probably because the C standard does not enforce that the NULL pointer is 0 (even though it is on most machines). The standard only enforces that the NULL pointer “is obtained by casting 0 to a pointer type”. The overflow thread you linked mentioned that a pointer can be compared to “0”, in which case it is checked whether it is the NULL pointer or not. This is because there is an implicit cast of the value 0 to the pointer type. However, there exist architectures where the address 0 is reserved and this implicit cast produces some specific value that is the NULL pointer, which is not 0.

1 Like