0

In the following code, when the line doit(x,y) is executed, what is passed to the pointer? The addresses of x and y or the values of x and y?

#include <stdio.h>

int doit(int x[], int y[]) {
   x = y;
   x[0] = 5;
   y[2] = 10;
}

int main(void) {
   int x[2];
   int y[2];

   x[0] = 1;
   x[1] = 2;
   y[0] = 3;
   y[1] = 4;

   doit(x, y);
   printf("%d %d %d %d", x[0], x[1], y[0], y[1]);
}
5
  • 1
    I'm noticing you posting a lot of basic questions about how C works. I don't mean to insult you, I just want to help you learn. What tutorial/book are you using to learn C? Commented Sep 26, 2009 at 21:02
  • 1
    Im sorry for starting many posts, Chris. I really want to learn how to program, but my professor is teaching the class as if everyone had years of programming experience. I'm taking intro to C programming, but on the first day of his lecture, he started talking about activation records and how compiler manages memory etc etc I'm using the textbook C Programming by K. N King but it's not much help with undefined behaviors... Commented Sep 26, 2009 at 21:06
  • Don't apologize - everyone has to learn somewhere. The stereotypical book to learn C from is K&R, written by the creators of C. I never read it personally - I learned from Google searches - but it comes highly recommended by many C programmers, and is definitely still useful. Commented Sep 26, 2009 at 21:09
  • 3
    You have a bug in the code: y[2] = 10; Y is defined as array 0..1. So that line write into x's memory. Commented Sep 26, 2009 at 21:39
  • Thank you, Chris! Im gonna try to find a copy in the bookstore! Commented Sep 26, 2009 at 22:09

4 Answers 4

5

First, as a small matter of convenience, you may find it easier to initialize the arrays like this:

int x[2] = { 1, 2 };
int y[2] = { 3, 4 };

As a note, this is only legal in initialization. We can't do this:

int x[2];
x = { 1, 2 };

This is an array initialization. We're not "assigning" to an array because an array isn't an lvalue, but we can still initialize it. Note that any values we don't fill in will be initialized to zero:

int lots[100] = { 1 };

Now lots is an array of 100 elements, lots[0] is 1, and lots[1] through lots[99] are all 0. We guarantee the values because we initialized the array. If we just did:

int lots[100];

Then we've declared, but not initialized, our array, so it holds garbage like any other local variable. A common idiom is:

int lots[100] = { 0 };

To initialize our array to all zeroes - the one value we declare is 0, and the other ones are zeroed out automatically.

Second, to address your actual concern, let's look at doit():

int doit(int x[], int y[]) {

This is pretty basic - it declares a function that takes two arrays. But you can't pass arrays to a function in C, or return them from a function in C, so what does this really mean?

int doit(int *x, int *y) {

In C, all an "array" is (when passed to a function) is a pointer to the first element of that array. That's why arrays are zero indexed. To access the first element of an array, we dereference the pointer. To access the second element, we add sizeof(array type) to the pointer, and dereference that:

x[0] == *x
x[1] == *(x + 1)
// and so on

A commonly noted product of this is the following:

x[2] == *(x + 2) == *(2 + x) == 2[x]

While not commonly used, it is still pretty cool. But don't use it in real code. That's decidedly not cool.

Anyway, so the addresses of our arrays (well, pointers to the first elements of our arrays) are passed to our doit() function. What happens next:

    x = y;

This is saying "Tell our local x pointer to point to y. It doesn't change the original x array, because we're assigning to the pointer, not to the array (which we can't do). So we end up basically with two names, x and y, for the same array. Any changes to x or y will be reflected in the array passed into y.

    x[0] = 5;
    y[2] = 10;

This is pretty basic. We set the first element of our array to 5 and the third element to 10. No problems here, assuming the array passed in for y has at least three elements (cue ominous music).

}

Actually, this is one of the bigger problems in doit(). How did we declare doit()?

int doit(int x[], int y[])

So doit() returns an int, but we have no return statement! While some compilers may accept this in varying degrees, it's much better to either return a value, or change the return type of doit() to void (which I suspect is what you want) so that it doesn't return anything. If your function returns void, you can omit the return statement at the end, or you could explicitly say return; with no arguments, since you're returning nothing. (Don't worry, this monster is almost finished.)

The biggest problem here, as you well know, is that we can't guarantee that the arrays we pass our function will have three elements. We can't even guarantee they'll be arrays. We can call it like this:

int i = 10;
doit(&i, &i);

And it's legal. The C language will not check to make sure the arrays you pass a function are big enough, nor will it give you any built-in facilities for checking the size of the array. This doesn't work:

size_t array_size(int a[]) {
    return sizeof(a) / sizeof(a[0]);
}

It won't work because it's rewritten at the compiler level as this:

size_t array_size(int *a) {
    return sizeof(a) / sizeof(a[0]);
}

