Computer Science Canada

Arrays and Mutability

Author:  apython1992 [ Mon Jul 04, 2011 3:52 pm ]
Post subject:  Arrays and Mutability

Hey guys, I'm back with another pointer question for my raytracer. I've encountered a situation where I declare a fixed array as a local variable in a function, and pass it into another function where I expect it to be modified. I've stepped through my code to make sure that the value actually *is* getting modified, but when control is returned to the original function, the value is the same. I feel like this may have something to do with a subtle difference between arrays and pointers, but I haven't found anything online to suggest that there is a problem with this. For reference, here is a dummy example of what I'm talking about:

c++:

int main()
{
    double my_array[3];
    some_function(my_array);
    // after function call, my_array hasn't changed as it should have in some_function
    return 0;
}

void some_function(double *array)
{
    // stuff that modifies array
}


While I *do* think that this is because I'm erroneously treating an array as a pointer, I would like someone who knows a thing or two about C or C++ to verify this/tell me otherwise. Thanks!

Author:  crossley7 [ Mon Jul 04, 2011 4:07 pm ]
Post subject:  RE:Arrays and Mutability

try

c++:

void some_function (double &array)
{

}


I believe you just have reference and dereference operators backwards. Check this page out to get straightened out. As it is I get confused by these regularly and avoid them if i can

http://www.cplusplus.com/doc/tutorial/pointers/

Author:  Insectoid [ Mon Jul 04, 2011 5:29 pm ]
Post subject:  RE:Arrays and Mutability

Yay pointers! I still get confused sometimes too. Remember fellas, there are 3 values associated with pointers! Their own location, the location they store and the data at the location they store.

Treating a pointer like a regular primitive type (ie no special symbols) returns the location the pointer is storing.

Any time you use an asterix, you're working with the data at the location being stored.

Any time you use an ampersand, you're working with the pointer's own location. Note this operator returns a pointer to self for all primitive types (and probably some/most/all complex types).

But you're making a raytracer, so I guess you're already aware of and practised in pointer use. I'm just writing this to prove to myself that I still know it. Also, how did I ever accomplish anything at all in Turing with no pointers (I know Turing supports pointers, but I never used them)?

Author:  apython1992 [ Mon Jul 04, 2011 5:39 pm ]
Post subject:  Re: RE:Arrays and Mutability

crossley7 @ Mon Jul 04, 2011 4:07 pm wrote:
try

c++:

void some_function (double &array)
{

}


I believe you just have reference and dereference operators backwards. Check this page out to get straightened out. As it is I get confused by these regularly and avoid them if i can

http://www.cplusplus.com/doc/tutorial/pointers/


Actually what I'm doing here with the asterisk isn't a dereference operator - this seems to be a common source of confusion amongst C/C++ programmers. Take a look at the following:
c++:
int num = 3;
int *p = #
int other_num = *p;


In this example, the first asterisk is used to declare a pointer to an int, whereas the second asterisk is used to dereference 'p" and obtain the value at the location it points to. Here, num = 3, p would be something like 0x2afc38, and other_num = 3.

All clear?

@Insectoid - this is my first time doing anything remotely serious in C++, so you can well imagine I've had my share of frustrations up to this point Smile Not just with pointers, but with the various uses of the const keyword mingled in with class inheritance...it's been fun to say the least.

Author:  Tony [ Mon Jul 04, 2011 5:52 pm ]
Post subject:  RE:Arrays and Mutability

It gets even more fun when you have const pointers. One of the interpretations might be "pointer to a constant address (can't change the value of the pointer), but data at that address is mutable". Yay C++ Laughing

Author:  Insectoid [ Mon Jul 04, 2011 6:22 pm ]
Post subject:  RE:Arrays and Mutability

Isn't that exactly what happens when you declare an array of characters in the following manner?
code:
char foo[5];
.

As far as I can tell, this is identical to the following:
code:
const char *foo = (char*) malloc (5*sizeof char)

Author:  apython1992 [ Mon Jul 04, 2011 6:44 pm ]
Post subject:  RE:Arrays and Mutability

