4

Im new with c language and im having difficult while trying to change an array on my main function using another function. Besides seeing the solution I'd also like to get a full explanation what is wrong with my code and what's the explanation to your solution.

OK so I did too much tryouts and error experimentation but with no success to solve my problem. Eventually this is my current code:

#include <stdio.h>
#define MAX 20
typedef int Values[MAX];

int changeArr(Values *vals2) 
{
    *vals2[0] = 200;
    *vals2[1] = 100;
    printf("%d and ", *vals2[0]);
    printf("%d\n", *vals2[1]);
    return 0;
}   

int main (int argc, char *argv[]) 
{
    Values vals;
    changeArr(&vals);
    printf("%d and ", vals[0]);
    printf("%d\n", vals[1]);
    return 0;
}

Output is:

200 and 100
200 and 0

Instead of:

200 and 100
200 and 100

1 Answer 1

12

Version 1:

#include <stdio.h>
#define MAX 20
typedef int Values[MAX];

int changeArr(int vals2[]) {
    vals2[0] = 200;
    vals2[1] = 100;
    printf("%d and ", vals2[0]);
    printf("%d\n", vals2[1]);
    return 0;
}   

int main (int argc, char *argv[]) {

    Values vals;
    changeArr(vals);
    printf("%d and ", vals[0]);
    printf("%d\n", vals[1]);
    return 0;

}

Instead of passing a pointer to the array, pass a pointer to the array's first element.

Version 2:

#include <stdio.h>
#define MAX 20
typedef int Values[MAX];

int changeArr(Values *vals2) {
    (*vals2)[0] = 200;
    (*vals2)[1] = 100;
    printf("%d and ", (*vals2)[0]);
    printf("%d\n", (*vals2)[1]);
    return 0;
}   

int main (int argc, char *argv[]) {

    Values vals;
    changeArr(&vals);
    printf("%d and ", vals[0]);
    printf("%d\n", vals[1]);
    return 0;

}

use parentheses to compensate for the precedence.

The problem in your code is that

*vals2[1]

is *(vals2[1]), so it dereferences a pointer one unit of Values after the passed pointer. You have no accessible memory allocated there, so it's undefined behaviour. In practice, it is the same as accessing

arr[1][0]

for an

int arr[2][MAX];

But your vals is only (equivalent to) an int arr[1][MAX];, so you are accessing out of bounds. But if nothing worse happens, *vals2[1] = 100; in changeArr sets vals[MAX] in main to 100. It may overwrite something crucial, though.

In int changeArr(Values *vals2) you are passing a pointer to an array of MAX ints, resp the first such array in an array of Values. Then vals2[1] is the second array in that array of Values (which doesn't exist here), and *vals2[1] == vals2[1][0] the first int in that second array. You want to modify elements of the first (and only) array in the pointed-to memory block, so you want to access vals2[0][1], or equivalently (*vals2)[1].

A picture:

vals2                          vals2[1]
 |                               |
 v                               v
|vals[0]|vals[1]|...|vals[MAX-1]|x

in changeArr, the pointer vals2 points to the array vals. Since it's a pointer to an int[MAX], vals2+1 points to an int[MAX] at an offset of MAX*sizeof(int) bytes after the start of vals (which is just behind the end of vals). vals2[1], or equivalently *(vals2 + 1), is that array just after vals (which doesn't exist).

You want to change vals[1], which is located in the array vals2[0], at an offset of 1*sizeof(int) bytes, so you need vals2[0][1] or equivalently (*vals2)[1].

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

5 Comments

First thank you for ur detailed answer! i am trying to implement the main idea and by re-reading your explanation and looking at my entire code (which is doing much more logic), another question comes up: what if i am doing another use of a function in the changeArr() function, for example the function fscanf() - which writes a value from file to a certain index in the values array, how am i supposed to send that pointed array? fscanf(file, "%d", (*vals2)([0]) causes the error: warning: format argument is not a pointer..
In that case, when you want to fscanf into the i-th element of the array, you can take its address, fscanf("%d", &(*vals2)[i]); or you can avoid the dereferencing implied by [i] and pass fscanf("%d", (*vals2)+i). But I prefer taking the address, fscanf("%d", &object); uniformly, regardless of whether the object is an ordinary int variable or an array element.
Thanks Daniel! This is definitely the best explanation iv'e received over this website. As a student who's new with C i'm sure this post will help many others. Anyway, though u left me speechless I'll use this opportunity ask: (1) in theory, is there any difference from version 1 to version 2 codes? (2) is there any easy way to loop such values array until the last index that was assigned (in other words, lets say the array is 10 size and only 3 were assigned and i want to loop only these three elements) ?
(1) Yes, there is a difference. In version 1, the function receives an int*, it does not know how large the array that it points into is, and the type can't document how large an array is expected. In version 2, the function receives an int (*)[20], so it knows precisely how many elements *vals2 has - but it doesn't know a block of how many int[20] the argument vals2 itself points to. For what the function does, I think version 1 is more appropriate, though it would be better to pass also a size argument so that the function doesn't access elements that don't exist.
(2) Basically, you have to have a size argument/return/out parameter telling whoever receives it how many elements there are to be considered. In some cases, you can mark the end with a sentinel value (as is done for C strings with the 0-terminator, but in general, 0 is a valid entry in a char[], so you can't use that for other uses of character arrays), but a size parameter is more universally applicable.

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.