0

Sanity-check questions:

I did a bit of googling and discovered the correct way to return a one-dimensional integer array in C is

int * function(args);
  1. If I did this, the function would return a pointer, right? And if the return value is r, I could find the nth element of the array by typing r[n]?

  2. If I had the function return the number "3", would that be interpreted as a pointer to the address "3?"

  3. Say my function was something like

    int * function(int * a);
    

    Would this be a legal function body?

    int * b;
    b = a;
    return b;
    

    Are we allowed to just assign arrays to other arrays like that?

  4. If pointers and arrays are actually the same thing, can I just declare a pointer without specifying the size of the array? I feel like

    int a[10];
    

    conveys more information than

    int * a;
    

    but aren't they both ways of declaring an array? If I use the latter declaration, can I assign values to a[10000000]?

Main question:

  1. How can I return a two-dimensional array in C? I don't think I could just return a pointer to the start of the array, because I don't know what dimensions the array has.

Thanks for all your help!

i love stackoverflow
  • 1,555
  • 3
  • 12
  • 24
  • 5
    Quite frankly, I think you need to buy a book. I recommend Kernighan and Ritchie. Read the chapter on pointers. – user93353 Nov 11 '12 at 04:32
  • Yes, and read section 6 of the [comp.lang.c FAQ](http://www.c-faq.com/). – Keith Thompson Nov 11 '12 at 04:34
  • 1
    To point 4. Pointers are not arrays, arrays are not pointers - http://stackoverflow.com/questions/12676402/why-cant-i-treat-an-array-like-a-pointer-in-c – Mike Nov 11 '12 at 05:43
  • 1
    The best way is to write a short program in your code::block or just use gdb to watch the memory value. You can get a better understanding of this with your own experiment. – lichenbo Nov 11 '12 at 07:20

3 Answers3

3
  1. Yes
  2. Yes but it would require a cast: return (int *)3;
  3. Yes but you are not assigning an array to another array, you are assigning a pointer to a pointer.
  4. Pointers and arrays are not the same thing. int a[10] reserves space for ten ints. int *a is an uninitialized variable pointing to who knows what. Accessing a[10000000] will most likely crash your program as you are trying to access memory you don't have access to or doesn't exist.
  5. To return a 2d array return a pointer-to-pointer: int ** f() {}
001
  • 13,291
  • 5
  • 35
  • 66
  • Thanks! Wait, is int a[10] also an uninitialized variable? The elements don't seem to be initialized, but that's weird because if a[10] was uninitialized, how would you know what address to allocate the 10 units of memory at? – i love stackoverflow Nov 11 '12 at 04:51
  • Also, for the two dimensional array, if you're returning a pointer to pointers, does that mean the array is not stored as one continuous block of memory? – i love stackoverflow Nov 11 '12 at 04:51
  • int a[10] is not an uninitialized variable. However, that data in the array is uninitialized (a[0], a[1], a[2]...) – 001 Nov 11 '12 at 04:55
  • Also, for 2d returns, see this: http://stackoverflow.com/questions/720707/how-to-return-two-dimensional-char-array-c – 001 Nov 11 '12 at 04:57
  • Be careful with `int **` is returning a 2D array. There's a difference between a 2D array and an array of pointers to arrays of integers. If you need to return a pointer to a 2D array with dimensions not known in the calling code, then you've got a lot of work to do. ([SO 720707](http://stackoverflow.com/questions/720707) is of limited help; it is for C++ and the solution uses `new` instead of `malloc`. It does show how to create an array of pointers to arrays of `int`, though, if you translate the `new` into `malloc`.) – Jonathan Leffler Nov 11 '12 at 05:26
2
  1. Yes; array indexing is done in terms of pointer arithmetic: a[i] is defined as *(a + i); we find the address of the i'th element after a and dereference the result. So a could be declared as either a pointer or an array.

  2. It would be interpreted as an address, yes (most likely an invalid address). You would need to cast the literal 3 as a pointer, because values of type int and int * are not compatible.

  3. Yes, it would be legal. Pointless, but legal.

  4. Pointers and arrays are not the same thing; in most circumstances, an expression of array type will be converted ("decay") to an expression of pointer type and its value will be the address of the first element of the array. Declaring a pointer by itself is not sufficient, because unless you initialize it to point to a block of memory (either the result of a malloc call or another array) its value will be indeterminate, and may not point to valid memory.

  5. You really don't want to return arrays; remember that an array expression is converted to a pointer expression, so you're returning the address of the first element. However, when the function exits, that array no longer exists and the pointer value is no longer valid. It's better to pass the array you want to modify as an argument to the function, such as

    void foo (int *a, size_t asize) { size_t i; for (i = 0; i < asize; i++) a[i] = some_value(); }

Pointers contain no metadata about the number of elements they point to, so you must pass that as a separate parameter.

For a 2D array, you'd do something like

void foo(size_t rows, size_t columns, int (*a)[columns])
{
   size_t i, j;
   for (i = 0; i < rows; i++)
     for (j = 0; j < columns; j++)
        a[i][j] = some_value;
}

This assumes you're using a C99 compiler or a C2011 compiler that supports variable length arrays; otherwise the number of columns must be a constant expression (i.e., known at compile time).

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

These answers certainly call for a bit more depth. The better you understand pointers, the less bad code you will write.

An array and a pointer are not the same, EXCEPT when they are. Off the top of my head:

int a[2][2] = { 1, 2, 3, 4 }; int (* p)[2] = a; ASSERT (p[1][1] == a[1][1]);

Array "a" functions exactly the same way as pointer "p." And the compiler knows just as much from each, specifically an address, and how to calculate indexed addresses. But note that array a can't take on new values at run time, whereas p can. So the "pointer" aspect of a is gone by the time the program runs, and only the array is left. Conversely, p itself is only a pointer, it can point to anything or nothing at run time.

Note that the syntax for the pointer declaration is complicated. (That is why I came to stackoverflow in the first place today.) But the need is simple. You need to tell the compiler how to calculate addresses for elements past the first column. (I'm using "column" for the rightmost index.) In this case, we might assume it needs to increment the address ((2*1) + 1) to index [1][1].

However, there are a couple of more things the compiler knows (hopefully), that you might not.

The compiler knows two things: 1) whether the elements are stored sequentially in memory, and 2) whether there really are additional arrays of pointers, or just one pointer/address to the start of the array.

In general, a compile time array is stored sequentially, regardless of dimension(s), with no extra pointers. But to be sure, check the compiler documentation. Thus if the compiler allows you to index a[0][2] it is actually a[1][0], etc. A run time array is however you make it. You can make one dimensional arrays of whatever length you choose, and put their addresses into other arrays, also of whatever length you choose.

And, of course, one reason to muck with any of these is because you are choosing from using run time multiplies, or shifts, or pointer dereferences to index the array. If pointer dereferences are the cheapest, you might need to make arrays of pointers so there is no need to do arithmetic to calculate row addresses. One downside is it requires memory to store the addtional pointers. And note that if the column length is a power of two, the address can be calculated with a shift instead of a multiply. So this might be a good reason to pad the length up--and the compiler could, at least theoretically, do this without telling you! And it might depend on whether you select optimization for speed or space.

Any architecture that is described as "modern" and "powerful" probably does multiplies as fast as dereferences, and these issues go away completely--except for whether your code is correct.

Mike Layton
  • 93
  • 1
  • 6