Moc*_*ida 1 c libc strlen rust
所以我只是在 Rust 中修改 C 库,我发现以下代码:
extern crate libc;
use libc::{c_char, c_int, size_t};
extern "C" {
fn printf(fmt: *const c_char, ...) -> c_int;
fn strlen(arr: *const c_char) -> size_t;
}
fn main() {
unsafe {
printf("This uses C's standard lib printf".as_ptr() as *const i8);
print!("\n");
let s = "Useless thing again";
print!("Length of {}: ", s);
let x = strlen(s.as_ptr() as *const i8);
print!("{}", &x);
}
}
Run Code Online (Sandbox Code Playgroud)
将产生这个:
This uses C's standard lib printf
Length of Useless thing again: 31
Run Code Online (Sandbox Code Playgroud)
strlen()还计算了宏内的字符串切片print!。但如果我这样做:
extern crate libc;
use libc::{c_char, c_int, size_t};
extern "C" {
fn printf(fmt: *const c_char, ...) -> c_int;
fn strlen(arr: *const c_char) -> size_t;
}
fn main() {
unsafe {
printf("This uses C's standard lib printf".as_ptr() as *const i8);
print!("\n");
print!("blah blah blah\n");
let s = "Useless thing again";
let x = strlen(s.as_ptr() as *const i8);
print!("{}", &x);
}
}
Run Code Online (Sandbox Code Playgroud)
它将产生这样的结果:
This uses C's standard lib printf
blah blah blah
19
Run Code Online (Sandbox Code Playgroud)
它正确地计算了“无用的东西”,并且不会计算s变量之上的任何内容。我知道它可能与记忆有某种联系,但实际上我对低水平还很陌生。我可以有一些详细的解释吗?
这可以归结为 C 字符串、胖指针以及字符串文字在可执行文件中存储方式之间的差异。
\n您可能已经知道,C 将字符串表示为 a char *。由于无法知道何时停止从内存中读取字符串,因此在末尾添加了一个空终止符(值为 0 的字节)。
所以strlen它只是计算字节数,直到找到值为 0 的字节。printf执行类似的操作,只是将找到的内容输出到 stdout。
// This string occupies 5 bytes of memory due to the implicit null terminator\nchar *string_literal = "test";\n// [\'t\', \'e\', \'s\', \'t\', 0]\nRun Code Online (Sandbox Code Playgroud)\n然而,C String 方法可能存在问题。如果要获取子字符串,则需要修改原始字符串以添加新的空终止符或将所需部分复制到内存的新部分。解决这个问题的方法是用指针存储字符串的长度
\n// This isn\'t technically correct, but it is easier to think of this way\npub struct string {\n ptr: *const i8,\n length: usize,\n}\nRun Code Online (Sandbox Code Playgroud)\n您可以看到 C++std::string和 Rust 切片中使用的胖指针。由于 Rust 决定使用胖指针作为默认值,因此编译器将选择在可能的情况下不包含空终止符以节省空间。
在 Linux 可执行文件(ELF 格式)中,代码中使用的所有字符串文字和常量均由编译器自行决定添加到二进制文件的文本部分。
\n在不知道太多的情况下,我将猜测第一个代码示例的文本部分是什么样的:
\nThis uses C\'s standard lib printf\\0\\nUseless thing againLength of : \\0\nRun Code Online (Sandbox Code Playgroud)\n我通过按照代码中给出的顺序将所有字符串文字放在一起并删除将在编译时删除的部分(例如 in {}rust\ 的 print 语句)来获得此近似值。通过这个 na\xc3\xafve 估计,我们实际上看到了与第一个代码示例的输出匹配的空终止符之前的 31 个字符。您可以使用自己验证这一点objdump -sj .text executable_file(假设我正确地执行了该命令)。
我想指出的一件事是,字符的长度不是固定的。例如,Unicode 字符的长度可以是 4 个字节。因此,如果您打算将字符串传递给 c,建议您使用二进制字符串来更明确地了解数据类型,并且如果您不确定是否会传递它,请直接添加 null 终止符。
\n// The b converts the string to a [u8; N] and \\0 is the null terminator.\nlet example = b"test 123\\0";\nRun Code Online (Sandbox Code Playgroud)\n