可重现:为什么在 C 中传递这个对象会破坏我的代码?

2022-01-12 00:00:00 c crash c++

根据我的理解,C 假设所有参数都是 int 并且它返回 int.我想绕过这个对象,但我不知道如何和 AFAIK 它的 int 大小相同,但它会中断.这是可重现的代码.

From my understanding C assumes all parameters are int's and it returns ints. I'd like to pass around this object but i have no idea how and AFAIK its the same size of int but it breaks. Here is the Reproducible code.

在 testc.c 中.注意:这必须在 C 文件中.

In testc.c. Note: this MUST be in a C file.

int test_c1() {
    return test_c3(test_c2());
}

在 testcpp.cpp 中

In testcpp.cpp

#include <iostream>
using namespace std;
struct MyType{
    int a, b;
};

template <class T>
struct WrappedPointer {
    T* lhs;
public:
    void LHS(T*v) { lhs=v; }
    T*   LHS() { return lhs; }
    WrappedPointer(){}
    WrappedPointer(T*value) : lhs(value){}
    WrappedPointer(const WrappedPointer&v) : lhs(v.lhs){}
    T* operator->() const { return lhs; }
    T* operator*() const { return lhs; }
};
typedef WrappedPointer<MyType> ObjPtr;
static_assert(sizeof(ObjPtr) == sizeof(int), "");
static_assert(sizeof(ObjPtr) == sizeof(void*),"");

extern "C" {
    ObjPtr test_c1();
    ObjPtr test_c2() {
        //ObjPtr s=0;
        ObjPtr s;
        s.LHS(0);
        cout <<"c2 " << s.LHS() << endl;
        return s; 
    }
    ObjPtr test_c3(ObjPtr v) { 
        cout <<"c3 " << v.LHS() << endl;
        return v; 
    }
};

int main() {
    auto v = test_c1();
    cout <<"main " << v.LHS() << endl;
}

gcc 编译标志

gcc -Wall -c testc.c
testc.c: In function 'test_c1':
testc.c:2:2: warning: implicit declaration of function 'test_c3' [-Wimplicit-function-declaration]
testc.c:2:2: warning: implicit declaration of function 'test_c2' [-Wimplicit-function-declaration]
g++ -std=c++0x -Wall -c testcpp.cpp
g++ testc.o testcpp.o
a.exe

它应该崩溃,正如你所看到的,我得到的唯一警告是函数是隐式的 :(.为什么会崩溃?尤其是当我断言 ObjPtr 确实与 int 大小相同时. 我该如何解决这个问题,以便我可以绕过 ObjPtr?我不能修改 C 库,所以 testc.c 是禁区.

It should crash and as you can see the only warning i ever got was the function is implicit :(. Why does a crash? especially when i asserted that ObjPtr is indeed the same size as int. How do i fix this so that i can pass around ObjPtr? I CAN NOT modify the C library so testc.c is off limits.

-edit- 而不是在 VS 2010 中崩溃,我得到了这个显示传递的对象不正确的打印输出.我不明白B"最后来自哪里.这发生在调试模式下.释放访问冲突的崩溃.

-edit- instead of crashing in VS 2010 i get this printout which shows the passed object is incorrect. I don't understand where "B" comes from at the end. This happens in debug mode. Release crashes with access violation.

c2 00000000
c3 0046F8B0
main CCCCCCCC
B
Press any key to continue . . .

如果你好奇,如果你注释掉构造函数(并且什么都不改变),这将在 gcc 中工作.如果您将类更改为结构,因此没有成员是私有的,它将在 msvc2010 中工作.这个修复是无稽之谈,但当我这样做时它似乎考虑了 POD,并且神奇地代码工作.这很奇怪,因为 C 中的定义没有改变(因为没有定义).并且构造函数没有做任何不同的事情.

If your curious, if you comment out the constructors (and change nothing else) this will work in gcc. If you change class into struct so no member is private it will work in msvc2010. This fix is nonsense but it appears its consider POD when i do this and magically the code works. Which is weird since the definition in C hasn't changed (as there is no definition). And the constructors aren't doing anything different.

推荐答案

根据我的理解,C 假设所有参数都是 int,它返回 int.

From my understanding C assumes all parameters are int's and it returns ints.

不完全是.

在 1999 ISO C 标准之前,调用没有可见声明的函数会导致编译器假定它返回 int 类型的结果.这不适用于参数;它们被假定为参数的(提升的)类型,如果有的话.

Prior to the 1999 ISO C standard, calling a function without a visible declaration would cause the compiler to assume that it returns a result of type int. This does not apply to parameters; they're assumed to be of the (promoted) type(s) of the argument(s), if any.

C99 删除了隐式 int"规则;调用没有可见声明的函数是违反约束的,这基本上意味着它是非法的.即使在 1999 年之前,这也不是一个好主意.

C99 dropped the "implicit int" rule; calling a function without a visible declaration is a constraint violation, which basically means that it's illegal. It's not a good idea even in pre-1999 C.

如果要从 C 调用 C++,则调用的任何函数都应具有与两种语言兼容的参数和返回类型.C 没有类或模板,因此让 C 程序调用返回 WrappedPointer 的 C++ 函数至少是有问题的.

If you're going to call C++ from C, any functions you call should have parameter and return types that are compatible with both languages. C doesn't have classes or templates, so having a C program call a C++ function that returns a WrappedPointer<MyType> is at least questionable.

假设指针的大小与 int 相同,这会使您的代码非常不可移植.即使它们的尺寸相同,它们也不能互换;int 和指针函数的结果可能会使用不同的机制(例如不同的 CPU 寄存器)返回.

Assuming that pointers are the same size as int makes your code extremely non-portable. Even if they're the same size, they're not interchangeable; int and pointer function results might be returned using different mechanisms (different CPU registers, for example).

我建议让 test_c1()test_c2() 返回 void*,它可以指向任何类型的对象.而且您的 C 源代码需要对其调用的任何函数都有可见的声明(最好是原型).

I suggest having test_c1() and test_c2() return void*, which can point to an object of any type. And your C source needs to have visible declarations (preferably prototypes) for any functions it calls.

相关文章