gpt4 book ai didi

c - 为什么我在Rust中的C strlen()也计算print中的字符串切片! `s`变量后的宏?

转载 作者:行者123 更新时间:2023-12-03 11:42:22 24 4
gpt4 key购买 nike

所以我只是在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);
}
}
会产生这个:
This uses C's standard lib printf

Length of Useless thing again: 31
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);
}
}
它将产生以下结果:
This uses C's standard lib printf

blah blah blah
19
它正确地计数了“无用的东西”,并且不会计数 s变量之外的任何东西。我知道它可能与内存有某种联系,但实际上我是一个很新的低级人。我可以详细解释一下吗?

最佳答案

归结为C字符串,胖指针以​​及字符串文字在可执行文件中的存储方式之间的区别。
C字符串
您可能已经知道,C将字符串表示为char *。由于无法知道何时停止从内存中读取字符串,因此将空终止符(值为0的字节)添加到末尾。
因此,strlen所做的只是计数字节数,直到找到一个值为0的字节为止。printf进行了类似的操作,只是将找到的结果输出到stdout。

// This string occupies 5 bytes of memory due to the implicit null terminator
char *string_literal = "test";
// ['t', 'e', 's', 't', 0]
胖指针
但是,C String方法可能会出现问题。如果要使用子字符串,则需要修改原始字符串以添加新的空终止符,或将所需的节复制到内存的新部分。解决方案是使用指针存储字符串的长度
// This isn't technically correct, but it is easier to think of this way
pub struct string {
ptr: *const i8,
length: usize,
}
您可以看到在C++的 std::string和Rust的slice中使用的胖指针。由于Rust决定使用胖指针作为默认指针,因此编译器将选择不包括空终止符,以节省空间。
// I have not found a source to confirm this yet, but I think converting a raw string
// literal directly to a pointer may prompt the compiler to add a null terminator for safety
"Test String".as_ptr()
内存
在Linux可执行文件(ELF格式)中,编译器会自行决定将代码中使用的所有字符串文字和常量添加到二进制文件的文本部分。
不知道太多,我将猜测第一个代码示例的文本部分是什么样的:
This uses C's standard lib printf\0\nUseless thing againLength of : \0
通过按照代码中给定的顺序将所有字符串文字放在一起,并删除将在编译时删除的部分(例如rust的print语句中的 {}),可以得到这种近似值。通过这种幼稚的估计,我们实际上在与第一个代码样本的输出匹配的空终止符之前实际看到了31个字符。您可以使用 objdump -sj .text executable_file自己验证这一点(假设我正确使用了该命令)。
异常(exception)情况
我想指出的一件事是字符的长度不是固定的。例如,一个Unicode字符可以是4个字节长。因此,如果计划将字符串传递给c,建议您改用二进制字符串来更明确地说明数据类型,如果不确定是否要传送该字符串,则直接添加null终止符。
// The b converts the string to a [u8; N] and \0 is the null terminator.
let example = b"test 123\0";

关于c - 为什么我在Rust中的C strlen()也计算print中的字符串切片! `s`变量后的宏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66160622/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com