2

Edit 3 describes the narrowed-down problem after debugging

Edit 4 contains the solution - it's all about type difference between C and C#

Today I came across a curious problem. In C I have the following struct:

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

}s_z88i2;

Furthermore I have a function (this is a simplified version):

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

The assigned values are used nowhere - I'm aware of that. When I reach // Testplace 1 the following statement is executed:

char tmpstr[256];
sprintf(tmpstr,"RB, Node#: %li, DOF#: %li, Type#: %li, Value: %f", inp.Node, inp.DOF, inp.TypeFlag, inp.CValue);

tmpstr then is passed to a messagebox. It shows - as one would expect - the values given in my struct I passed to the function in an nice and orderly way. Moving on through the function the values inside the struct get assigned to some variables. On reaching Testplace 2 the following is executed:

sprintf(tmpstr,"RB, Node#: %li, DOF#: %li, Type#: %li, Value: %f",nkn, ifg, iflag1, wert);

Again, tmpstr is passed to a messagebox. However, this doesn't show what one would expect. The values for Node and Typeare still correct. For DOFand Value the displayed values are 0 which leads me to the conclusion that something is going terribly wrong during assigning the values. I somehow sometimes managed to get a way to long number for value whis was as incorrect as 0. But I have not been able to reproduce that mistake during my last tests. Possible values for inp are e.g. {2,1,1,-451.387}, so the first 1 and -451.387are forgotten.

Does anyone know what I'm doing wrong or how to fix this?

Many thanks in advance!

Edit:

Changed %ito %li but the result did not change. Thank to unwind!

I'm developing this dll with Dev-Cpp using MinGW (unfortunately) because I wasn't able to convince Visual Studio 2012 Pro to compile this properly. Although the documentation of the original source says it is plain ANSI-C. This bugs me a bit because I cannot debug this dll properly with Dev-Cpp. Hence the messageboxes.

Edit 2:

As Neil Townsend suggested, I switched to passing a reference. But this also did not cure the problem. When I access the values in my struct directly everything is fine. When I assign them to variables some get lost.

A short notice on how I'm calling the function. The dll is to be accessed from C#, so I'm meddeling with P/Invoke (as I get it).

[DllImport("z88rDLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int pass_i2(ref s_z88i2 inp, int total);

is my definition in C#. I have lots of other functions imported and they all work fine. It is this function I encounter these Problems for the first time. I call the function via:

s_z88i2 tmpi2 = FilesZ88.z88i2F.ConstraintsList[i];
int res = SimulationsCom.pass_i2(ref tmpi2, FilesZ88.z88i2F.ConstraintsList.Count);

First I set the struct, then I call the function.

Why Oh Why has VS to be picky when it comes to compiling ANSI-C? It certainly would make things easier.

Edit 3:

I can narrow the problem down to sprintf, I think. Having convinced VS to build my dll I was able to step through it. It appears that the values are assigned very nicely indeed to the variables they belong in. If, however I want to print these variables via sprintf they turn out rather empty (0). Curiously, that the value is always 0and not something else. I'm still interested in why sprintfbehaves that way, but I consider my initial problem solved/panic defeated. So thanks everyone!

Edit 4:

As supercat points out below, I had a rethink about type-compatibility between C and C#. I was aware that an int in C# evaluates as a long in C. But after double-checking I found that in C my variables are really FR_INT4 (which I kept out of the original question for reasons of clarity => bad idea). Internally FR_INT4 is defined as: #define FR_INT4 long long, so as a super-long-long. A quick test showed that passing a long from C# gives the best compatibility. So the sprintf-issue can maybe be simplified to the question: "What is the format-identifier of a long long?".

It is %lli which would is quite simple, actually. So I can announce drumroll that my problem really is solved!

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

returns every value I want. Thank you very much everyone!

3
  • What type does long represent in your C code? In C#, type long is a 64-bit integer, but in many C implementations it's a 32-bit integer. Commented Mar 26, 2013 at 19:31
  • In my C code I have long (actually it is a FR_INT4 which is - by compiler flags set to #define FR_INT4 long long) From C# I am passing an int which is - as I get it from here - really a long once passed to C. Or am I mistaken and FR_INT4 is really a very long long so I should pass a long from C#? => Yes I should! (I'll update my post. Yet again ;-) Thanks for pointing me to it! Commented Mar 27, 2013 at 7:21
  • The C# type long is an alias for System.Int64. I would suggest that all code which relies upon memory layout should use types that clearly identify the size of the items involved and also that, whenever possible, one endeavor to align all power-of-two-sized primitives on multiples of their size (e.g. avoid having double or Int64 items with a displacement of 4, 12, 20, etc. bytes). Commented Mar 27, 2013 at 16:17

3 Answers 3

1

Formatting a value of type long with the format specifier %i is not valid. You should use %li.

Sign up to request clarification or add additional context in comments.

2 Comments

This does not help, I will edit my post including compilerinfo
But thanks anyway! (I should be more polite in comments tss...)
0

In C, it is a better approach to pass a reference or pointer to the struct rather than the struct. So:

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

You will need to correct the sprintf liness accordingly, inp.X becomes inp->X. To use this function either:

 // 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);

or:

 // 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);

This way pass_i2 will operate on the struct you send it, and any changes it makes will be there on return from pass_i2.

7 Comments

Thank you, but that doesn't work either. I'll update my Question again, including mor detail.
I don't know a lot about c#, but do you use (other than printing) DOF and wert after the assignments? Is it possible that you don't use them and so an optimising compiler doesn't bother to make the assignment? Have you run the code in a debugger line at a time to see what's going on?
In my actual code I do use the values (at least I intend to do so). I think that this has nothing to do with C#, since the values arrive in my C-code correctly. As for the debugging, I'm currently trying to convince VS to accept my C-code so I can use VS as a debugger. Or do you know any way this would be possible comfortably with Dev-Cpp? I'll keep you updated!
In the meanwhile I managed to achieve something! See my original post for update.
Good news that it is working, very odd about sprintf. It's not odd that they are 0 (it's what you initialise them as), but it is odd that sprintf isn't picking up the change in the variable. It would be interesting to see what the sprintf did if you initialised the variables to something else ...
|
0

To clarify this as answered:

My struct actually is:

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

which requires long to be passed from C# (and not int as I previously thought). Through debugging I found out that the assignment of values behaves as it should, the problem was within sprintf. If I use %lli as format-identifier even this problem is solved.

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

Is the statement I need to use. So thanks again everyone who contributed!

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.