0

I'm very new to C. I want to write C-code that I will wrap in Cython.
This is my C-code (a random-value generator based on a seed):

#include <math.h>

typedef struct {
    int seed;
    int bits;
    int max;
    double fmax;
    int value;
} rGen;

rGen rGen_Init(int seed, int start, int bits) {
    rGen gen;
    int max = pow(2, bits);
    gen.seed    = seed % max;
    gen.bits    = bits;
    gen.max     = max;
    gen.fmax    = (double) max;
    gen.value   = start % max;
    return gen;
}

double rGen_Next(rGen gen) {
    int value = gen.value;
    value = (gen.seed << gen.bits) * value / (gen.bits + 1);
    value = pow(value, 4);
    value = value % (gen.max - 1);
    gen.value = value;

    return (double) value / gen.fmax;
}

Doing a little check if the value attribute of the rGen instance does change:

#include "pyran.c"
#include <stdio.h>

int main() {
    rGen gen = rGen_Init(1000, 200, 32); // Create an initialized struct-instance
    int i;
    for(i = 0; i < 10; i += 1) {
        rGen_Next(gen);
        printf("%i\n", gen.value);
    }
    return 0;
}

The output is:

200
200
200
200
200
200
200
200
200
200

The value of the value attribute does not change. Why does it behave like this ? Do I have to use pointers ? And if so, how do I use them ?

Niklas R
  • 16,299
  • 28
  • 108
  • 203
  • It suppose this would be easier to answer if there would be straight C code showing the problem. – pmr Aug 20 '11 at 15:20

2 Answers2

2

rGen_Next takes its argument by value, modifies the copy and discards it. To actually modify it, take a pointer argument.

double rGen_Next(rGen* gen) {
    int value = gen->value;
    value = (gen->seed << gen->bits) * value / (gen->bits + 1);
    value = pow(value, 4);
    value = value % (gen->max - 1);
    gen->value = value;

    return (double) value / gen->fmax;
}

Call it like this: rGen_Next(&gen); if gen is a variable with automatic storage duration. Also: It is good C practice to have an init function that takes a pointer to the object actually being initialized. This way it is up to the user how the structure is allocated.

bool rGen_Init(rGen* gen, int seed, int start, int bits) {
    int max = pow(2, bits);
    gen->seed    = seed % max;
    gen->bits    = bits;
    gen->max     = max;
    gen->fmax    = (double) max;
    gen->value   = start % max;

    // omit the return if there is no chance of failure, 
    return true;
}
pmr
  • 58,701
  • 10
  • 113
  • 156
  • I'll check it out ! Why do I have to use `->` now ? Because it's a pointer ? – Niklas R Aug 20 '11 at 15:49
  • @Niklas `->` dereferences a pointer it is sugar for `(*val).`. C makes a difference between values and references (in Python everything is a reference if I recall correctly). Using `*` yields the value pointed to and `.` accesses it. You should get a C introduction before you start writing it :) – pmr Aug 20 '11 at 15:53
  • Thank you ! Hehe, C is very complex, I already read some stuff, but nothing could explain the pointer stuff good to me. But I think I got it now. :) – Niklas R Aug 20 '11 at 15:54
  • @Summer_More_More_Tea Yes that was the problem. :) – Niklas R Aug 20 '11 at 16:48
1

The lifetime of gen instance defined in rGen_init terminates when function returns. You can define the function rGen_init(struct rGen *gen,...) and modify parameter gen using this pointer.

Summer_More_More_Tea
  • 12,740
  • 12
  • 51
  • 83
  • Can't I write a function that does already return a pointer ? Also, is the struct-instance copied when calling `rGen_Next`? Otherwise it's `value` attribute would've changed, right ? – Niklas R Aug 20 '11 at 15:25
  • @Niklas Yes, rGen_Next takes its argument by value. – pmr Aug 20 '11 at 15:38
  • You can write a function return a pointer, but make sure the instance is still alive(whose memory will not be released/deallocated) when function returns. And yes, you are right, the struct is copied when calling rGen_Next. The problem is that your function modify a copy of `struct rGen` instance, instead of the instance itself. If you wanna modify the instance, you should pass a pointer as argument(which is pass-by-reference/address) instead of pass a struct instance(which is pass-by-value). – Summer_More_More_Tea Aug 20 '11 at 15:40
  • Ahhh. :) When changing it all to pointers, the compiler (gcc) tells me that I `request for member '(any attribute of rGen here)' in something not a structure or union`. What does this error mean ? – Niklas R Aug 20 '11 at 15:44
  • @Niklas you can refer this thread http://stackoverflow.com/questions/2184419/what-does-request-for-member-in-something-not-a-structure-or-union-me. Maybe that's where the problem resides. If you can provide code snippet we can analyse case by case. :-) – Summer_More_More_Tea Aug 20 '11 at 15:50