1

I am beginner in C++ programming. I wrote a simple program. I defined a function named "myFunction" and it gets an array and its size as input argument. Inside the function the first element of array changes to 100. However, in the main function the value of the first elements of the array changed. Why is that? I tought it must not change. I know that in the similar case for an int variable it does not happen.

#include <iostream>

using namespace std;

void myFunction(int a[],int b)
{
    a[0] = 100;
}

void myFunction2 (int a)
{
    a = a+2;
}

main()
{
    int size_of_array = 5;
    int myArray[size_of_array] = {7,8,2,3,5};
    int c = 2;
    myFunction(myArray,5);
    myFunction2(c);
    cout << myArray[0]<< endl;
    cout << c;
}

I expected that the first elements of "myArray" should be 7 after I called "myFunction"

Chris
  • 26,361
  • 5
  • 21
  • 42
Omid Abasi
  • 23
  • 4
  • 1
    To avoid confusion, I'd recommend writing `void myFunction(int *a,int b)`. It means _exactly_ the same thing, which is the confusing part you don't want to hide. – hyde Mar 22 '23 at 06:42
  • Why have you tagged C++ with the tag C? – 273K Mar 22 '23 at 06:45
  • You are correct, and your confusion is reasonable. This is one reason (among many) not to use arrays in C++, their behaviour is not consistent with other types. Instead you should prefer a vector i.e. `std::vector`. If you used that you would find that the values of the vector do not change in `main`. – john Mar 22 '23 at 07:05
  • Where are you learning C++ from? With `using namespace std;` and "C" style arrays it looks like your material might be outdated. As John said, use std::array, or std::vector to pass arrays between functions. (With "C" style arrays you can loose ALL size information and easily access arrays out of bounds). – Pepijn Kramer Mar 22 '23 at 07:09
  • Thanks for your reply. I edited taggs. Yes, it is an outdated course in my mother tongue. Would you please let me know a book or a course to learn C++ which is not outdate? – Omid Abasi Mar 22 '23 at 07:26
  • @OmidAbasi Maybe you can find something useful [here](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/388282) – john Mar 22 '23 at 21:09

2 Answers2

3

In C++ arrays don't get passed around as values, per se. Instead your array is allocated in main as a series of five sequential (probably) 32-bit int values.

Your myFunction function gets a pointer to the beginning of that chunk of memory, rather than a copy of the entire array. a[0] = 100; is functionally identical to *(a + 0) = 100;. You are dereferencing the pointer and assigning to it. This changes the value in memory at that address. This change reflects in main after you've called myFunction because that memory address never changed.

The int value you passed to myFunction2 was copied. Changes made to a in that function have no effect on c in main.

It's worth noting that variable length arrays like you've shown are not part of the C++ standard. While some compilers may support them, it's best not to count on that support.

It would be sufficient to write the following and let the compiler figure out the size.

    int myArray[] = {7,8,2,3,5};

Now, if we leave the realm of raw arrays and start using a container like std::vector, we can pass by either value or reference, and if we pass by value, you do get a copy. Consider:

#include <vector>
#include <iostream>

void foo(std::vector<int> v) {
    v[0] = 100;
}

void bar(std::vector<int>& v) {
    v[0] = 100;
}

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};

    for (auto x : v) std::cout << x << std::endl;

    foo(v);

    for (auto x : v) std::cout << x << std::endl;

    bar(v);

    for (auto x : v) std::cout << x << std::endl;

    return 0;
}

Output:

1
2
3
4
5
1
2
3
4
5
100
2
3
4
5

After calling foo there is no change to v in main. But because v is passed to bar by reference, assigning to its first element does reflect in main when we print the array.

Chris
  • 26,361
  • 5
  • 21
  • 42
2

This is one of the bad features that C++ has inherited from C: Raw arrays cannot be passed as a copy to a function, although there is syntax that looks like it does.

void f(int v[]);
void f(int v[2]); // number is ignored here

are in C and C++ just another notation for

void f(int* v);

This is one of the reasons why std::array was introduced. This can be used like a raw array, but provides the normal rules when used as a function argument.

#include <iostream>
#include <array>

// copy argument is not possible with raw array
void call_by_value(std::array<int, 2> array) {
    array[0] = 5;
    std::cout << "call_by_value: " << array[0] << ", " << array[1] << '\n';
}

// return is not possible with raw array
std::array<int, 2> return_array() {
    return {8, 7};
}

int main() {
    std::array<int, 2> array = {1, 2};

    std::cout << "main array: " << array[0] << ", " << array[1] << '\n';

    call_by_value(array);

    std::cout << "main array: " << array[0] << ", " << array[1] << '\n';

    array = return_array(); // assign array is not possible with raw arrays

    std::cout << "main array: " << array[0] << ", " << array[1] << '\n';
}
main array: 1, 2
call_by_value: 5, 2
main array: 1, 2
main array: 8, 7

To make the confusion complete, it is very possible to pass pointers or references to raw arrays as function arguments. However, the syntax is not very nice and especially the pointer variant can easily be used incorrectly.

#include <iostream>

void call_by_reference(int(&array)[2]) {
    array[0] = 5;
    std::cout << "call_by_reference: " << array[0] << ", " << array[1] << '\n';
}

void call_by_pointer(int(*array)[2]) {
    (*array)[1] = 7;
    std::cout << "call_by_pointer: " << (*array)[0] << ", " << (*array)[1] << '\n';
}

int main() {
    int array[2] = {1, 2};

    std::cout << "main array: " << array[0] << ", " << array[1] << '\n';

    call_by_reference(array);

    std::cout << "main array: " << array[0] << ", " << array[1] << '\n';

    call_by_pointer(&array);

    std::cout << "main array: " << array[0] << ", " << array[1] << '\n';
}
main array: 1, 2
call_by_reference: 5, 2
main array: 5, 2
call_by_pointer: 5, 7
main array: 5, 7

In call_by_pointer the brackets in (*array)[1] are especially important for access!

#include <iostream>
#include <array>

void call_by_pointer(int(*array)[2]) {
    std::cout << "call_by_pointer: " << *array[0] << ", " << *array[1] << '\n';
}

int main() {
    int array[2] = {1, 2};
    call_by_pointer(&array);
}
call_by_pointer: 1, -18532096

This program compiles without problems! But the element access is wrong. The access operator [x] has priority over the dereference operator *. Therefore, according to the pointer arithmetic for accessing element one in memory, the address is advanced by the size of an int array with two elements and this is then dereferenced. But this is already outside the array, so memory garbage is output.

Again, std::array improves the situation.

#include <iostream>
#include <array>

void call_by_reference(std::array<int, 2>& array) {
    array[0] = 5;
    std::cout << "call_by_reference: " << array[0] << ", " << array[1] << '\n';
}

void call_by_pointer(std::array<int, 2>* array) {
    (*array)[1] = 7;
    std::cout << "call_by_pointer: " << (*array)[0] << ", " << (*array)[1] << '\n';
}

int main() {
    std::array<int, 2> array = {1, 2};

    std::cout << "main array: " << array[0] << ", " << array[1] << '\n';

    call_by_reference(array);

    std::cout << "main array: " << array[0] << ", " << array[1] << '\n';

    call_by_pointer(&array);

    std::cout << "main array: " << array[0] << ", " << array[1] << '\n';
}
main array: 1, 2
call_by_reference: 5, 2
main array: 5, 2
call_by_pointer: 5, 7
main array: 5, 7

The parameter syntax is easy to read and forgetting the parentheses in the pointer variant will result in a compilation error.

Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51