Only difference is that foo in the second example would reside in memory allocated from the heap as opposed to the stack or global memory.

@Tony - I think I've seen examples of the const keyword placed after every other identifier/name in a class method:
c++:
const char* const blah(const *value) const;

Which kinda blows my mind. I see the value in it though in terms of code-safety - things will get caught at compile time instead of tossing you a deadly little segfault your way at runtime.

Back to my original post, does anyone have any ideas?

Author:  crossley7 [ Mon Jul 04, 2011 7:31 pm ]
Post subject:  RE:Arrays and Mutability

I guess I learn something new in c++ every time I open it. That is part of why I almost never use pointers. I have no idea how to use them.

My only experience with them is mostly from a few tutorials and SDL surface declarations. Wish I could actually understand how they work, think I've looked at and tried to learn pointers a bunch of times

Author:  Tony [ Mon Jul 04, 2011 7:38 pm ]
Post subject:  RE:Arrays and Mutability

@Insectoid -- not quite, there are some C++ internals magics (aren't there always?)
Quote:


tony$ cat test.cpp
#include <iostream>
using namespace std;
int main()
{
char foo1[5];
const char *foo2 = (char*) malloc (5*sizeof(char));
cout << sizeof(foo1) << endl;
cout << sizeof(foo2) << endl;
return 0;
}
tony$ g++ test.cpp
tony$ ./a.out
5
8


@apython1992 -- can you extend your example such that you actually try to modify the values?

Author:  apython1992 [ Mon Jul 04, 2011 8:06 pm ]
Post subject:  RE:Arrays and Mutability

It will probably be easier if I post the two functions in question in case there is something else I am overlooking. First, here is the first function I talked about (it does nowhere near what it is supposed to yet). It declares an array called "intersect" of size 3, and then it gets passed to the object.get_intersect() method. Basically, if there is no intersection point, I just set intersect to 0 (null pointer), but that doesn't appear to ever happen back in raytrace() (like I said, I've stepped through my code to see if it actually does get set to 0 in get_intersect()).

c++:

void raytrace(Ray *ray)
{
    double intersect[3];
    double *min_intersect;
    double curr_mag = 0;
    double min_mag = 0;

    // See if ray hits any objects, and get the closest intersection point
    for (unsigned int i = 0; i < objects.size(); i++)
    {
        objects[i]->get_intersect(ray, intersect);

        if (intersect == 0) continue;

        curr_mag = sqr_mag(intersect);

        if (i == 0)
        {
            min_mag = sqr_mag(intersect);   // Origin is (0, 0, 0) so we don't need vector subtraction here
            min_intersect = intersect;
        }
        else if (curr_mag < min_mag)
        {
            min_mag = curr_mag;
            min_intersect = intersect;
        }
    }

    if (min_intersect != 0)
    {
       cout << min_intersect[0] << ' ' << min_intersect[1] << ' ' << min_intersect[2] << endl;
    }
    else cout << "No intersect." << endl;
}


And the get_intersect() method (spheres):
c++:

void Sphere::get_intersect(const Ray *ray, double *intersect) const
{
    double ray_to_center[3];
    const int *ray_origin = ray->get_origin();
    double *ray_direction = ray->get_direction();

    // Obtain vector from ray origin to center of sphere
    for (int i = 0; i <= 2; i++)
        ray_to_center[i] = m_origin[i] - ray_origin[i];

    // Dot the ray with this vector to get the closest approach
    double tc = dot(ray_direction, ray_to_center);

    if (tc <= 0)
    {
        intersect = 0;
        return;             // Don't want backwards intersections
    }

    double center_to_ray[3];

    for (int i = 0; i <= 2; i++)
    {
        intersect[i] = tc * ray_direction[i] + ray_origin[i];
        center_to_ray[i] = intersect[i] - m_origin[i];
    }

    // Take magnitude of line drawn from calculated point to center
    // of sphere; if greater than radius, no intersect, if equal, return
    // calculated point, else carry out final calculation
    double mag = sqr_mag(center_to_ray);

    if (mag <= EPSILON)
    {
        tc -= m_radius;

        for (int i = 0; i <= 2; i++)
            intersect[i] = tc * ray_direction[i] + ray_origin[i];
    }
    else if (mag > m_radius)
    {
        intersect = 0;
    }
    else
    {
        // Use Pythagorean Theorem to calculate closest point based on right triangle
        double tf = tc - sqrt(m_radius - mag);

        for (int i = 0; i <= 2; i++)
            intersect[i] = tf * ray_direction[i] + ray_origin[i];
    }
}

