1

I've been struggling to fix this since morning. The value 'pd' pm doesn't change outside the function. Could someone tell me what the mistake is?

void foo(u8 *pm, u8 *pd) 
{
    pm = "IWR ";
    memcpy(pd, pm, sizeof(pm));
    printf("in foo pm = %s, pd = %s \n", pm, pd);
}

int main()
{
    u8 pm[5] = "0";
    u8 pd[5] = "IWO ";
    printf("pm = %s, pd = %s \n", pm, pd);  
    foo(pm, pd);
    printf("after pm = %s, pd = %s \n", pm, pd);
}

My final output after call to foo is pm = (null) and pd = "IWO ". I thought that 'pm' also would change value.

(Here is the code on ideone, but in that case pm prints as 0, not (null). Why is this? )

pm = 0, pd = IWO  
in foo pm = IWR , pd = IWR  
after pm = 0, pd = IWR  
Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
swap
  • 69
  • 1
  • 3
  • 8
  • C++ but don't want to use string functions – swap Feb 08 '12 at 19:04
  • It works fine here - though you do have a probable `sizeof` bug. – Carl Norum Feb 08 '12 at 19:04
  • Oops! my mistake! But I still don't quite understand the concept of passing char arrays in C/C++ – swap Feb 08 '12 at 19:07
  • 1
    `sizeof` doesn't do what you think it does. In particular, given a pointer, `sizeof` does *not* know the length of the array it might point to. – Aaron McDaid Feb 08 '12 at 19:12
  • @swap, can you copy in the actual output you are getting, please? – Aaron McDaid Feb 08 '12 at 19:13
  • @Aaron my final output after call to foo is after pm = (null) and pd = "IWO " I thought that 'pm' also would change value. – swap Feb 08 '12 at 19:34
  • @swap, I've put that information into the question. You should try to edit the question to ensure it includes everything. For example, you said "pm = (null)", but I don't believe you :-). I think you got the string "`0`"; is this right? – Aaron McDaid Feb 08 '12 at 20:15

3 Answers3

4
  1. sizeof(pm) in your function foo() is the size of a pointer. Not the size of the array, as it looks like you're assuming.
  2. I guess you mean that pm doesn't change outside the function, since given your program, pd most certainly does. The reason pm doesn't change is because C (and C++ in the way you're using it) is a pass-by-value language. The C FAQ has a question about precisely your problem.
Carl Norum
  • 219,201
  • 40
  • 422
  • 469
1

You can pass an array by reference with a template:

template<typename T, unsigned int Length1, unsigned int Length2>
void foo(T(&pm)[Length], T(&pd)[Length2]) {
    memcpy(pd, "IWR ", Length2 - 2); // - 2 for the NULL
    pd[Length2 - 1] = 0;

    printf("in foo pm = %s, pd = %s \n", pm, pd);
}

And use it the same way you were using foo before. Note that this will only work with arrays, not with pointers.

Note that

pm = "IWR ";

Doesn't do anything in your original function (just modifies the local pointer variable) and doesn't work in this modified one (can't assign to arrays). If you want to do that, you'll have to use memcpy as well.

If you don't want to use templates, then you'll have to pass the size of each array into the function (or use a sentinel value but don't) because when you pass an array into a function (without passing it by reference) it decays to a pointer, and while sizeof(array) will give you the number of bytes in an array, sizeof(pointer) just gives you the number of bytes in a pointer which is not what you want.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • So, you say I have to pass the size to do a memcpy. But can I do it without memcpy? What I want basically is a function that changes both my values and gives me the modified values. But I am lost in the part where I have to do a pass-by-ref – swap Feb 08 '12 at 19:16
  • @swap in the code you posted, you are passing _a pointer to the first element of the array **by value**,_ not by reference. If you do it that way, there's no way to know the size of the array pointed to by the pointer so you have to pass the size as an argument to the function. When you pass _the array_ by _reference_, you know the size without having to pass it as an argument to the function (by both the `Length` template arguments and `sizeof` will also work). – Seth Carnegie Feb 08 '12 at 19:21
  • Oh.. okay. But my ignorance lies in passing a char array as reference. How would the syntax look like? – swap Feb 08 '12 at 19:24
  • Can you please give the syntax without using templates? Does it look something like this? foo(u8 &pm, u8 &pd, int lenPm); main() { foo(pm, pd); } – swap Feb 08 '12 at 19:36
  • @swap it would look like `void foo(u8(&pm)[5], u8(&pd)[5]); int main() { foo(pm, pd); }`. Note that I put a literal `5` in there. You have to because array sizes must be constants. That means that if you don't use templates, you can't use the function with arrays that have any other size but what you hard-code into the function (5 in this case), which is why passing arrays by reference is almost never used without templates. – Seth Carnegie Feb 08 '12 at 19:38
0

Hi by doing pm = "IWR" you have changed what pm have been pointing to a const reference. i.,e you have completely changed the pointer pm ( inside the function ) to point to a different location.

if you do this inside the function your code would work as expected. See why its failing below.


    pm[0] = 'I';
    pm[1] = 'W';
    pm[2] = 'R';

    before foo in main
    ----------
     print pm "0\000\000\000"
     print &pm ==  (u8 (*)[5]) 0x7fbffff7b0

    in foo as soon as we enter
    ------
     print pm = (u8 *) 0x7fbffff7b0 "0" (you are pointing to the same location as in main )
     print pm = (u8 **) 0x7fbffff788

    ----     pm = "IWR" ---
   print pm = 0x400adc "IWR" (now pm is pointing to location in memory where const IWR is stored )
   print &pm = (u8 **) 0x7fbffff788

   ---back to main ---
     print pm =  "0\000\000\000" ( we havent changed anything on location fff7bo so no affect )
     print &pm $8 = (u8 (*)[5]) 0x7fbffff7b0 

Google for changing the head of linked list and you should be able to find proper explanation

cslibrary.stanford.edu/103/LinkedListBasics.pdf [ take a look changing head pointer ]

Shanky
  • 139
  • 1
  • 12