And sizeof(int *) is not the size of the array, but the size of the pointer to the array (and the pointer is just a numeric address in memory, usually a computer word). So while it's legal to modify y[2] in your doit() function, the array you passed in main() doesn't have enough elements, so we get undefined behavior. The compiler can do anything - it can overwrite x, or overwrite some other local variable, or overwrite your code data, or do anything it wants to (the classic example is causing demons to fly out of your nose) and still be a valid C compiler. That's the danger of C - it won't check to make sure you play by the rules. The reason for this is that it's meant to be a fast language, capable of producing highly efficient code, and checking whether or not you play by the rules makes your program slower. So if you want to make sure you play by the rules, you have to check the rules yourself:

void doit(int y[], size_t len) {
    y[0] = 5;
    if(len > 2)
        y[2] = 10;
    else
        y[len] = 10;
}

Then call it:

#define SIZE 2
int y[SIZE] = { 3, 4 };
doit(y, SIZE);

Now we can call doit() safely, even on small arrays. This pattern of passing an array length as a separate argument to a function is very common in C, and is found in most C standard library functions dealing with arrays and pointers.

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

5 Comments

Jesus, that is a massive post. Now I need a nap.
-1 "In C, all an "array" is is a pointer to the first element of that array." No they are not. An array is an array, a pointer is a pointer. I'll come back with a very good example by litb.
When you pass an array to a function, that's all an array is. But you're right, I should have clarified that.
isn't the only difference between the two how they are initialized? When you work with an array with the array syntax it all translates to pointer arithmetic.
@Carson - Arrays are also constant, and not lvalues. values = func() is valid for pointers, but not arrays.
2

Adress of the first member of the array.

We can check this using a debugger, for example, gdb.

Starting program: /tmp/doit 

Breakpoint 2, main () at doit.c:11
11  int x[]={1,2,4,5};
(gdb) n
12  int y[]={11,12,14,15};
(gdb) print x
$1 = {1, 2, 4, 5}
(gdb) print y
**$2 = {134520820, -1076989448, 134513312, -1207230476}**
(gdb) print &x[0]   <--- PRINT ADDRESS OF FIRST ELEMENT OF X.***
***$3 = (int *) 0xbfce71f4
(gdb) print &y[0]   <--- PRINT ADDRESS OF FIRST ELEMENT OF Y.***
$4 = (int *) 0xbfce71e4
(gdb) n
14  doit(x,y);
(gdb) step
//FUNCTION CALL IS DISPLAYED HERE. 
Breakpoint 1, ***doit (x=0xbfce71f4, y=0xbfce71e4)*** at doit.c:7
7 }
(gdb) 

Values of x and y as passed to doit are shown here:

Breakpoint 1, ***doit (x=0xbfce71f4, y=0xbfce71e4)*** at doit.c:7

x is 0xbfce71f4. Thats the address of the first element of the array X.

y is 0xbfce71e4. Thats the address of the first element of the array Y.

Additionally, think about this.

arrays can't be assigned in C. What I mean, is that x=y will yield a compilation error. (something like : error: incompatible types in assignment). Consequently, if each parameter received an array, instead of the address of its first element, your code wouldn't compile.

Comments

2

When arrays are passed as arguments, they always 'degrade' to simple pointers. So the prototype:

int doit(int x[], int y[]) ;

is equivalent to

int doit( int* x, int* y ) ;

I prefer the second as it is clear what is really happening, the first promises something that cannot be delivered. It serves perhaps as an indication that what is expected is a pointer to an array rather a pointer to a single object, but it has no effect on actual code generation. Often such functions have additional arguments to specify the size of teh array being passed.

The difference between an array and a pointer is simply that an array contains size information. So for example in the following:

void fn( int x1[], int* x2 )
{
    printf( "sizeof(x1) = %u\n", sizeof( x1 ) ) ;
    printf( "sizeof(x2) = %u\n", sizeof( x2 ) ) ;
}

int main()
{
    int x[10] ;
    int* y = x ;
    printf( "sizeof(x) = %u\n", sizeof( x ) ) ;
    fn( x, y ) ;
}

Will output (on 32bit platform):

sizeof(x) = 40
sizeof(x1) = 4
sizeof(x2) = 4

yet x, x1 and x2 all refer to the same array.

2 Comments

Don't you mean 'sizeof(x) = 40'? (And 'paltform' could be fixed too).
While we're at it, why are you declaring fn() to take two arguments, but only passing it one?
-4

The addresses of arrays are passed to the doit() function, but what does "passed to the pointer" mean?

2 Comments

Considering how much more reputation points you have, I feel silly explaining habits of this website, but I think these requests for clarification are expected to be expressed as comments on the question, not as answers (and you definitely have the minimum reputation for commenting).
Pascal, did you not notice that there was an answer preceding the question?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.