为什么通过引用传递的参数没有在函数中修改?

Why is my parameter passed by reference not modified within the function?

提问人: 提问时间:9/29/2008 最后编辑:12 revs, 3 users 54%Barth 更新时间:3/5/2016 访问量:1231

问:

我在静态库中有一个 C 函数,我们称之为 A,具有以下接口:

int A(unsigned int a, unsigned long long b, unsigned int *y, unsigned char *z);

此函数将更改 y 和 z 的值(这是肯定的)。我从动态 C++ 库中使用它,使用 extern “C”。

现在,这让我感到不安:

  • y 设置正确,z 未更改。我的意思是,如果两者都使用(尖)值 666 初始化,则 y 指向的值将在调用后发生变化,但 z 指向的值(仍然是 666)不会改变。
  • 当从 C 二进制文件调用时,此函数可以无缝工作(值 由 z 指向的被修改)。
  • 如果我创建一个虚拟 C 库,其中包含具有相同原型的函数,并且我从动态 C++ 库中使用它,它运行良好。如果我重用相同的变量来调用 A(..),我得到的结果和以前一样,z 不会改变。

我认为以上几点表明,声明我的变量并不是一个愚蠢的错误。

我显然被卡住了,我无法更改 C 库。你对问题可能是什么有任何线索吗? 我在考虑 C/C++ 接口上的一个问题,每个实例解释 char* 的方式。

编辑:我终于发现了问题所在。请看下面我的回答。

C++ C 参数 链接器 按引用传递

评论

0赞 Alexander 9/29/2008
你在第 2 点中提到的 C 二进制文件,是你最近编译的吗?如果没有,如果重新编译二进制文件会发生什么?我想知道 C 库中的函数是否真的在做它应该做的事情。
0赞 Martin York 9/30/2008
我很好奇你为什么用英语来描述你可以通过显示代码来准确显示的内容。我们只有你的话,你正在正确地安排事情。但是,如果您向我们展示代码,我们将知道您是否正确。
0赞 Barth 9/30/2008
好吧,我认为代码有点太复杂了,不能直接放在这里。但是,你是对的,下次我会考虑至少给出一个简化的代码,如果它有意义的话。

答:

1赞 quinmars 9/29/2008 #1

据我所知,long long 不是标准 C++ 的一部分,也许这就是您问题的根源。

0赞 Greg Hewgill 9/29/2008 #2

在您的 C++ 程序中,原型是否声明为 ?extern "C"

1赞 lr2 9/29/2008 #3

