“C 便是一种编程语言”
2025-10-31 12:18
我们将能够某种应用程序,使我们的语言学必需附加外部给定。外部给定应用程序,是的,FFI......
然后你挖掘出,什么,Rust,你也有 C 的 FFI?Swift 你也有吗?甚至连 Python 也有?!
为了与主要的图形其他用户界面提问,每种语言学都必需学则会却说 C 语言学。然后,当它们能够彼此间提问时,也就都却讲到了 C 语言学。
直到现在,C 语言学成了程序设计国际标准语言学。它不再极少极少是一种C#学,还成了一种条款。
与 C 交互涉及哪些不足之处?很明显,几乎每种语言学都必需学则会却说 C 语言学。那么,“却说 C 语言学”是什么意思?这是却说要以 C 语言学常量的形式阐述应用程序的种类和给定,并以某种形式动手一些事情:
反之亦然这些种类的中轴;用元数据器动手一些事情,将给定的大写字母解出为指针;用尽量的 ABI 来附加这些给定(比如把参数放于正确的线程之前)。然而这之中有两个诱因:你不用就让撰写一个 C 解出器;C 并未一个 ABI,甚至是假设好的种类中轴。你不用就让解出一个 C 常量就让,解出C语言学实质上是不显然的。
“但是,等等!有很多工具箱可以读取 C 语言学的常量,比如rust-bindgen!”
但还是不行:
bindgen 应用于 libClang 来解出 C 和 C++常量。要删减 bindgen 追踪 libclang 的形式,参看 clang-sys PDF。关于 bindgen 如何应用于 libclang 的格外多内容,参看 bindgen 其他用户指南。任何花了大量等待时间无论如何从词法上统计分析 C(++)常量的人,很快就则会却说“啊,去他的”,并转而用一个 C(++)C#器来动手这件事情。请记起,极少极少从词法上统计分析 C 常量是未意义的:你还能够解出 #includes、typedefs 和 macros 的。因此,直到现在你能够协作应用软件所有的常量解出逻辑,并以某种形式找到与你所注目的生态的系统相对应的 DEFINED 内容。
就拿 Swift 这个极端的范例来却说吧。在 C 语言学互连和资源不足之处,它实质上以外一切军事优势。
该语言学是由微软公司开发的,它有效地取代了 Objective-C,被选为在苹果应用软件上假设和应用于的系统 API 的主语言学。我显然,在这个步骤之前,它在ABI适应性和取而代之设计不足之处比其他任何语言学都格外进一步。
它也是我认出的对 FFI 默许极好的语言学之一。它可以本地导入(Objective-)C(++)常量,并生成一个漂亮的原生 Swift 应用程序,相关种类则会相应“桥接”到 Swift 之前对等的种类(通常是表面的,因为这些种类的 ABI 相同)。
Swift 的开发者同时也是微软公司 Clang 和 LLVM 工程项目的协作者和维护人。他们都是 C 语言学及其衍生物不足之处的全球性级专家学者。Doug Gregor 就是其之前之一,以下是他对 C FFI 的说法:
看吧,即便是 Swift 也不乐意动手这种事情。(另外可以参见Jordan Rose和John McCall在llvm上的PPT去了解“Swift为何采用这种形式”)。
那么,如果你无论如何也不只想应用于 C C#器在C#时统计分析并解出常量,那么你要怎么动手?你就要“手工艺译成”了!int64_t?还是却说写i64. long?......
C 本来并未 ABI好吧,这没什么可大惊小怪的:出于“软体的系统性”考虑,C 语言学之前的乘法种类被取而代之设计成形状不相同的。我们可以把赌注留在有点怪异的CHAR_BIT上,但我们还是不用真的long的形状和相反形式。
”但是等等!每个应用软件都有标准既有的附加四人和 ABI!“
的确是有,而且它们通常假设了 C 语言学之前关键书名的中轴!(而且,其之前一些不极少极少假设了 C 种类的附加四人,参见 AMD64 SysV。)
但这之中有一个棘手的诱因:其指令集之前并未假设 ABI。图形其他用户界面也未。我们必需针对特定的远距离举例来说(target triple)动手文书工作,比如“x86_64-pc-windows-gnu”(绝不与“x86_64-pc-windows-msvc”弄混了)。
好吧,则会有多少个这样的远距离举例来说呢?
> rustc --print target-listaarch64-apple-darwinaarch64-apple-iosaarch64-apple-ios-macabiaarch64-apple-ios-simaarch64-apple-tvos...粘贴预定义
还有:
...armv7-unknown-linux-musleabiarmv7-unknown-linux-musleabihfarmv7-unknown-linux-uclibceabihf...粘贴预定义
还有:
...x86_64-uwp-windows-gnux86_64-uwp-windows-msvcx86_64-wrs-vxworks>_粘贴预定义
这样的远距离举例来说合计有 176 个。我原本打算都列出来,以强既有视觉冲击,但显然是有点多了。ABI 显然是有点多了。而且,我们还未涉及到所有只不过相同的附加四人,比如 stdcall vs fastcall 或者 aapcs vs aapcs-vfp!
最少,所有这些 ABI 和附加四人之类的外面肯定要以机器可读的PDF发放给大家应用于:新技术性的 PDF 邮件。
好吧,最少对于特定的远距离其所小组,主要的 C 语言学C#器在 ABI 上陷入僵局了相反!当然,也有一些诡异的 C 语言学C#器,如 clang 和 gcc-。
> abi-checker --tests ui128 --pairs clang_calls_gcc gcc_calls_clang...Test ui128::c::clang_calls_gcc::i128_val_in_0_perturbed_small passedTest ui128::c::clang_calls_gcc::i128_val_in_1_perturbed_small passedTest ui128::c::clang_calls_gcc::i128_val_in_2_perturbed_small passedTest ui128::c::clang_calls_gcc::i128_val_in_3_perturbed_small passedTest ui128::c::clang_calls_gcc::i128_val_in_0_perturbed_big failed!test 57 arg3 field 0 mismatchcaller: [30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 3A, 3B, 3C, 3D, 3E, 3F]callee: [38, 39, 3A, 3B, 3C, 3D, 3E, 3F, 40, 41, 42, 43, 44, 45, 46, 47]Test ui128::c::clang_calls_gcc::i128_val_in_1_perturbed_big failed!test 58 arg3 field 0 mismatchcaller: [30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 3A, 3B, 3C, 3D, 3E, 3F]callee: [38, 39, 3A, 3B, 3C, 3D, 3E, 3F, 40, 41, 42, 43, 44, 45, 46, 47]...392 passed, 60 failed, 0 completely failed, 8 skipped粘贴预定义
这是我在 x64 Ubuntu 20.04 上行驶FFI abi-checker的结果。这是一个相当举足轻重的、表现良好的应用软件。这之中测试的是一些相当令人震惊厌烦的可能,即一些阶乘参数在两个由 clang 和 gcc C#的线性托错综复杂按值传递……而且最终了!甚至是 x64 linux 上的词组int128 ABI,clang 和 gcc 也未能陷入僵局相反。该种类是一个 gcc 扩大,但 AMD64 SysV ABI 在一个极好的 PDF 邮件之中动手了只不过相反假设和却说明。
我写这个外面是为了核查rustc之前的扯误,我并未指望挖掘出,这两个主要的 C C#器在不可或缺同时人们也最熟识的 ABI 上存在不相反!
ABI 就是真相。
试着把 C 家养因此,对 C 语言学常量动手语义解出是一个可怕的恶梦,必需由那个应用软件的 C C#器来启动,即使你让 C C#器告诉你种类以及如何忽略注释,但本来,你无论如何不用真的所有外面的形状/相反形式/附加四人。
如何与那泥巴外面透过互连呢?
你的第一个表单是只不过撤军,将你的语言学与 C 语言学透过灵魂附加,可以采用以下任何一种形式:
用 C(++)撰写C#器/行驶时,所以它无论如何都能却说 C 语言学。让你的“codegen”直接生成 C(++),这样其他用户就能够一个 C C#器。基于一个商业既有的本土既有 C C#器(gcc 或 clang)协作自己的C#器。但也局限此,因为除非你的语言学就让暴露了unsigned long long,否则你就则会后继者 C 的软体的系统性慌乱。于是,我们来到了第二个表单:撒谎、欺骗和偷窃。
如果这一切是紧接著躲不开的灾难,那么还不如开始在自己的语言学之前手工艺译成种类和应用程序假设。这实质上就是我们在 Rust 之前每天都在动手的事情。是的,人们应用于 rust-bindgen 之类的工具箱来相应既有这个步骤,但很多时候,还是能够核查或手工艺调整那些假设,生命之后,显然不用让经过某人诡异订制的 C 协作的系统软体的系统。
嘿,Rust,在x64 linux上intmax_t是什么?
pub type intmax_t = i64;粘贴预定义
酷!故事情结束了!嘿,Nim,在x64 linux上long long是什么?
clonglong {.importc: "long long", nodecl.} = int64粘贴预定义
酷!故事情结束了!很多预定义早已从各个环节之前剔除了 C,并且早已开始对本体种类的假设透过硬字节。毕竟,它们无论如何只是应用软件 ABI 的一部分!它们要动手什么?彻底改不定intmax_t的形状吗!?这无论如何是一个严重破坏 ABI 的删减。
哦,对了,phantomderp即将研究的那个外面又是什么?
我们谈下为什么不用删减intmax_t,因为如果我们从long long(64 位乘法)改回词组int128_t(128 位乘法),某些小数邮件就则会无所适从,应用于扯误的附加四人/留在四人。但是,有未一种作法——如果预定义选用了——我们可以在在此之后操作的系统在之前换用给定附加,而让老的操作的系统在保持原处?让我们撰写一些预定义,测试一下表面别名可以为 ABI 造成什么协助。是的,他们的篇名就让写得最好,解决了一些相当举足轻重的单单诱因,但是......C#学如何解决问题这种不定既有?如何自行决定与哪个Ubuntu的intmax_t互连?如果有一些 C 语言学常量涉及到了intmax_t,它应用于哪个假设?
我们在讨论 ABI 只不过相同的应用软件时应用于的主要机制是远距离举例来说。你真的什么是远距离举例来说吗? x86_64-unknown-linux-gnu。你真的都包括什么吗?实质上涵盖了过去 20 年之中所有主要的桌面上/IP Linux Ubuntu。表面上,你可以针对某个远距离透过C#,并得到一个在所有这些应用软件上都能“正常文书工作”的小数邮件。但是,可能显然大不相同,比如有些程序在在C#时则会默认intmax_t比int64_t大。
任何意图动手出这种彻底改不定的应用软件是不是都则会被选为一个在此之后远距离举例来说? x86_64-unknown-linux-gnu2?如果任何针对x86_64-unknown-linux-gnuC#的外面都可以在上面行驶,这还缺少吗?
删减单张而又不严重破坏 ABI”那又怎样,难道 C 语言学就永远不则会再改进型了吗?“
却说不是也是,因为它最糟的取而代之设计。
老实却说,透过 ABI 兼容的删减可谓是一种艺术作品形式。这项文书工作的一部分是准备好。如果你准备好好了,动手不严重破坏 ABI 的删减就则会直观很多。
正如 phantomderp 的篇名所指出的那样,像 glibc(g是x86_64-unknown-linux-gnu之前的gnu)早已似乎了这一点,并应用于大写字母Ubuntu既有这样的机制来格外取而代之单张和 API,同时为任何针对从新Ubuntu的C#保有从新Ubuntu。
因此,如果有个作法int32_t my_rad_symbol(int32_t),你告诉C#器将其给定为my_rad_symbol_v1,那么任何针对你所发放的常量透过C#的人,都则会在预定义之前写上my_rad_symbol,但则会元数据到my_rad_symbol_v1。
然后,当你未确定单单一定会应用于int64_t时,可以把int64_t my_rad_symbol(int64_t)当作my_rad_symbol_v2,但无论如何保有从新的假设my_rad_symbol_v1。 任何人在针对你的常量透过C#时,如果是针对取而代之Ubuntu就应用于大写字母v2,而针对从新Ubuntu则继续应用于v1!
但无论如何有一个安全性诱因:任何针对在此之后常量所动手的C#都不用与从新Ubuntu的托透过元数据!托的 v1 Ubuntu实际上未 v2 大写字母。所以,如果你只想要热门的取而代之适应性,就能够接受与从新有的系统不兼容的全然。
不过,这并不是什么大诱因,只是则会让应用软件供应商感觉到难过,因为未人必需立即应用于他们花了这么多等待时间动手出来的外面。你推出了一个闪亮的取而代之适应性,却要放于赶走等数年的等待时间,等到大家显然它不定得足够普及/商业既有,乐意缺少它并创下对从新应用软件的默许(或者乐意为它协作动态核查和回退)。
如果你只想让人们立即换用,那么就是向左兼容的诱因了。这就能够让从新Ubuntu必需适应它们只不过未概念的取而代之适应性。
删减种类而不严重破坏 ABI好了,除了删减给定的单张,我们还可以删减什么?我们可以删减种类中轴吗?
可以!但也不可以!这取决于你暴露种类的形式。
C 语言学真正有趣的其之前一个适应性是,它让你可以区分中轴已知的种类和中轴未知的种类。如果你只在 C 语言学的常量之前前向回应一个种类,那么任何与该种类交互的其他用户预定义都不用真的该种类的中轴,而必需多年来通过指针不表面地对它动手解决问题。
所以你可以开发一个像MyRadType* make_val()和use_val(MyRadType*)这样的 API,然后利用某种程度的大写字母Ubuntu既有技能来暴露make_val_v1和use_val_v1,任何时候你只想删减这个中轴,都要在与该种类交互的所有外面上删减Ubuntu。某种程度地,你得保有MyRadTypeV1、MyRadTypeV2和一些种类假设,以确保人们应用于“正确”的种类。
最好,我们可以彻底改不定只不过相同Ubuntu错综复杂的种类中轴!对吗?嗯,大多数时候是这样。
如果有多个外面基于你的托协作,它们在种类不表面的只不过彼此间附加,就则会显现最糟的可能:
lib1:开发一个 API,应用于种类MyRadType*附加use_val;lib2:附加make_val ,并将结果传给 lib1。如果 lib1 和 lib2 是基于托的只不过相同Ubuntu透过C#的,那么make_val_v1就则会被传递给use_val_v2!这时,你有两个考虑来解决问题这个诱因:禁止这样动手,警告那些这样动手的人,令人震惊伤心。以一种向左兼容的形式取而代之设计MyRadType,这样混同就没诱因了。协作向左兼容常用的技能有:保有未应用于的配置文件供今后Ubuntu应用于。MyRadType 的所有Ubuntu都有一个联合的前缀,让你可以“核查”所应用于的Ubuntu。有形状自适应的配置文件,这样从新Ubuntu可以“跳过”取而代之增部分。情形统计分析:MINIDUMP_HANDLE_DATA微软确实是向左兼容的大师,他们甚至让他们真正负责任的外面在只不过相同的指令集错综复杂保持中轴兼容。我最近遇到的一个范例是Minidumpapiset.h之前的MINIDUMP_HANDLE_DATA_STREAM。
这个 API 阐述了一个Ubuntu既有的值列表。该列表以这种种类开始:
typedef struct _MINIDUMP_HANDLE_DATA_STREAM { ULONG32 SizeOfHeader; ULONG32 SizeOfDescriptor; ULONG32 NumberOfDescriptors; ULONG32 Reserved;} MINIDUMP_HANDLE_DATA_STREAM, *PMINIDUMP_HANDLE_DATA_STREAM;粘贴预定义
其之前:
SizeOfHeader是 MINIDUMP_HANDLE_DATA_STREAM 本身的形状。如果能够在结尾加进格外多的配置文件,那也没关系,因为从新Ubuntu可以应用于这个值来样品头的“Ubuntu”,并跳过任何它们不识别的配置文件。SizeOfDescriptor是数小组之前每个类型的形状。这也是为了让你真的类型是什么“Ubuntu”,你可以跳过不真的的配置文件。NumberOfDescriptors是数小组长度。Reserved是一个保有配置文件(Minidumpapiset.h相当严谨,从不应用于任何可用字节,因为可用字节的值就其,而且是一种序列既有的小数文档。我只想要他们加进这个配置文件是为了使本体的形状是 8 的倍数,这样就不则会有数小组类型是否能够在头之后可用的诱因了。哇,这才是认真对待安全性!)全然上,微软应用于这种Ubuntu既有设计方案是有诱因的,他们假设了两个Ubuntu的数小组类型:typedef struct _MINIDUMP_HANDLE_DESCRIPTOR { ULONG64 Handle; RVA TypeNameRva; RVA ObjectNameRva; ULONG32 Attributes; ULONG32 GrantedAccess; ULONG32 HandleCount; ULONG32 PointerCount;} MINIDUMP_HANDLE_DESCRIPTOR, *PMINIDUMP_HANDLE_DESCRIPTOR;typedef struct _MINIDUMP_HANDLE_DESCRIPTOR_2 { ULONG64 Handle; RVA TypeNameRva; RVA ObjectNameRva; ULONG32 Attributes; ULONG32 GrantedAccess; ULONG32 HandleCount; ULONG32 PointerCount; RVA ObjectInfoRva; ULONG32 Reserved0;} MINIDUMP_HANDLE_DESCRIPTOR_2, *PMINIDUMP_HANDLE_DESCRIPTOR_2;// 最取而代之MINIDUMP_HANDLE_DESCRIPTOR假设。typedef MINIDUMP_HANDLE_DESCRIPTOR_2 MINIDUMP_HANDLE_DESCRIPTOR_N;typedef MINIDUMP_HANDLE_DESCRIPTOR_N *PMINIDUMP_HANDLE_DESCRIPTOR_N;粘贴预定义
关于这些本体的单单内容,有几个比较有意思的地方:
对它的删减只是在结尾加进配置文件;“最后一个”有种类假设;保有一些 Maybe Padding(RVA 是 ULONG32 种类)。在向左安全性不足之处,微软绝对是一头守卫者的猿猴。他们对可用如此谨慎,甚至在 32 位和 64 位错综复杂采用了相同的中轴!(本来,这相当举足轻重,因为你只想要一个指令集的小型转储邮件解决问题器必需解决问题每个指令集的小型转储邮件。)好吧,最少它就让很健壮,如果你按照它的准则来,通过引述透过操作,并应用于 size 配置文件。
但最少可以玩再继续。只是在某些时候,你不得不却说“你的词不对”。微软显然不则会这么却说,他们只则会动手一些可怕的事情。
情形统计分析:jmp_buf我对这种可能不是很熟识,但在研究 glibc 历史上的侵略性删减时,我在 lwn 上看到了这篇好笑的篇名:glibc s390 ABI的侵略性删减。我显然这篇篇名比较准确。
全然推论,glibc 曾一度严重破坏过种类的 ABI,最少在 s390 上是这样。根据这篇篇名的阐述,它造成了慌乱。
特别地,他们彻底改不定了setjmp/longjmp应用于的状态保存种类(即jmp_buf)的中轴。看吧,他们并不是十足的傻瓜。他们真的这是一个严重破坏 ABI 的删减,所以他们负责任地动手了大写字母Ubuntu既有。
但是,jmp_buf并不是一个不表面种类。有些外面程序中地存储了这个种类的比如说,比如 Perl 的行驶时。不用却说,这个正因如此不是很很难忽略的种类早已渗透到许多小数邮件之前去了,最终的结论是,Debian 的所有外面都能够重取而代之C#。
这篇篇名甚至讨论了对 libc 透过Ubuntu换用以解决问题这种可能的显然性:
在像 Debian 这样的混合 ABI 生态的系统之前,SO 起名的彻底改不定(SO name bump)则会所致两个 libc 被加载并竞争相同的大写字母起名空间,而解出(以及 ABI 考虑)由 ELF 插值和作用域准则决定。这真是紧接著恶梦。这显然是一个比告诉所有人重取而代之协作并回归正常轨道格外最糟的解决设计方案。(这篇篇名很极好,遇到困难您读一下。)
就让能删减 intmax_t?在我看来,未必。和jmp_buf一样,它不是一个不表面种类,也就是却说,它被大量的随机本体程序中,被其他大量的语言学和C#器当作一个特定的表示,并且显然存在于大量的公共应用程序之前,而这些应用程序不出 libc、linux、甚至Ubuntu贡献者的控制之下。
当然,libc 可以尽量地应用于大写字母Ubuntu既有技能,使其 API 可以适应在此之后假设,但是,彻底改不定一个基本数据种类(像intmax_t)的形状,则会在格外大的应用软件生态的系统之前引起慌乱。
如果有人必需推论我是扯的,我则会很高兴,但据我所知,动手出这样的彻底改不定能够一个在此之后远距离举例来说,并且不允许任何为从新 ABI 协作的小数邮件/托在这个取而代之举例来说上行驶。当然,你可以这样动手,但我并不佩服任何动手了这些文书工作的Ubuntu。
依然,还有 x64 int 的诱因:它是相当基本的种类,而且多年来形状从不不定过,无数的操作的系统在显然对它动手了不用察觉的假设。这就是为什么 int 在 x64 上是 32 位的,尽管它“一定会”是 64 位的:int 多年来都是 32 位,以至于将软体换用到在此之后形状只不过无望,尽管它是一个全在此之后指令集和远距离举例来说。
我也只想要我的观点是扯的。如果 C 语言学只是一种独立的C#学,那我们就可以毫无顾虑地往前冲。但它本来不是了,它是一个条款,还是一个最糟的条款,而我们还必能够用它。
很难过,C,你征服了全球性,但似乎不再以外而今的美好。
书名元数据:
。上海妇科医院哪家比较专业深圳牛皮癣专科医院
广东男科医院哪家好
杭州男科医院挂号
武汉肝病医院哪家最好
眼屎多
血栓
新生儿科
甲流引起的咳嗽吃什么药
生殖中心
 
