我知道C++ 中的"未定义行为"几乎可以让编译器做任何想做的事情.但是,我遇到了让我感到惊讶的崩溃,因为我认为代码足够安全.
在这种情况下,真正的问题仅发生在使用特定编译器的特定平台上,并且仅在启用了优化时才发生.
我尝试了几件事来重现问题并将其简化到最大程度.这是一个名为的函数的摘录Serialize,它将获取bool参数,并将字符串true或复制false到现有的目标缓冲区.
如果bool参数是未初始化的值,那么这个函数是否会在代码审查中,没有办法告诉它实际上可能会崩溃?
// Zero-filled global buffer of 16 characters
char destBuffer[16];
void Serialize(bool boolValue) {
// Determine which string to print based on boolValue
const char* whichString = boolValue ? "true" : "false";
// Compute the length of the string we selected
const size_t len = strlen(whichString);
// Copy string into destination buffer, which is zero-filled (thus already null-terminated)
memcpy(destBuffer, whichString, len);
}
Run Code Online (Sandbox Code Playgroud)
如果使用clang 5.0.0 +优化执行此代码,它将/可能崩溃.
boolValue ? "true" …
我从未明白ABI是什么.请不要指向维基百科的文章.如果我能理解它,我就不会在这里张贴这么长的帖子.
这是我对不同界面的看法:
电视遥控器是用户和电视之间的接口.它是一个现有实体,但本身无用(不提供任何功能).遥控器上每个按钮的所有功能都在电视机中实现.
接口:它是间"现有实体"层
functionality和consumer的该功能.接口本身不起作用.它只是调用后面的功能.现在取决于用户是谁,有不同类型的接口.
命令行界面(CLI)命令是现有实体,消费者是用户和功能所在.
functionality:我的软件功能解决了我们描述这个界面的一些目的.
existing entities:命令
consumer:用户图形用户界面(GUI)窗口,按钮等是现有实体,消费者再次是用户和功能所在.
functionality:我的软件功能解决了我们描述这个界面的一些问题.
existing entities:窗口,按钮等..
consumer:用户应用程序编程接口(API)函数(或更正确的)接口(在基于接口的编程中)是现有实体,这里的消费者是另一个程序而不是用户,并且该层后面的功能也是如此.
functionality:我的软件功能解决了我们描述这个界面的一些问题.
existing entities:函数,接口(函数数组).
consumer:另一个程序/应用程序应用程序二进制接口(ABI)这是我的问题开始的地方.
functionality:???
existing entities:???
consumer:???
ABI涵盖了诸如此类的详细信息
- 数据类型,大小和对齐方式;
- 调用约定,它控制函数参数的传递方式并返回检索的值;
- 系统调用号码以及应用程序应如何向操作系统进行系统调用;
其他ABI标准化细节,如
- C++名称错误,
- 异常传播,和
- 在同一平台上调用编译器之间的约定,但不需要跨平台兼容性.
谁需要这些细节?请不要说操作系统.我知道汇编编程.我知道链接和加载是如何工作的.我确切地知道里面发生了什么.
为什么C++名称输入?我以为我们正在谈二进制.语言为什么会进来?
无论如何,我已经下载了[PDF] System V Application Binary Interface Edition 4.1(1997-03-18),看看它究竟包含了什么.好吧,大部分都没有任何意义.
为什么它包含两章(第4和第5章)来描述ELF文件格式?实际上,这些是该规范中仅有的两个重要章节.其余章节是"处理器特定的".无论如何,我认为这是一个完全不同的话题.请不要说ELF文件格式规范是 ABI.根据定义,它不符合接口的条件.
我知道,因为我们谈论的水平很低,所以必须非常具体.但我不确定它是如何"指令集架构(ISA)"具体的?
我在哪里可以找到Microsoft Windows的ABI?
所以,这些是困扰我的主要问题.
我是linux系统编程的新手,在阅读Linux系统编程时遇到了API和ABI .
API的定义:
API定义了一个软件在源级别与另一个软件通信的接口.
ABI的定义:
API定义源接口,而ABI定义特定体系结构上两个或多个软件之间的低级二进制接口.它定义了应用程序如何与自身交互,应用程序如何与内核交互以及应用程序如何与库交互.
程序如何在源级别进行通信?什么是源级别?它无论如何都与源代码有关?或者库的源代码包含在主程序中?
我所知道的唯一区别是API主要由程序员使用,而ABI主要由编译器使用.
在C中按值传递结构是否有任何缺点,而不是传递指针?
如果结构很大,显然存在复制大量数据的性能方面,但对于较小的结构,它应该基本上与将多个值传递给函数相同.
当用作返回值时,它可能更有趣.C只有函数的单个返回值,但是你经常需要几个.所以一个简单的解决方案是将它们放在一个结构中并返回它.
是否有任何理由支持或反对这一点?
因为对于每个人来说,我在这里谈论的内容可能并不明显,所以我举一个简单的例子.
如果您使用C编程,您迟早会开始编写如下所示的函数:
void examine_data(const char *ptr, size_t len)
{
...
}
char *p = ...;
size_t l = ...;
examine_data(p, l);
Run Code Online (Sandbox Code Playgroud)
这不是问题.唯一的问题是你必须同意你的同事的参数顺序,所以你在所有功能中使用相同的约定.
但是当你想要返回相同类型的信息时会发生什么?你通常得到这样的东西:
char *get_data(size_t *len);
{
...
*len = ...datalen...;
return ...data...;
}
size_t len;
char *p = get_data(&len);
Run Code Online (Sandbox Code Playgroud)
这很好,但问题更多.返回值是返回值,但在此实现中它不是.从上面没有办法说明函数get_data不允许查看len指向的内容.并且没有任何东西可以使编译器检查实际通过该指针返回的值.那么下个月,当其他人修改代码时却没有正确理解它(因为他没有阅读文档?)它会在没有人注意的情况下破坏,或者随机开始崩溃.
所以,我提出的解决方案是简单的结构
struct blob { char *ptr; size_t len; }
Run Code Online (Sandbox Code Playgroud)
这些例子可以像这样重写:
void examine_data(const struct blob data)
{
... use data.tr and data.len ...
}
struct blob = { .ptr = ..., .len = ... };
examine_data(blob); …Run Code Online (Sandbox Code Playgroud) 以下链接解释了UNIX(BSD风格)和Linux的x86-32系统调用约定:
但是UNIX和Linux上的x86-64系统调用约定是什么?
我正在准备C中的一些培训材料,我希望我的示例适合典型的堆栈模型.
C堆栈在Linux,Windows,Mac OSX(PPC和x86),Solaris和最新的Unix中的发展方向是什么?
如何将类对象(尤其是STL对象)传递给C++ DLL?
我的应用程序必须以DLL文件的形式与第三方插件交互,我无法控制这些插件构建的编译器.我知道STL对象没有保证ABI,我担心我的应用程序会导致不稳定.
我正在观看Chandler Carruth在CppCon 2019中的演讲:
在该示例中,他举例说明了您对使用std::unique_ptr<int>over和会产生多少开销而感到惊讶int*。该段大约在时间点17:25开始。
您可以看一下他的示例代码对(godbolt.org)的编译结果 -可以看到,确实,编译器似乎不愿意传递unique_ptr值-实际上,底线是只是一个地址-在寄存器内,仅在直接内存中。
Carruth先生在27:00左右提出的观点之一是,C ++ ABI要求按值传递参数(某些但不是全部;也许-非基本类型?而不是在寄存器中。
我的问题:
PS-为了不给这个问题留下代码:
普通指针:
void bar(int* ptr) noexcept;
void baz(int* ptr) noexcept;
void foo(int* ptr) noexcept {
if (*ptr > 42) {
bar(ptr);
*ptr = 42;
}
baz(ptr);
}
Run Code Online (Sandbox Code Playgroud)
唯一指针:
using std::unique_ptr;
void bar(int* ptr) noexcept;
void baz(unique_ptr<int> ptr) noexcept;
void foo(unique_ptr<int> ptr) noexcept {
if (*ptr > 42) {
bar(ptr.get());
*ptr = 42;
}
baz(std::move(ptr));
}
Run Code Online (Sandbox Code Playgroud) 假设我有三个编译对象,都是由相同的编译器/版本生成的:
为简单起见,我们假设所有头文件都是用C++ 11编写的,只使用其语义在所有三个标准版本之间没有变化的构造,因此任何相互依赖性都用头包含正确表达,编译器没有反对.
这些对象的组合是什么,链接到单个二进制文件是不安全的?为什么?
编辑:欢迎涵盖主要编译器(例如gcc,clang,vs ++)的答案
在去年我使用的是nlohmann json库[1]并使用GCC 5.x在x86_64上进行交叉编译arm-linux-gnueabi-*,没有任何警告.当我将GCC更新为更新版本时,GCC会生成一些神秘的诊断笔记.例如,这是其中一个注释
In file included from /usr/arm-linux-gnueabi/include/c++/7/vector:69:0,
from include/json.hpp:58,
from src/write_hsi.cpp:23:
/usr/arm-linux-gnueabi/include/c++/7/bits/vector.tcc: In member function ‘void std::vector<_Tp, _Alloc>::_M_realloc_insert(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long int, long long unsigned int, double, std::allocator, nlohmann::adl_serializer>}; _Tp = nlohmann::basic_json<>; _Alloc = std::allocator<nlohmann::basic_json<> >]’:
/usr/arm-linux-gnueabi/include/c++/7/bits/vector.tcc:394:7: note: parameter passing for argument of type ‘std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >::iterator {aka __gnu_cxx::__normal_iterator<nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >}’ changed in GCC 7.1
vector<_Tp, _Alloc>::
^~~~~~~~~~~~~~~~~~~ …Run Code Online (Sandbox Code Playgroud)