【发布时间】:2013-03-15 02:10:08
【问题描述】:
Edit 3 描述了调试后缩小的问题
Edit 4 包含解决方案 - 这完全是关于 C 和 C# 之间的类型差异
今天我遇到了一个奇怪的问题。在 C 中,我有以下结构:
typedef struct s_z88i2
{
long Node;
long DOF;
long TypeFlag;
double CValue;
}s_z88i2;
另外我还有一个函数(这是简化版):
DLLIMPORT int pass_i2(s_z88i2 inp, int total)
{
long nkn=0,ifg=0,iflag1=0;
double wert=0;
int val;
// Testplace 1
nkn=inp.Node;
ifg=inp.DOF;
iflag1=inp.TypeFlag;
wert=inp.CValue;
// Testplace 2
return 0;
}
分配的值无处使用 - 我知道这一点。
当我到达// Testplace 1 时,会执行以下语句:
char tmpstr[256];
sprintf(tmpstr,"RB, Node#: %li, DOF#: %li, Type#: %li, Value: %f", inp.Node, inp.DOF, inp.TypeFlag, inp.CValue);
tmpstr 然后被传递到一个消息框。正如人们所期望的那样,它显示了我以一种良好而有序的方式传递给函数的结构中给出的值。继续通过函数,结构内的值被分配给一些变量。到达Testplace 2 时,将执行以下操作:
sprintf(tmpstr,"RB, Node#: %li, DOF#: %li, Type#: %li, Value: %f",nkn, ifg, iflag1, wert);
同样,tmpstr 被传递到消息框。然而,这并没有表明人们会期望什么。 Node 和 Type 的值仍然正确。对于DOF和Value,显示的值是0,这使我得出结论,在分配值期间出现了严重错误。我有时会设法找到一种方法来获取价值,而这与0 一样不正确。但是在我上次的测试中,我无法重现这个错误。
inp 的可能值为例如{2,1,1,-451.387},所以第一个1和-451.387被遗忘了。
有谁知道我做错了什么或如何解决这个问题?
非常感谢!
编辑:
将%i更改为%li,但结果没有改变。感谢放松!
我正在使用 MinGW 使用 Dev-Cpp 开发这个 dll(很遗憾),因为我无法说服 Visual Studio 2012 Pro 正确编译它。尽管原始来源的文档说它是普通的 ANSI-C。这让我有点恼火,因为我无法使用 Dev-Cpp 正确调试这个 dll。因此出现了消息框。
编辑 2:
正如 Neil Townsend 建议的那样,我转而传递参考。但这也没有解决问题。当我直接访问结构中的值时,一切都很好。当我将它们分配给变量时,有些会丢失。
关于我如何调用该函数的简短通知。该 dll 将从 C# 访问,所以我正在干预 P/Invoke(据我所知)。
[DllImport("z88rDLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int pass_i2(ref s_z88i2 inp, int total);
是我在 C# 中的定义。我导入了许多其他功能,它们都可以正常工作。正是这个功能我第一次遇到这些问题。我通过以下方式调用该函数:
s_z88i2 tmpi2 = FilesZ88.z88i2F.ConstraintsList[i];
int res = SimulationsCom.pass_i2(ref tmpi2, FilesZ88.z88i2F.ConstraintsList.Count);
首先设置结构,然后调用函数。
为什么在编译 ANSI-C 时为什么要挑剔 VS?这肯定会让事情变得更容易。
编辑 3:
我想我可以将问题缩小到sprintf。在说服 VS 构建我的 dll 之后,我能够逐步完成它。看起来这些值确实很好地分配给了它们所属的变量。但是,如果我想通过sprintf 打印这些变量,它们会变得相当空(0)。奇怪的是,这个值总是0而不是别的东西。我仍然对 为什么 sprintfbehaves 这种方式感兴趣,但我认为我最初的问题已经解决/恐慌被打败了。所以谢谢大家!
编辑 4:
正如 supercat 在下面指出的,我重新考虑了 C 和 C# 之间的类型兼容性。我知道 C# 中的 int 在 C 中的计算结果为 long。但是经过仔细检查后,我发现在 C 中我的变量确实是 FR_INT4 (出于清楚的原因,我将其排除在原始问题之外= > 坏主意)。在内部FR_INT4 被定义为:#define FR_INT4 long long,所以作为一个超长-长。一个快速测试表明,从 C# 传递一个 long 可以提供最好的兼容性。所以sprintf-issue 可以简化为问题:“long long 的格式标识符是什么?”。
%lli 实际上很简单。所以我可以宣布 drumroll 我的问题真的解决了!
sprintf(tmpstr,"RB, Node#: %lli, DOF#: %lli, Typ#: %lli, Wert: %f\n", inp.Node, inp.DOF, inp.TypeFlag, inp.CValue);
返回我想要的每个值。 非常感谢大家!
【问题讨论】:
-
long在你的 C 代码中代表什么类型?在 C# 中,类型long是一个 64 位整数,但在许多 C 实现中它是一个 32 位整数。 -
在我的 C 代码中,我有很长的时间(实际上它是一个
FR_INT4,它是 - 通过将编译器标志设置为#define FR_INT4 long long)从 C# 我传递一个int这是 - 我得到它从这里开始 - 真的很长一次传递给 C。或者我错了,FR_INT4真的很长,所以我应该从 C# 传递一个长? => 是的,我应该! (我会更新我的帖子。再一次 ;-) 感谢您指点我! -
C# 类型
long是System.Int64的别名。我建议所有依赖于内存布局的代码都应该使用能够清楚地识别所涉及项目大小的类型,并且尽可能地努力将所有大小为 2 的原语对齐到它们大小的倍数上(例如避免使用位移为 4、12、20 等字节的double或Int64项目。