1

Sorry if this question seem newbie, I've started learn C a few days ago.

I have a function(get_taken_values) that should take a string(char[81) as input and return two values: a int and a int[9].

As I saw here, it's not possible to return multiple values, so I had to use a struct.

Here's what I have:

#include <stdio.h>
#include <stdlib.h>

#define max_size 9
#define max_input 81

struct result {
    int position;
    int *taken;  
};

struct result get_result(int pos, int take[]){
    struct result res;

    res.position = pos;
    res.taken = take;

    return res;
}

struct result get_taken_values(char string_arg[max_input]){
    int position = 0;
    int* chosen = calloc(max_size, sizeof(int)); //suggestion from a previous question

    int someotherarray[9] = {1,2,3,4,5,6,7,8,9}; // fictional
    int i;
    for (i = 0; i < max_size; i ++) {
        chosen[i] = someotherarray[i]; 
    }

    struct result res = struct result(position, chosen);
    return res; // I to return int and int[9]: eg: 0,{1,2,3,4,5,6,7,8,9}
}

But I'm getting:

ex16.c: In function 'get_taken_values':
ex16.c:127:22: error: expected expression before 'struct'
  struct result res = struct result(position, chosen);
                      ^

I tried cutting the get_result, and creating the struct in get_taken_values:

// inside get_taken_values
struct result res;
res.position = position;
res.taken = chosen;
return res;

But it raises this:

c:/mingw/bin/../lib/gcc/mingw32/4.8.1/../../../libmingw32.a(main.o):main.c:(.text.startup+0xa7): undefined reference to `WinMain@16'
collect2.exe: error: ld returned 1 exit status

I intend to pass the return value from get_taken_values to Python and use it there.

What's the correct way of doing this?

Community
  • 1
  • 1
f.rodrigues
  • 3,499
  • 6
  • 26
  • 62
  • `struct result res = { position, chosen };`. Watch argument order though. – keltar Dec 29 '14 at 04:28
  • gives the same error `collect2.exe: error: ld returned 1 exit status` – f.rodrigues Dec 29 '14 at 04:31
  • Linker error is unrelated with your question; you'll get it even without this code. It says you don't have `WinMain` function. Do you? – keltar Dec 29 '14 at 04:32
  • The code provided is all I have. – f.rodrigues Dec 29 '14 at 04:34
  • 1
    Then the program is incomplete. Linker is very right to fail here. If you'll carefully explain what you want to achieve, someone might have an answer for you. For now, your problem clearly isn't with 'returning a struct', you'll have the same error when returning int etc.. – keltar Dec 29 '14 at 04:37
  • @f.rodrigues Maybe you should actually implement a `main()` function because execution in C starts from main(). What does your book tell you? ;-) – Masked Man Dec 29 '14 at 04:38
  • I don't want to run the program in C, I want to call it externally from python. – f.rodrigues Dec 29 '14 at 04:39
  • @f.rodrigues then don't try to link it in C. – Masked Man Dec 29 '14 at 04:41
  • @f.rodrigues - do you understand that the data on the stack will be destroyed as soon as the stack frame is destroyed? res is on the stack. Even if you return it, by the time the caller grabs it, the stack frame has been destroyed. Correct way is for the caller to supply the struct's pointer which the callee would populate. And also, you are missing the main() function. It would be best if you first learn C and then try to do something at this level. – RcnRcf Dec 29 '14 at 04:43
  • I want to use it as a shared library. – f.rodrigues Dec 29 '14 at 04:43
  • I'm learning, I was able to call other functions from python, now I'm upping the level a bit, I was able to create all the other elements, I 'm just stuck in returning them. The previous one that I was able to return was a int/float and other simple data types, now I'm trying to return a struct. And hence the question. – f.rodrigues Dec 29 '14 at 04:45
  • 1
    @RD445 it isn't returning pointer to local struct, rather copy of struct. It is fine. I'm not aware how python would know what to do with it though, but that is not something I used to work with. And someone should free this memory somewhen. But that is a separate question. – keltar Dec 29 '14 at 04:48
  • @f.rodrigues int/float values fit in register(s) and thus the values are easily returned. Most structs do not fit in register(s) and thus aren't returned in the same way as the basic data types are returned. Thus, pointers come in to play. The solution is what I specified above. I haven't developed code for Python but I know how C works and would imagine that Python's documentation would explain how you pass structs in and out. – RcnRcf Dec 29 '14 at 04:49
  • If you use `int taken[max_size]` in the struct, then you don't have to manually free memory. Just return the 40-byte struct (`4 + 9*4`) by value. Python ctypes handles the ABI correctly for returning structs by value with MSVC. For Win64, the first argument (register `rcx`) is a hidden argument (not visible in the API) that points to the caller's memory allocated for the result. ctypes does this for you if you set `get_taken_values.restype` to the `result` `ctypes.Structure` subclass with the given `_fields_`. – Eryk Sun Dec 29 '14 at 07:01
  • Also `char string_arg[max_input]` is pointless. C doesn't pass arrays. An array argument decays to a pointer, and the compiled code has no idea about the size `max_input`; just use `char *string_arg` and set `get_taken_values.argtypes = [ctypes.c_char_p]` in Python. – Eryk Sun Dec 29 '14 at 07:02
  • Did you solve the problem? – Iharob Al Asimi Dec 29 '14 at 14:41

2 Answers2

2

Just do this

#include <stdio.h>
#include <stdlib.h>

#define max_size 9
#define max_input 81

struct result {
    int position;
    int *taken;  
};

struct result get_result(int pos, int take[]){
    struct result res;

    res.position = pos;
    res.taken = take;

    return res;
}

struct result get_taken_values(char string_arg[max_input]){
    struct result res;

    res.position = 0;
    res.taken    = malloc(max_size * sizeof(int)); // you don't need to 
                                                   // initialize the values 
                                                   // you will do it below
    if (res.taken == NULL) // always check the success of malloc
        return res;
    int someotherarray[9] = {1,2,3,4,5,6,7,8,9}; // fictional
    int i;
    for (i = 0; i < max_size; i ++) {
        res.taken[i] = someotherarray[i]; 
    }
    return res; // I to return int and int[9]: eg: 0,{1,2,3,4,5,6,7,8,9}
}

you have to remember to call free(res.taken) at some point.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
1

There are a few things you should correct on the code:

On struct result get_taken_values(char string_arg[max_input]) There is no need for the 'max_input'. you can just declare '(char string_arg[])' meaning you are going to pass the function a pointer to a char type.

On int* chosen = calloc(max_size, sizeof(int));

You are going to inizinitialize the array later so no need to use calloc and you can use malloc. Always check that malloc dont return NULL.

This is how you shoud write it

int* chosen = malloc(max_size * sizeof(int)); 
if (chosen  == NULL) { 
    return res; 
}

And finnaly, you cant copy a struct using struct result res = struct result(position, chosen);

this is what you need to do:

struct result get_taken_values(char string_arg[max_input])
{
    struct result res;
    int* chosen = malloc(max_size * sizeof(int)); 
    if (chosen  == NULL) {
        return res;
    }

    res.taken = chosen;
    res.position = 0;

    int someotherarray[9] = {1,2,3,4,5,6,7,8,9};

    int i;
    for (i = 0; i < max_size; i ++) {
        res.taken[i] = someotherarray[i]; 
    }
    return res; 
    }
Ariel M
  • 175
  • 2
  • 10
  • This is NOT how you should have written it. The return value of `malloc()` shouldn't be casted. See http://stackoverflow.com/q/605845/3488231 – user12205 Dec 29 '14 at 11:08