- 
									条纹西装外套很多姑娘都能穿出气场,大翻领+线条设计+颜色骚丽是何韵诗的标配
									你也可以在腰上加几条黄丝是不是也很有爱~除了纯白色也可以糅合一些亮色,与纯白色下半身连动在一起相比,转至一些亮色则会非常有个性。 橘红色毛衣外套很多妈妈都能外套不开场,大 
 
- 
									黄晓明版《鹿鼎记》将在泰国播出,一众女演员的感情现状如何!
									《经典之作品》是金庸老友的经典之作之一,印地语海外版的《经典之作品》将在2月24日在缅甸开播。印地语配音加上泰文字幕,不知道会是什么样的效果。而黄晓明海外版《经典之作品》开播16年后,一众女艺人的爱恋 
- 10-31初中地理考点知识速记顺口溜,牢记这15页,次次考试不大于95分
- 10-31一个公司值不值得你继续呆,要看两个“关键点”,不符合就辞任吧
- 10-31内卷离我们有多恰好
- 10-31豆瓣8.9《友你高飞》超治愈:在这里,你永远可以做你想做的事
- 10-31知乎上市后首份成绩单,有些“诡异”
- 10-31长城汽车:控股股东共计减持“长汽转债”350万张 占出版总量10%
- 10-31年级课本涂鸦火了,闰土变身“美少女战士”,让老师大开眼界
- 10-31雷佳音新剧仅播2集,卫视收视第二,终于有让我常在狂追的剧了
- 10-31美股科技类股再未遭抛售 能源和金融等价值股领跑
- 10-31集中隔离期,宣武区和平里四小的275名小学生如何度过?