Author:  DemonWasp [ Tue Jul 05, 2011 8:04 am ]
Post subject:  RE:Arrays and Mutability

I'm pretty sure your array is getting passed by value, which means copying. If you print out the value of "&intersect" just before the call to get_intersect() and just inside that function, you'll find that they're different (if I'm correct).

What you probably want is for your method signature to look like:

c++:

void Sphere::get_intersect(const Ray *ray, double ** intersect) const


Note the two stars -- it's a pointer to an array, so one star for pointer, one for array.

Then your method call looks like:
c++:

objects[i]->get_intersect(ray, &intersect);


Note the & operator being used to get the address of intersect so you can pass a pointer by value.

Now, I'm not a C++ expert and I haven't tried this right this moment, so I could be totally wrong. You should probably try this on your trivial example first. There may also be a "neater" way of doing it, but I wouldn't know.

Author:  apython1992 [ Tue Jul 05, 2011 8:45 am ]
Post subject:  Re: RE:Arrays and Mutability

DemonWasp @ Tue Jul 05, 2011 8:04 am wrote:
I'm pretty sure your array is getting passed by value, which means copying. If you print out the value of "&intersect" just before the call to get_intersect() and just inside that function, you'll find that they're different (if I'm correct).

Passing an array into a function will pass a pointer to its first element. I have verified that my array *is* getting passed by reference (a pointer to the first element of the array is passed, by value). I think I have found my problem, though: In get_intersect, if there is no intersection point I am trying to change the pointer's address in memory (pointing to NULL), which I believe is strictly forbidden with arrays, because they are essentially constant pointers. I'm finding myself getting thoroughly surprised by the behaviour of g++ - it sure isn't picking up a lot of these mis-uses (though to be fair, I'm only compiling using the default settings in Code::Blocks, so I should probably add some flags to help me catch some of this stuff).

All this being said - is there an easy way to get array-like behaviour on the stack without having to treat is as a const pointer? If not, I can find another mechanism for handling non-intersections, but I may as well learn something. Smile

Appreciate the help guys.

Author:  apython1992 [ Tue Jul 05, 2011 9:05 am ]
Post subject:  Re: RE:Arrays and Mutability

Insectoid @ Mon Jul 04, 2011 6:22 pm wrote:
Isn't that exactly what happens when you declare an array of characters in the following manner?
code:
char foo[5];
.

As far as I can tell, this is identical to the following:
code:
const char *foo = (char*) malloc (5*sizeof char)


Also I think this is a pointer which points to values that are const. Instead, we'd want a constant pointer with mutable values. It would have to be
code:
char * const foo = (char *) malloc(5 * sizeof(char))


I think that syntactically it should be the other way around. Makes more sense to me.

Author:  DemonWasp [ Tue Jul 05, 2011 9:37 am ]
Post subject:  RE:Arrays and Mutability

Part of the value of stack allocation is the assurance that the value is non-nullable (see RAII). You should probably just return a boolean indicating intersection or non-intersection.

Author:  apython1992 [ Tue Jul 05, 2011 9:43 am ]
Post subject:  RE:Arrays and Mutability

