【问题标题】:Assigning value from struct to variable fails将值从结构分配给变量失败
【发布时间】: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 项目。

标签: c# c struct assign


【解决方案1】:

为了澄清这一点:

我的结构实际上是:

typedef struct s_z88i2
{
    long long Node;
    long long DOF;
    long long TypeFlag;
    double CValue;
}s_z88i2;

这需要从 C# 传递 long(而不是像我之前认为的 int)。通过调试我发现赋值的行为是应该的,问题在sprintf之内。如果我使用%lli 作为格式标识符,即使这个问题也解决了。

sprintf(tmpstr,"RB, Node#: %lli, DOF#: %lli, Typ#: %lli, Wert: %f\n", inp.Node, inp.DOF, inp.TypeFlag, inp.CValue);

是我需要使用的语句。所以再次感谢所有贡献的人!

【讨论】:

    【解决方案2】:

    在 C 中,将引用或指针传递给结构而不是结构是一种更好的方法。所以:

    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;
    }
    

    您需要相应地更正 sprintf 行,inp.X 变为 inp->X。要使用此功能:

     // Option A - create it in a declaration, fill it, and send a pointer to that
     struct s_z88i2 thing;
     // fill out thing
     // eg. thing.Node = 2;
     pass_i2(&thing, TOTAL);
    

    或:

     // Option B - create a pointer; create the memory for the struct, fill it, and send the pointer
     struct s_z88i2 *thing;
     thing = malloc(sizeof(struct s_z88i2));
     // fill out thing
     // eg thing->Node = 2;
     pass_i2(thing, TOTAL);
    

    这样pass_i2 将对您发送的结构进行操作,并且它所做的任何更改都会在从pass_i2 返回时出现。

    【讨论】:

    • 谢谢,但这也不起作用。我会再次更新我的问题,包括更多细节。
    • 我对 c# 了解不多,但你在作业后使用(除了打印)DOF 和 wert 吗?您是否有可能不使用它们,因此优化编译器不会费心进行分配?您是否一次在调试器行中运行代码以查看发生了什么?
    • 在我的实际代码中,我确实使用了这些值(至少我打算这样做)。我认为这与 C# 无关,因为这些值正确地到达了我的 C 代码中。至于调试,我目前正试图说服 VS 接受我的 C 代码,以便我可以将 VS 用作调试器。或者您知道使用 Dev-Cpp 可以轻松实现这一点的任何方式吗?我会及时通知你!
    • 与此同时,我取得了一些成就!请参阅我的原始帖子以获取更新。
    • 好消息,它正在工作,sprintf 很奇怪。它们为 0 并不奇怪(您将它们初始化为),但奇怪的是 sprintf 没有接收到变量的变化。如果您将变量初始化为其他东西,看看 sprintf 做了什么会很有趣......
    【解决方案3】:

    使用格式说明符%i 格式化long 类型的值无效。你应该使用%li。

    【讨论】:

    • 这没有帮助,我将编辑我的帖子,包括编译器信息
    • 不过还是谢谢! (我应该在cmets tss中更有礼貌...)
    猜你喜欢
    • 2014-08-20
    • 1970-01-01
    • 2018-03-29
    • 1970-01-01
    • 2010-12-28
    • 2021-03-19
    • 2013-01-21
    • 1970-01-01
    • 2022-01-01
    相关资源
    最近更新 更多