Unexpected behavior of strcpy()

This is my program:

  char *myA[4] = {"12345678901234567890",
                  "BlabberdeBlabaBlasty",
                  "AbrcadabraSomeGarble",
                  "SomeNiknobledebobley"
                 }; 
  char myStr3[20] = "";
  char myStr4[20] = "Doe er iets in";
  // strcpy works only when myStr3 and myStr4 are declared
  // AND initialized.
  strcpy(myStr3, myStr4);

  myA[2] = myStr3;

  printf("myA[2] contains: |%s|\n\n", myA[2]);    // |Doe er iets in|

  // Uncomment strcpy() below and the content of myA[2] becomes &
  // WHY THE .... IS THAT?
  //strcpy(myStr3, "&");
  
  printf("myA[2] contains: |%s|\n\n", myA[2]);    // |&|

  // print contents of myA array.
  for (int i = 0; i < 4; i++)
  {
    printf("i: |%i| String: |%s|\n", i, myA[i]);
  }

The output is:
// With strcpy(myStr3, "&"); active
//i: |0| String: |12345678901234567890|
//i: |1| String: |BlabberdeBlabaBlasty|
//i: |2| String: |&|
//i: |3| String: |SomeNiknobledebobley|

// With //strcpy(myStr3, "&"); commented out
//i: |0| String: |12345678901234567890|
//i: |1| String: |BlabberdeBlabaBlasty|
//i: |2| String: |Doe er iets in|
//i: |3| String: |SomeNiknobledebobley|

It must have something to do with pointers, but after a day searching and trying, I need help.
What is going on here?

You forgot to allocate room in some of your arrays for the terminating null - '\0'. Example, the length of "foo" in C, is 4.

After a good night sleep I realized that with the statement: "myA[2] = myStr3;" I just copied pointers.
When this statement is executed "myStr3" AND "myA[2]" are pointing to the same memory location.
Then when the second statement "strcpy(myStr3, "&");" is executed, myStr3 is pointing to a memory location that contains the value "&".
And as "myStr3" AND "myA[2]" are pointing to the exact same memory location, I get "&" when I print the content of "myA[2]".
But,
knowing what's going on is half of the solution.
Next,
is finding out how to get "myA[2]" pointing to the location where the char string "myStr4" is pointing to.

Do you know where the text is when you do this:

char *myA[4] = {"12345678901234567890",
                  "BlabberdeBlabaBlasty",
                  "AbrcadabraSomeGarble",
                  "SomeNiknobledebobley"
                 };

The compiler puts those text somewhere, and all you have a an array of four pointers.

I suggest that you own all the data.

char myA[4][21] = 
{
  "12345678901234567890",      // [0][0] to [0][20]
  "BlabberdeBlabaBlasty",      // [1]
  "AbrcadabraSomeGarble",      // [2]
  "SomeNiknobledebobley"       // [3]
};

void setup()
{
  Serial.begin( 9600);
  strcpy( myA[1], "Hello World");
  Serial.println( myA[2][2]);  // one character
  Serial.println( myA[1]);     // whole line
}

void loop()
{
}

Where is that "printf()" coming from ? Is this a Arduino question ?

Ask yourself what this statement does:

myA[2] = myStr3;

Anytime you use an array name without the bracketed subscript (e.g., myStr3[0]) as you've done above, the expression on the right resolves to the memory address where myStr3 is stored. For most Arduinos, that's going to be a 16-bit piece of data. The assignment operator is going to attempt to take those 16 bits and shove them into an 8-bit address space, which will spill half of myStr3's memory address on the floor. As a general rule, with strings formed with char arrays need to be manipulated using one of the str*() or mem*() functions.

econjack:
Ask yourself what this statement does:

myA[2] = myStr3;

Anytime you use an array name without the bracketed subscript (e.g., myStr3[0]) as you've done above, the expression on the right resolves to the memory address where myStr3 is stored. For most Arduinos, that's going to be a 16-bit piece of data. The assignment operator is going to attempt to take those 16 bits and shove them into an 8-bit address space, which will spill half of myStr3's memory address on the floor. As a general rule, with strings formed with char arrays need to be manipulated using one of the str*() or mem*() functions.

No. myA is an array of pointers to char. myA[2] is a pointer to char. myStrs3 is also a pointer to char. The above line changes the pointer stored in myA[2] so it points to the char array stored at myStr3;

My bad. It is an lvalue-to-lvalue assignment.

pagemaker:
Next,
is finding out how to get "myA[2]" pointing to the location where the char string "myStr4" is pointing to.

That would be:

  myA[2] = myStr4;

instead of:

  myA[2] = myStr3;

You are replacing the contents of myStr3 with a copy of the contents of myStr4. That gives you TWO arrays containing the same characters. But then you replace the contents of myStr3 with "&" so it no longer contained the same characters as myStr4. If you want to point myA[2] to the contents of myStr4 you should use myStr4.

It has taken me a while to figure it out, but in the end...
Here's the working code:

void copyToArray(char **arrayPtr, const char *strPtr)
{
  *arrayPtr = (char*) malloc(sizeof(char) * 20);
  
  strcpy(*arrayPtr, strPtr);
}

int main(int argc, const char * argv[])
{
  char *myA[3] = {"First string", "Second String", "Third string"};
  
  char strOne[20] = "";
  char strTwo[20] = "Hello my dear";
  
  char *ptrStrOne = NULL;
  
  ptrStrOne = strcpy(strOne, strTwo);
  
  copyToArray(&myA[1], ptrStrOne);

  //myA[1] = ptrStrOne;

  printf(" myA[1]: |%s|\n", myA[1]);           // |Fifth string|
  printf("*myA[1]: |%c|\n", *myA[1]);          // |F|
  printf("&myA[1]: |%p|\n\n", &myA[1]);        // |0x7ffeefbff3f8|

  ptrStrOne = strcpy(strOne, "&");
    
  printf(" myA[1]: |%s|\n", myA[1]);           // |&|
  printf("*myA[1]: |%c|\n", *myA[1]);          // |&|
  printf("&myA[1]: |%p|\n\n", &myA[1]);        // |0x7ffeefbff3f8|

  for (int i = 0; i < 3; i++)
  {
    printf("String: |%i| is: |%s|\n", i, myA[i]);   // |Fifth string|
  }
  return 0;
}

Output:
myA[1]: |Hello my dear|
*myA[1]: |H|
&myA[1]: |0x7ffeefbff3f8|

myA[1]: |Hello my dear|
*myA[1]: |H|
&myA[1]: |0x7ffeefbff3f8|

String: |0| is: |First string|
String: |1| is: |Hello my dear|
String: |2| is: |Third string|
Program ended with exit code: 0

Now I'm able to update/attach to strOne as much as I like and copy the contents of strOne to and element of the array.

Never, never, EVER use malloc() without checking the return value, or your program WILL crash if you've run out of memory. If malloc() returns NULL, you did NOT get any memory, and using the returned pointer WILL clobber RAM, starting at address 0.

@RayLinvingston
Well, my teacher must have forgotten to tell me that.
Thank you.

@Koepel
Yes, this is an Arduino question.
Arduino is C++ and C++ relies on pointers; so it is IMHO an Arduino question.

@johnwasser
I'm afraid I wasn't to clear about my intentions, but as you can see in my solution, what you suggested din't work for me.

@aarg
I wasn't to kien on that. Too problem frustrated me too much.

Thanks all.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.