不知道。尝试调试单步执行 A 并查看会发生什么(汇编代码警报!

评论

0赞 Barth 9/29/2008
你对“汇编代码警报”是正确的,C 库不包含符号......我不确定我是否能够理解这一点。
1赞 Tyler 9/29/2008 #4

也许您可以将原始函数包装在从 C++ 库调用的 C 库中?

根据您的第 2 点和第 3 点,这似乎可以工作。

如果没有,它会给你另一个调试点来寻找更多线索 - 看看故障首先出现在你的哪个库中,并检查为什么 2 和 3 有效,但这没有 - 最小的区别是什么?

您还可以尝试检查函数调用在每种情况下设置的堆栈,以检查差异是否在这里 - 考虑不同的调用约定。

1赞 Alexander 9/29/2008 #5

步骤1:将从C++端传递的指针y和z与C函数接收的指针进行比较。

P.S. 我不想听起来很明显,但只是在这里仔细检查一下。我想当你说 z 在从 C 二进制文件调用时被很好地修改时,你的意思是 z 所指向的数据被很好地修改了。指针 y 和 z 本身是按值传递的,因此您无法更改指针。

评论

0赞 Barth 9/29/2008
是的,当我说 Z 被修改时,我的意思是 z 指向的数据被修改了。我会看看双方的指针。谢谢
4赞 SmacL 9/29/2008 #6

看起来你的 C 库和 C++ 编译器处理长多头的方式有所不同。我的猜测是,C 库可能是 C89 之前的标准,并且实际上将 64 位长视为 32 位。您的 C++ 库正在正确处理它并在调用堆栈上放置 64 位,从而损坏 y 和 z。也许尝试通过 *int A(unsigned int a, unsigned long b, unsigned int *y, unsigned char z) 调用函数,看看你会得到什么。

只是一个想法。

评论

0赞 Alexander 9/29/2008
假设上述内容是正确的,如果没有建议的修改,C 函数将看到 y = 0(在小端架构上),并且 z 指向 C++ y 指向的位置。
0赞 SmacL 9/29/2008
是的。假设 y 是一个指针,它就变成了架构 depentent。我的猜测是 OP 使用的是 32 位系统,因此 y=0 可能会更好,尽管 y=NULL 更好。
0赞 Barth 9/29/2008
我尝试了您的建议(用无符号的长号打电话),但没有帮助。Alexander :y 被 A() 修改,z 只是指向与调用前相同的值。
0赞 SmacL 9/29/2008
好吧,我很可能是错的,因为这只是一个猜测。尝试在两个编译器中检查 sizeof(unsigned long long)。两者都应为 8 个字节。如果没有,这是你的问题,你需要一个新的原型,否则就是别的东西。
2赞 Roddy 9/29/2008 #7

这是其中一个问题,从你所描述的内容来看,没有任何明显的错误,但事情并没有按照你预期的方式进行。

我认为你应该编辑你的帖子以提供更多信息,以便获得一些明智的答案。特别是,让我们从以下方面开始:

  • 此代码适用于什么平台: Windows,linux,嵌入式的东西 或。。。?
  • C是什么编译器 静态库内置?
  • 什么 编译器是 C++ 动态库 内置?
  • C是什么编译器 可以成功调用 图书馆建有?
  • 你有 源代码级调试器?如果是这样,可以 您 C++。

除非你对 A 总是修改 Z 指向的数据是错误的,否则你问题的唯一可能原因是参数传递约定之间的不兼容。“长多”的问题可能暗示事情并不像看起来那样。

作为最后的手段,您可以比较反汇编的 C++ 调用代码(您说失败)和 C 调用代码(您说成功),或者使用调试器逐步执行 CPU 指令(是的,真的 - 您将学到很好的技能并解决问题)

1赞 Alexander 9/29/2008 #8

另一个疯狂的猜测:你确定你正在链接到你的 C 库中函数的正确实例吗?难道您的库中有几个这样的功能可用吗?在 C 语言中,链接器在决定如何解析函数时并不关心返回类型或参数列表——只有名称很重要。因此,如果您有多个同名函数...

可以通过编程方式验证函数的标识。创建一个 C 库,该库使用一些测试参数调用函数 A,该库工作正常,并打印指向函数 A C++的指针。然后打印指向从 C++ 代码中看到的原始 A 函数的指针,并将指针与在同一进程中调用时 C 库看到的指针进行比较。

1赞 Alexander 9/29/2008 #9

再一次,一个显而易见的,但谁知道呢......您确定您调用的 C 函数是无状态的,这意味着它的输出仅取决于其输入吗?如果函数不是无状态的,则可能是“隐藏”状态导致从 C++ 应用调用时函数的不同行为(不更改指向的数据)。z

1赞 Barth 9/30/2008 #10

首先,非常感谢大家的帮助。 多亏了你给我的众多想法和线索,我终于能够解决这个问题。你的建议帮助我质疑我所认为的理所当然的事情。

对我的问题的简短回答:问题是我的C++库使用了旧版本的C库。这个旧版本错过了第 4 个参数。因此,第四个论点显然从未改变过。

我现在有点惭愧,因为我意识到这就是问题所在。但是,我被我的代码编译良好的事实所误导。这是因为 C++ 库是针对正确版本的 C 库编译的,但在运行时它使用了与我正在使用的另一个库静态链接的旧版本。

C++ Lib (M) ---> dyn C++ lib (N) ---> C lib (P) v.1.0
     |
     ------> C lib (P) v.1.1

(N) 是一个动态库,它与 (P) 版本 1.0 静态链接。 编译器接受了从 (M) 到具有 4 个参数的函数的调用,因为我链接到 (P) 版本 1.1,但在运行时它使用了旧版本的 (P)。

请随意编辑此答案或问题,或要求我这样做。

评论

0赞 Barth 9/30/2008
谢谢。你的答案对我有很大帮助(询问我是否链接到每个实例的 C 库中函数的正确实例,以及其他实例)。干杯!