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

2024-04-19

所以我只是在 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 的字节)。

So what strlen它只是计算字节数,直到找到值为 0 的字节。printf做类似的事情,只不过它将找到的内容输出到标准输出。

// 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 的切片。由于 Rust 决定使用胖指针作为默认值,因此编译器将选择在可能的情况下不包含空终止符以节省空间。

Memory

在 Linux 可执行文件(ELF 格式)中,代码中使用的所有字符串文字和常量均由编译器自行决定添加到二进制文件的文本部分。

在不知道太多的情况下,我将猜测第一个代码示例的文本部分是什么样的:

This uses C's standard lib printf\0\nUseless thing againLength of : \0

我通过将所有字符串文字按照代码中给出的顺序放在一起并删除将在编译时删除的部分(例如{}在 Rust 的打印语句中。通过这种天真的估计,我们实际上看到空终止符之前正好有 31 个字符与第一个代码示例的输出相匹配。您可以使用自己验证这一点objdump -sj .text executable_file(假设我正确地执行了该命令)。

例外情况

我想指出的一件事是 a 的长度特点不是固定的。例如,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";
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Rust 中的 C strlen() 也会计算 print 中的字符串切片! `s` 变量之后的宏? 的相关文章

  • LINQ TO ENTITY 无法与枚举类型进行比较

    下面是枚举叶子 public enum Leaves Annual 0 Medical 1 Hospitalization 2 Unpaid 3 下面是linq查询 public ActionResult ApproveLeave int
  • return 语句是否为按值返回的函数创建临时对象?

    当我学习 C 11 右值引用和移动语义时 我开始对函数如何返回值来初始化变量感到困惑 看下面的例子 Widget makeWidget Widget w return w Widget w1 makeWidget 这里我假设没有 RVO 即
  • 是否有一种快速替代方法可以从 XNA 中的位图对象创建 Texture2D?

    我环顾四周 发现从位图创建Texture2D的唯一方法是 using MemoryStream s new MemoryStream bmp Save s System Drawing Imaging ImageFormat Png s S
  • 如何让我的方法等待所有线程完成?

    我有一个方法可以触发线程来完成一些工作 将有 2 个线程异步运行一段时间 当调用它们的回调方法时 回调会触发另一个线程 直到所有工作完成 如何让我的方法等待所有这些线程完成并被触发 如果这是 Net 4 0 您可以使用CountdownEv
  • 从对象中获取类型正在返回运行时类型[重复]

    这个问题在这里已经有答案了 我有一个简单的功能 public string getType object obj Type type obj getType return type FullName 如果您在运行时创建的字符串对象上使用此函
  • 如何更改 GridView 内 ListViewItemPresenter 中的 SelectedBackground

    我在 SubSection 中有一个 Clickable Gridview
  • JObject ToString 与 StringEnumConverter 不起作用

    我正在尝试序列化一个匿名类 如下所示 public enum ErrorCode A B C var response JObject FromObject new Error new Message Test Code ErrorCode
  • 这个对象的内存会是什么样子?

    我想知道这个类 它的对象 的内存布局是什么样的 class MyClass string myString int myInt public MyClass string str int i myString str myInt i MyC
  • 如何对具有无效值的属性使用 JSON.net 的默认值

    我正在使用 Newtonsoft JSON 库来反序列化来自 Web 服务的响应 问题是某些字段包含无效值 例如 一条记录上的一个字段包含一个 T 表示该字段应该是数字 我想做的是将无效字段的值设置为 null 或其他默认值 我的所有属性都
  • 二叉树实现C++

    二叉树插入 include stdafx h include
  • 更改子进程中的 iostream

    现在 我正在开发一个项目 其中我需要启动一个子进程来使用 C 在 Linux 中执行一个新程序 并且我需要重定向标准输入和输出 就像在 C 中一样 它们是cin and cout 到一个文件 这意味着在子进程中 标准输入和输出都是文件 子进
  • 如何使用 GCC 在 C 上编译库?

    我用这些文件创建了一个库pila h and pila c 我编译文件pila c with gcc pila c c这个库运行良好 我已经测试过了 然后我又做了一个图书馆 这个库有文件pila funciones extra h and
  • 使用本地系统帐户运行时,GetAccessControl 方法失败,出现意外错误代码 3

    我已经创建了 Windows 服务并使用本地系统帐户运行它 该服务正在读取用户文件并查找其所有者 在获取文件的访问权限以查找所有者时 它抛出以下异常 方法失败 出现意外错误代码 3 StackTrace 在 System Security
  • C# 中的自定义按钮:如何删除悬停背景?

    我正在尝试使用 Visual Studio 2005 对我的表单 其 FormBorderStyle none 执行自定义按钮 我在链接到该按钮的 ImageList 中有我的 3 种状态按钮图像 this btnClose AutoSiz
  • 如何避免函数的多重定义(Linux、GCC/G++、Code::Blocks)

    我有一个代码块项目 它使用许多不同的文件 通常是由其他程序员编写的 目前我遇到的情况是 我有两个不同的子项目 其中包含以相同方式命名的函数 比方说 F int x 因此 F int x 是在两个不同位置的两个源文件中定义的 并且它们有两个不
  • 如何将 typedef 结构传递给函数?

    此刻我正在努力 void avg everything 但这给了我错误 error subscripted value is neither array nor pointer 当我今天早些时候收到此错误时 这是 因为我没有正确地将 2D
  • OpenMP 动态调度与引导调度

    我正在研究 OpenMP 的调度 特别是不同的类型 我了解每种类型的一般行为 但澄清一下何时进行选择会很有帮助dynamic and guided调度 英特尔的文档 https software intel com en us articl
  • 对 MFC UI 应用程序进行单元测试吗?

    如何对大型 MFC UI 应用程序进行单元测试 我们有一些大型 MFC 应用程序已经开发了很多年 我们使用一些标准的自动化 QA 工具来运行基本脚本来检查基础知识 文件打开等 这些由 QA 小组在日常构建后运行 但我们希望引入一些程序 以便
  • C# - 平移光标

    我正在 PictureBox 控件中实现大图像的平移 并且设置适当的方向平移光标没有问题 但是 我似乎找不到用于平底锅原点的图像 内部带有箭头的圆圈 我在哪里可以找到它 我觉得image您正在寻找的内容未包含在框架中 每个应用程序都使用自己
  • 有C语言的解释器吗? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话

随机推荐

  • 以编程方式关闭 WP7 应用程序? [复制]

    这个问题在这里已经有答案了 可能的重复 Windows Phone 7 关闭应用程序 https stackoverflow com questions 3659195 windows phone 7 close application 如
  • 表达忽略视图目录

    我已经设置了一个配置文件来存储我的 Express 应用程序的应用程序路径 cookie 秘密等设置 问题是它似乎忽略了我的视图路径目录设置 配置 js exports server port 3000 cookie secret path
  • 使用类的 __new__ 方法作为工厂:__init__ 被调用两次

    我在 python 中遇到了一个奇怪的错误 其中使用 new 将类的方法作为工厂会导致 init 实例化类的方法被调用两次 这个想法最初是为了使用 new 母类的方法根据传递的参数返回其子类之一的特定实例 而无需在类外部声明工厂函数 我知道
  • 需要类或命名空间;语法正确且正确

    在 dualstk h 中 ifndef 32 dualstk h define 32 dualstk h include
  • AWS 检查状态机/Step Functions 并发运行

    我在处理状态机 步骤函数 的并发运行时遇到很多问题 该状态机中确实有胶水作业任务 状态机由 Lambda 启动 并由 FIFO SQS 队列触发 lambda 获取消息 检查正在运行的状态机实例数量 如果该数量低于 GlueJob 并发运行
  • .htaccess 将一个文件夹中的所有文件重定向到另一个文件夹中的完全相同的文件

    我们只需将名为 音乐 的目录中的每个页面移动到名为 信息 的目录 这是唯一的更改 有很多指向音乐页面的链接 我们不想全部破坏 我不擅长重写条件 基本上 我想做的就是当用户输入 music index php 或 music life myp
  • 复合属性

    有没有办法在 C 中制作复合属性以在编译时提供等效的元数据 例如 改变 ClassInterface ClassInterfaceType AutoDual ProgId MyProgId MyMefExport MyProgId publ
  • 有没有办法使用 getUserMedia 减少延迟?

    在尝试减少视频延迟的同时WebRTC通信时 我测量了视频捕获和视频显示之间的延迟 为了防止测量 WebRTC 涉及的延迟 我只是使用getUserMedia和一个 HTMLvideo显示流 我通过每帧显示时间戳来做到这一点 使用reques
  • 编译器错误:对调用的引用不明确

    Case 1 static void call Integer i System out println hi i static void call int i System out println hello i public stati
  • 更改 ToolStripMenuItem 的大小

    我正在自定义绘制一个菜单项MenuStrip 我遇到的问题是菜单项坚持根据文本调整自身大小 这不是我想要的 没有文本 我可以设置AutoSize设置为 false 并显式指定大小 但包含的菜单 ToolStripDropDown 仍然根据文
  • $_POST、$_GET 和 $_REQUEST 为空

    SOLVED 我省略了输入字段中的名称属性 我有一个简单的 html php 表单 我提交了 POST 始终为空 如果我尝试 get 那么 GET 总是空的 REQUEST 和 php input 相同 我没有得到任何回报 有很多关于这个主
  • AVCaptureVideoPreviewLayer 和从相机位置预览

    我正在开发一个允许用户拍照的应用程序 我已经开始使用AVCam https developer apple com library ios samplecode AVCam Introduction Intro html苹果提供了 但我实际
  • ElasticSearch - 索引模板和索引模式有什么区别

    我在这里阅读了对我的问题的解释 https discuss elastic co t whats the differece Between index pattern and index template 54948 https disc
  • 如何通过 django admin 中的操作请求用户输入?

    在我的代码中 我正在编写一个分组操作 我想询问用户每个组需要多少人 然后用一个警报框进行响应 根据用户输入显示 您有 4 个组 的内容 我如何在 django admin 中执行此操作 如何创建某种弹出窗口来询问他们想要放入一个组中的人数
  • 默认移动构造函数/赋值和删除的复制构造函数/赋值

    根据标准 如果类 X 的定义没有显式声明移动构造函数 则当且仅当 X 没有用户声明的复制构造函数 X 没有用户声明的复制赋值运算符 X 没有用户声明的移动赋值运算符 并且 X 没有用户声明的析构函数 现在以下无法编译 include
  • Highstocks 图表宽度未正确呈现

    您好 我在使用 jquery 选项卡时遇到 highstocks 问题 这是构造函数的代码 Chart new Highcharts StockChart Chart new Highcharts StockChart chart rend
  • 在调试或正常运行时读取字符串时出现 NullReferenceException [关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 当尝试在调用时读取文件时DoSomething from Something 在里面TestProgram Load 方
  • WebSocket 库 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想在 Linux 上使用 C 访问 WebSocket API 我见过不同的图书馆 比如libweb
  • 为什么Redis中不建议使用KEYS?

    在Redis中 建议不要使用按键命令 https redis io commands KEYS 为什么会这样呢 是因为它的时间复杂度是 O N 吗 或者是别的什么原因 我做了下面的实验来证明KEYS命令有多么危险 当带有 KEYS 的一个命
  • 为什么 Rust 中的 C strlen() 也会计算 print 中的字符串切片! `s` 变量之后的宏?

    所以我只是在 Rust 中修改 C 库 我发现以下代码 extern crate libc use libc c char c int size t extern C fn printf fmt const c char gt c int