1

I would like to perform a deep copy of a char**, but I have no idea how to allocate memory / copy this datatype. This is for a copy constructor in a class that contains a char**. For example, lets say I have this code:

char ** arr1 = new char*[20];
arr1[0] = (char*)"This is index 1";
arr1[1] = (char*)"This is index 2";
char ** arr2;

How do I deep copy the contents of arr1 into arr2? Any help is appreciated!

8
  • 11
    You misspelled std::vector<std::string> Commented Nov 1, 2017 at 7:42
  • @StoryTeller haha, I wish. It’s for a programming assignment, and the teacher wants all strings to be stored as char*, so an array of strings has to be stored as an array of char*. Unnecessary complication in my opinion, and not the way I’d do it for myself, but I must learn how to do it for this assignment nonetheless. Commented Nov 1, 2017 at 7:44
  • Is this a C course? Given your example, there is no way of telling how many strings are in arr1 (unless the number of entries it's stored somewhere) Commented Nov 1, 2017 at 7:45
  • 3
    @AdrianBernat: To make it a bit more explicit, we consider this a very poor programming assignment. Teaching C++ this way might have been acceptable in the 20th century, but it has no place in this age. Even if you do want to teach the underlying operations, you do so after teaching C++ basics, and furthermore you do it right. You wrap char[] in your own String class, and then build a custom Vector<String>. Commented Nov 1, 2017 at 8:22
  • 1
    Find a better teacher. Commented Nov 1, 2017 at 8:33

4 Answers 4

3

It’s for a programming assignment, and the teacher wants all strings to be stored as char*,...

You can tell your teacher that std::string does store strings as char*. If he still doesnt like you to use std::string you should write your own wrapper, because working with bare char* is what you do when you write C, but not in C++. You should write a:

struct my_string {
    char* data;
    ... constructor, operator[], etc...
};

You basically dont need to write more code than you already do, but you should put it in the right place (ie hide it behind a nice interface). You will immediately see the benefit of it when you eg consider ...

...so an array of strings has to be stored as an array of char*.

No. An array of strings is std::array<my_string> (or std::vector<my_string> if it is supposed to be dynamic). And if your teacher insists on not using std::vector, then you should do the same as you just did for strings for vectors (ie encapsulate all the dirty pointer and memory stuff in one place).

Sign up to request clarification or add additional context in comments.

Comments

2

This seems more like a C question, but here is an example:

char **AllocateAndDeepCopy(char **arr1, int arr1size) 
{
    unsigned int    i;
    char            **arr2;

    /* Allocate string array */
    arr2 = new char*[arr1size];

    /* Iterate array elements */
    for (i=0; i<arr1size; i++) {
        /* Allocate string */
        arr2[i] = new char[strlen(arr1[i])+1];

        /* Copy contents */
        strcpy(arr2[i], arr1[i]);
    }   

    return arr2;
}

Later you have to deallocate arr2 this way:

void DeallocateArr2(char **arr2, int size) 
{
    for (int i=0; i<size; i++) {
        delete arr2[i];     
    }

    delete arr2;
}

3 Comments

I know how to do it this way, as I’ve done deep copies many times before. This did inspire an idea however; I may have the size of the array be an integer that I can pass in. The dilemma that inspired this question to be asked, however, was deep copying without having the size of the array passed in. For a char*, this step is done by saying new char[strlen(to_copy) +1] however, I could not think of an equivalent for allocating memory for a char**. Is it possible to allocate that memory without having the size of the array passed in as an int to the function?
The clue is in the difference between how a char * is laid out in memory as opposed to how arrays i.e. char **, int *, etc are laid out, i.e. null-terminator.
Yes, you can use a NULL element at the end of the array arr1 which can be used as indicator of it's size. Then you can avoid passing the size.
1

I can only shake my head about the sorry state of C++ education. We have a looong way to go there. But since that’s apparently a given, what’s the best you can do?

To copy a C-style data structure like that you have know two things at the point of copy. Both are not inherently provided by a C-style array, so you’ll have to track them explicitely.

  • The capacity of arr1: 20. If that’s not a compile time constant you have to store it and pass it around. Since you want to implement a copy ctor that means storing the capacity in a non-static member variable of the object.
  • The number of used indexes in arr1: 2. Same as above. Alternatively make sure that all unused indexes are set to nullptr.

Now you can allocate an arr2 of the correct size and then allocate+memcpy all used indexes.

However, your program will go up in flames regardless, because arr1 and arr2 cannot be treated the same, even though they look identical. The used indexes of arr1 must never ever be deleted because they contain pointers to character literals: They were never newd and live in read-only memory. On the other hand you absolutely must delete the indexes of arr2, because they were newd.

If this brutal disregard of const is really required by the assignment I’d go one step further. I’d introduce another member variable, an array of booleans that tracks which indexes of the char array point to char literals and which were dynamically allocated. During copy you now have all the necessary information to either memcpy or simply set the pointer. Crazy? Definitely, but the whole assignment is, and that way the craziness is visible at least instead of hidden behind an innocent-looking C-style cast. Btw: those should be const_cast<char*> to make it clear what’s going on.

Comments

0

Just take a look at http://en.cppreference.com/w/cpp/algorithm/copy, the deep copy is made by *d_first++ = *first++;

3 Comments

That is a shallow copy (it's an array of pointers to arrays, so 2D, and a 1D loop is necessarily shallow)
2D is an artist's impression char *d_first=*arr2;char *first=*arr1;while (first!=last) *d_first++=*first++; is deep.
That's a 1D copy of arr[0] and fails to copy arr[1].

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.