Page 1 of 1

Pointer modified when passing it as an argument?

Posted: Thu Aug 17, 2006 1:29 pm
by Candamir
I have a pointer: struct regs *r and pass this pointer as an argument to a function. In this function, the value of r is modified, but when the function returns, the original value of r is still the same. Is this a property of the compiler or a bug in my code?

Candamir

Re:Pointer modified when passing it as an argument?

Posted: Thu Aug 17, 2006 1:39 pm
by Kemp
Any changes to arguments are done on a copy of that variable that is made on the stack (optimisations nonewithstanding). Changes done to the thing pointed to are done on the actual thing pointed to as the value of the pointer is not changed.

Re:Pointer modified when passing it as an argument?

Posted: Thu Aug 17, 2006 3:08 pm
by Pype.Clicker
in other words, what you pass to a function through an argument in C is _always a value_. even when you have pointers around, you always give the _value_ of the pointer, never the variable itself.

e.g. you have

Code: Select all

void print_one_char(char* str) {
    putc(*str); // print first char
    str++;  // advance pointer to next char
}
and you do

Code: Select all

    char* msg="hello world";
    print_one_char(msg); print_one_char(msg); print_one_char(msg);

    print_one_char(msg+6); print_one_char("world");
all are correct invocation, and the resulting text will be "hhhww". note the construct "(msg+6)" and "("world")" where the value is compiled right before you call the function. How could print_one_char know it now no longer operate from a variable but rather on a computed expression ?

the answer is it doesn't need to care. print_one_char operates on its argument which is placed in a memory location that isn't used for anything else.

If that still seems weird to you, just grab the K&R and a book explaining the stack in assembly, then get a look at how a simple example is compiled into assembly by GCC...

Re:Pointer modified when passing it as an argument?

Posted: Thu Aug 17, 2006 3:51 pm
by Candamir
Hmm, it seems pretty the same as Java: Primitives in method arguments are modified without changing the original and objects are modified...

(Well, it's more like the other way round: Java is like C ;))

Thanks,

Candamir

Re:Pointer modified when passing it as an argument?

Posted: Fri Aug 18, 2006 2:13 am
by Pype.Clicker
"passing arguments by value" and "passing arguments by variable" are there since the first high-level languages, afaik.

Re:Pointer modified when passing it as an argument?

Posted: Fri Aug 18, 2006 3:10 am
by distantvoices
That's "passing arguments by reference", ain't it?

Re:Pointer modified when passing it as an argument?

Posted: Fri Aug 18, 2006 8:19 am
by Habbit
Well, if you need to modify the pointer itself (i.e. make it point to another variable) instead of the pointee (the struct), you need to pass a double pointer:

Code: Select all

void alterPointer(char** ptr)
{
    *ptr = "Another one";
}

int main(void)
{
    char* myString = "A string";
    cout << myString << endl;  // Prints 'A string'
    alterPointer(&myString);   // Notice the double indirection: &(char*) = char**
    cout << myString << endl;  // Prints 'Another one'
    return 0;
}
This could be used to implement, for example, PHP-like iteration functions (na?vely and w/o bound checks in this snippet):

Code: Select all

int next(int** ptrToIntArray)
{
    *ptrToIntArray++; // Move the array pointer to the next int
    return **ptrToIntArray; // Return the now-current element
}

void otherFunc(void)
{
    int* arr = new int[5]
    // Fill the array
    int item = next(&arr); // 1st item
    item = next(&arr); // 2ns item
    return;
}

Re:Pointer modified when passing it as an argument?

Posted: Fri Aug 18, 2006 9:22 am
by Candy
If you use a c++ compiler you can also pass a reference to the pointer:

Code: Select all

void modify(char *&x) {
   x = "hello world";
}

int main(...) {
   char *t = NULL;
   modify(t);
   printf("%s", t);
}

Re:Pointer modified when passing it as an argument?

Posted: Fri Aug 18, 2006 11:52 am
by proxy

Code: Select all

int next(int** ptrToIntArray)
{
    *ptrToIntArray++; // Move the array pointer to the next int
    return **ptrToIntArray; // Return the now-current element
}

this example is BROKEN, *p++ does not increment what p points to. ++ has a higher precidence than (yes both versions do) just postfix ++ returns the original value, not the new one.

thus p++ is functionally the same as "int t = p; ++p; return t;", just any current compiler will know to optimize this by reorderoing such that the temp is unneccessary in most cases.

what you want is:

Code: Select all

int next(int** ptrToIntArray)
{
    (*ptrToIntArray)++; // Move the array pointer to the next int
    return **ptrToIntArray; // Return the now-current element
}
or better yet:

Code: Select all

int next(int** ptrToIntArray)
{
    ++(*ptrToIntArray); // Move the array pointer to the next int
    return **ptrToIntArray; // Return the now-current element
}
Finally of course this is avoided entirely by using c++ and references!

Code: Select all

int next(int* &ptrToIntArray)
{
    ++ptrToIntArray; // Move the array pointer to the next int
    return *ptrToIntArray; // Return the now-current element
}
which can be improved to make it even shorter..

Code: Select all

int next(int*&ptrToIntArray)
{

    return *++ptrToIntArray; // Move the array pointer to the next int and Return the now-current element
}
proxy

Re:Pointer modified when passing it as an argument?

Posted: Fri Aug 18, 2006 7:56 pm
by Candamir
Well, thanks for your extensive answers. This was indeed one of the problems in my scheduler. However, I think I also have the same problem with my ASM code, but due to the nature of the stack in ASM I think maybe there the situation might be different...

This is my code:

Code: Select all

irq_common_stub:
    pushad
    push ds
    push es
    push fs
    push gs

    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov eax, esp

    push eax
    mov eax, irq_handler
    call eax
    pop eax

    pop gs
    pop fs
    pop es
    pop ds
    popad
    add esp, 8
    iret

Code: Select all

void irq_handler(struct regs *r)
{
   r = (struct regs *)something;
   ...
I already tried it out, but didn't quite understand the codes behaviour... IIUC, pop eax should then restore esp to 'something', shouldn't it?

Candamir

PS: Maybe I'm wrong in the first place and all that misses is a mov esp, eax after pop eax...

Re:Pointer modified when passing it as an argument?

Posted: Sun Aug 20, 2006 11:47 am
by Habbit
proxy wrote:
this example is BROKEN, *p++ does not increment what p points to. ++ has a higher precidence than (yes both versions do) just postfix ++ returns the original value, not the new one.
you are indeed right, as the msdn doc states, but then my c++ compiler is quite broken (m$vc++ 5), since it allows that kind of construct with the behaviour i described... maybe i should update to vs.net 2005 and vc++ 8.0 (or even get g++ ^_^)

Re:Pointer modified when passing it as an argument?

Posted: Fri Aug 25, 2006 11:14 am
by Midas
Habbit wrote:you are indeed right, as the msdn doc states, but then my c++ compiler is quite broken (m$vc++ 5), since it allows that kind of construct with the behaviour i described... maybe i should update to vs.net 2005 and vc++ 8.0 (or even get g++ ^_^)
Quite apart from anything else, MSVC++ 5 might compile something but it isn't C++. C++ wasn't standardised until after MSVC++6, IIRC. (Although I'm open to correction if I'm as wildly wrong as I may be).

Re:Pointer modified when passing it as an argument?

Posted: Fri Aug 25, 2006 11:35 am
by Kemp
AFAIK none of the current compilers can be said to compile standard C++ code, as there's lots of things in there that are hard to build into the compiler or that simply aren't used enough to be a priority.

Blatently going against standard operator precedence like that is definately something that needs fixing though.