1

I'm coming from c#, and this is causing me more issues than I would've expected. I've not seen a straight forward solution to this. I'm brand new to C, just playing with it, and all of the various implementation I've tried, from using structs, to passing pointers around isn't getting the job done. (local values aren't changing). Can someone give me a cut and dry example?

Here's a sample of my code

#include <stdio.h>

//alter values based on input
int * doStuff(int a, int b){
   int input;
   int arr[2]
   scanf("%d", &option);
   switch(option);
   case 1:
     arr[0] = a-2;
     arr[1] = b;
     return arr;
   break;
   case 2:
     arr[0] = a;
     arr[1] = b-2;
     return arr;
   break;
   default:
     return a, b;
}

main(){
   int a = 20;
   int b = 20;
   int returnedObject[2];
   //what i need is like in c# how I can do this
   returnedObject = doStuff(a, b);
    a = returnedObject[0];
   b= returnedObject[1];

}

I need to pass values, alter them, return them, and set the local variables. I'm completely lost with some of the more complex examples. Coming from c# pointers, arrays, how they work is losing me, and it isn't as simple as I had expected.

nathan rogers
  • 189
  • 5
  • 19

4 Answers4

2

Functions in C cannot return array types, nor can arrays be the target of an assignment. So code like

int returnedObject[2];
returnedObject = doStuff( a, b );

cannot work. C functions can return pointers to arrays, but I don't think you want to get into that.

C functions cannot return multiple objects, nor is multiple assignment supported.

If your two values are logically attributes of some larger, aggregate type, you can use a struct type to return a single object with multiple attributes, such as

struct result { int x, int y };

struct result doStuff( int a, int b )
{
  struct result ret = {a, b}; // initialize struct with inputs
  ...
  case 1:
    ret.x = a-2;
    break;  // y remains unchanged

  case 2:
    ret.y = b-2;
    break; // x remains unchanged
  ...
  return ret;
}

which you would call as

struct result stuff;
stuff = doStuff( a, b );

Alternately, you can pass pointers to your values and update them as necessary:

void doStuff( int *a, int *b )
{
  ...
  case 1:
    *a = *a - 2;
    break;

  case 2:
    *b = *b - 2;
    break;
}

which you would call as

doStuff( &a, &b );

In this case you don't actually return anything to the caller.

You can pass an array as an argument to the function, like so:

void doStuff( int a, int b, int *result )
{
  result[0] = a; 
  result[1] = b;
  ...
  case 1:
    result[0] = a - 2;
    break;

  case 2:
    result[1] = b - 2;
    break;
  ...
}

which you would call as

int results[2];
int a;
int b;
...
doStuff( a, b, results );

Note that when we pass the array expression to the function, its type is changed from int [2] to int *. Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize another array, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array. Array expressions in C lose their "array-ness" in most circumstances1. You can use the subscript operator [] on a pointer expression as you would an array expression (the array index operation a[i] is defined as *(a + i)), but pointers are not arrays and vice versa.


1. Note that I'm talking about the expression, not the object. The array object always stays an array object.
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • This is almost exactly what I wound up doing yesterday. Thanks for the explanation. I actually met a pair of c developers at the bar last night where I was working, and they helped me through after I asked :P Essentially, I created a struct and declared a collection of that type, "newed up" the struct locally, and passed the array as paramter. I know that passing by value is considered bad practice, but for first day with C, I'll take it. I see the question with 500 votes talking about pointers and references, I think that will help me take the next step. Thanks so much for all your help. – nathan rogers Oct 25 '15 at 22:08
  • @nathanrogers: "passing by value" is the only option C offers; we *fake* pass-by-reference by passing pointers, but ultimately all parameters are passed by value. As I mentioned above, arrays are special in that array expressions "decay" to pointer expressions under most circumstances, but that has more to do with the array subscripting operation than passing array expressions as arguments. – John Bode Oct 26 '15 at 14:28
1

Make the return type int *.

Create a local variable int *result = malloc(sizeof(int) * 2); and put your a and b or whatever you need to return in result.

When you call the function, assign the result to an int *.

int *res;
res = doStuff(a, b);
a = res[0];
b = res[1];

malloc allocates space for result on the heap. Its input is the number of bytes you need.

2 ints, 4 bytes per int -> 8 bytes == 2*sizeof(int)

The problem is that every time you call your function, you will keep allocating more and more space. C doesn't do garbage collection for you, so when you're done using your int *res, you should free that space by calling free(res).

Further Details:

In C#, you would initialize an array like this: int[] result = new result[2];

Have you every tried printing an array like this Console.WriteLine(myarray);. It doesn't work does it? C# I believe prints something like System.Int32[]. If you did this in Java (System.out.println(myarray);), it would print some weird number.

That number is actually the address of the array.

When you reference your array myarray, you are actually referencing the address of the array. When you do something like myarray[1], you are getting the address of myarray, adding to it 1*sizeof(int) and fetching that value.

In C, you can define arrays in the typical way: int arr[2]; which allocates space for 2 ints.

Alternatively, you can do: int *arr; which defines a pointer (or address). However, you need to tell it to allocate space for integers. As it is now, arr doesn't really point to anything. You allocate space by calling malloc: arr = malloc(2*sizeof(int)); which dynamically allocates space for 2 integers.

Now you can put stuff in your array. arr[0] = 1.

The [] is effectively doing pointer arithmetic.

arr[1] is the same as *(arr + 1)

arr + 1 is getting the address of the array and adding 1 to it. C sees that you're adding to an int * so it interprets 1 to mean 1 int (4 bytes). The * operator dereferences the address, which means it follows the pointer and gets the value stored at that address.

If printing arr gives you 0x1000, arr+1 gives you 0x1004 and * will return the int stored at 0x1004.

On to returning arrays in functions.

In C, you can't return an array like in C# (int[] function(...)), but you can return a pointer (int *function(...)), which you can use like an array (see above example).

The reason you want to use malloc is because malloc allocates space on the heap, so you can use that array after the function returns. If you defined an array like int arr[2];, the space would be allocated on the stack, and the array wouldn't persist after the function returns.

Perhaps you should look at a tutorial to learn more about pointers. I think my answer is too lengthy and not totally relevant to the question.

pushkin
  • 9,575
  • 15
  • 51
  • 95
  • it's saying "function returns address of local variable" This memory address stuff is confusing me. I've never had to mess with it before. – nathan rogers Oct 25 '15 at 03:47
  • Good catch. You should use `malloc` which allocates space on the heap. [This](http://stackoverflow.com/questions/12380758/c-error-function-returns-address-of-local-variable) might help. – pushkin Oct 25 '15 at 03:50
  • yes please. i am trying int * &funct(a,b){ //blah return &values} but now I'm getting lvalue required as left operand of assignment – nathan rogers Oct 25 '15 at 04:11
  • I'll elaborate on my response, but in the meantime get rid of the `&`. – pushkin Oct 25 '15 at 04:21
  • 2
    Don't cast malloc, that is a 30 year old anachronism – M.M Oct 25 '15 at 05:05
1

Simple solution (Passing by reference/pointer allows to change the value stored)

#include <stdio.h>

//alter values based on input
void doStuff(int *a, int *b, int option)
{
    switch(option)
    {
        case 1:
            // a decremented by 2, b is unchanged
            *a = *a - 2;
            break;

        case 2:
            // b decremented by 2, a is unchanged
            *b = *b - 2;
            break;

        default:
            // a and b are unchanged
            break;
    }
}

int main()
{
    int a = 20;
    int b = 20;
    int option;

    printf("Enter option (1 or 2):\n");
    scanf("%d", &option);

    printf("BEFORE doStuff() :: a = %d, b = %d\n", a, b);
    doStuff(&a, &b, option);
    printf("AFTER doStuff() :: a = %d, b = %d\n", a, b);

    return 0;
}
cm161
  • 492
  • 3
  • 6
1

You can wrap the array with a struct to return it directly.

#include <stdio.h>

struct doStuffRet {
   int arr[2];
};

//alter values based on input
struct doStuffRet doStuff(int a, int b){
   int option;
   struct doStuffRet r = {{a, b}};
   scanf("%d", &option);
   switch(option) {
      case 1:
         r.arr[0] = a-2;
         r.arr[1] = b;
         return r;
      break;
      case 2:
         r.arr[0] = a;
         r.arr[1] = b-2;
         return r;
      break;
      default:
         return r;
   }
}

int main(void){
   int a = 20;
   int b = 20;
   struct doStuffRet returnedObject;
   //what i need is like in c# how I can do this
   returnedObject = doStuff(a, b);
   a = returnedObject.arr[0];
   b = returnedObject.arr[1];
   printf("%d %d\n", a, b);
   return 0;
}
MikeCAT
  • 73,922
  • 11
  • 45
  • 70