I do need the actual point of intersection though (because it will be the origin for subsequent rays). I kept everything stack-based and just changed it to set the z co-ordinate of the intersection to -1 if it misses (this would ever happen with the way my raytracer is set up. A bit of a hackish solution, but it'll work).

Author:  Tony [ Tue Jul 05, 2011 10:58 am ]
Post subject:  Re: RE:Arrays and Mutability

apython1992 @ Tue Jul 05, 2011 8:45 am wrote:
I am trying to change the pointer's address in memory (pointing to NULL), which I believe is strictly forbidden with arrays, because they are essentially constant pointers. I'm finding myself getting thoroughly surprised by the behaviour of g++ - it sure isn't picking up a lot of these mis-uses

That's the problem. Not that it's forbidden, but you are changing the value of the local copy of the pointer (pointer's address is passed by-value; you are changing that value). For the changes to persist, you need to change only the data being pointed to (this is why we have double **pointers Wink )

g++ will let you do all kinds of undefined behaviours because... heck, you might actually know better (about the specifics of your target hardware, OS, or any combination there-of). Interesting read -- The Story of Mel, a Real Programmer http://www.outpost9.com/reference/jargon/jargon_49.html

Author:  apython1992 [ Tue Jul 05, 2011 11:34 am ]
Post subject:  RE:Arrays and Mutability

All of a sudden C++ feels way too nice Smile

Thanks Tony, I've resolved the problem.

Author:  Brightguy [ Tue Jul 05, 2011 3:11 pm ]
Post subject:  Re: RE:Arrays and Mutability

Insectoid @ Mon Jul 04, 2011 6:22 pm wrote:
Isn't that exactly what happens when you declare an array of characters in the following manner?
code:
char foo[5];

There's no pointer there. In many contexts foo can be used as if it were a pointer, but it's not: foo is of type "array of 5 chars".

Author:  Insectoid [ Tue Jul 05, 2011 3:49 pm ]
Post subject:  Re: RE:Arrays and Mutability

Brightguy @ Tue Jul 05, 2011 3:11 pm wrote:
Insectoid @ Mon Jul 04, 2011 6:22 pm wrote:
Isn't that exactly what happens when you declare an array of characters in the following manner?
code:
char foo[5];

There's no pointer there. In many contexts foo can be used as if it were a pointer, but it's not: foo is of type "array of 5 chars".


As I understand, foo itself is just a constant pointer to the first element of the array, and enough RAM is allocated at that location in the stack to contain the entire array. The rest is just syntactical sugar. This is how it has always been explained to me. Now, when the compiler sees 'char foo[]', does it actually see a unique array type, or does it see some sort of internal function that takes a type and a size and returns a pointer to that RAM?

Now, some out-loud thought. I'm not sure exactly how a pointer works. As far as I know, it's basically a 4-byte (larger if you're running a 64 bit system I think) integer that just contains an address. However, if you add to a pointer (say, *int +2) the compiler knows to add 2*sizeof int to the pointer, and go to that location. Where is the metadata stored, that says it is a pointer to integer? Is this done at compile time, or runtime? I suspect it is compile-time, as that would reduce RAM usage of the final product, and explains why I can't subsequently point from an integer to a character. If this is the case, then an array and a pointer are identical, except that an array is allocated on the stack and a pointer (with malloc) allocates to the heap. Now, if there is a command similar to malloc that allocates to the stack, then there is no need for array syntax beyond convenience, as there is no actual array type (because it's just a pointer and allocated RAM).

Now bear in mind, this is all just assumptions based on what I've been told, and what I've guessed. I may be way off the mark here, so correct me if I'm wrong.

Author:  Brightguy [ Tue Jul 05, 2011 4:30 pm ]
Post subject:  Re: Arrays and Mutability

The array foo is not a pointer. That said, in most cases where 'foo' appears in your code, a pointer is immediately constructed which points to the memory location of the array's first element. Essentally, 'foo' acts as if you wrote '&foo'. Tony gives an example where this does not happen, namely in sizeof(foo).

Author:  Velocity [ Tue Dec 13, 2011 10:59 am ]
Post subject:  RE:Arrays and Mutability

but if you had

char [foo];
{
array(multi(6^2))
multi(foo)
{
array(foo(multi) * 2 ^ 2
}
}
return 0;


: