0

I'm eradicating std::string in favor of C-strings, which I'm new to. How do I get the following to compile? g++ complains: cannot convert char(*)[16] to char**

#include <iostream>

void print(char** s, int n)
{
        for (int i = 0; i < n; ++i)
        {
                std::cout << s[i] << '\n';
        }   
}

int main()
{
        constexpr int n = 3;
        char s[n][16]{ "Hello", "Bye", "Sky"};
        print(s, n); 
}
Agrim Pathak
  • 3,047
  • 4
  • 27
  • 43
  • 6
    *"I'm eradicating std::string in favor of C-strings"* but why – Ry- May 31 '17 at 00:26
  • 3
    I hope you have a good reason to remove std strings in favour of char arrays. It will increase the possibility for error enormously. – Paul Rooney May 31 '17 at 00:26
  • I don't want dynamic memory for very small strings. – Agrim Pathak May 31 '17 at 00:27
  • 1
    `char s[n][16]{ "Hello", "Bye", "Sky"};` ojeez. – iehrlich May 31 '17 at 00:27
  • 3
    By now I would think most compilers implement *small string optimization* where small strings are not dynamically allocated. – Galik May 31 '17 at 00:30
  • Why specifically do you need a `char**` as opposed to a `char*` with row / column indexing? – synchronizer May 31 '17 at 00:30
  • 1
    Passing the array to the function, will only decay the first level array to a pointer, so the array size 16 is still part of the type of `s` and `print` takes a pointer to pointer. You would need to include the array size in the type of the argument to `print` for it to work. See this [question](https://stackoverflow.com/questions/16724368/how-to-pass-a-2d-array-by-pointer-in-c) (it's but I believe the principal holds for c++). – Paul Rooney May 31 '17 at 00:31
  • @Galik thanks to your comment I went and found this, which should be helpful: https://stackoverflow.com/questions/10315041/meaning-of-acronym-sso-in-the-context-of-stdstring/10319672#10319672 – synchronizer May 31 '17 at 00:33
  • @ojeez Beauty is in the eye of the beholder. Not knowing how your STL containers work deserves a bigger ojeez. – Agrim Pathak May 31 '17 at 01:36

3 Answers3

2

You created a multidimensional array, not an array of pointers. Usually an array can be said to be equivalent to a pointer, however in this case c++ needs to know the size of the second dimension of your array. The function would be as follows

void print(char s[][16], int n)`{
    for (int i = 0; i < n; ++i)
    {
            std::cout << s[i] << std::endl;
    }
}

Understandably you may want to pass the function using pointers as to not make an entire copy of the 2-d array. I saw you mentioned you were okay with variable length strings. That functionality is supported in the string library. You are dealing with c-strings which are not strings at all but static arrays of type character. Defining these c-strings using dynamic memory happens to give you the desired behavior as you create in the simplest terms an array of pointers.

void print(char** s, int n)
{
        for (int i = 0; i < n; ++i)
        {
                std::cout << s[i] << std::endl;
        }
}

int main()
{
        int n = 3, i;
        char** s = new char*[n];
        for (i = 0; i < 3; i++) {
          s[i] = new char[16];
        }
        s[0] = "Hello";
        s[1] = "Bye";
        s[2] = "Sky";
        print(s, n);
        for (i = 0; i < 3; i++) {
          delete [] s[i];
        }
        delete [] s;
        s = NULL;
        return 0;
}

Since you are using dynamic memory now you need to free your memory which is what the last loop serves to do. As you can see using all this dynamic memory is quite taxing and it would be easier to use the string library that has been optimized to do a much better job then you can. If you're still not convinced you should at least make your own string class to handle the dynamic memory that contains a char * as its private member. In either case you avoid this mess and just make an array of zed class objects and not deal at all with multidimensional nonsense. No one likes seg faults and memory leaks.

Adam
  • 21
  • 3
  • I misunderstood what was being asked regarding "variable length" strings. The whole point of moving away from std::string was to avoid dynamic memory. But your first piece of code looks good. So, just the 1 pointer is being copied in the first example, correct? – Agrim Pathak May 31 '17 at 02:34
  • Kind of, this article should explain it better then I can. [link](http://c-faq.com/aryptr/pass2dary.html) From what I can see as your intended purpose the first piece of code is definitely your best bet. – Adam May 31 '17 at 02:56
1

Given any type T, T arr[N]; declares a variable arr of type T[N], which is an array and not a pointer. When you use arr in almost all contexts, array to pointer conversions happen, giving the incorrect illusion that arr is a pointer of type T*.

char s[n][16] = { "Hello", "Bye", "Sky" };

declares s as an array of n elements of type char[16]. Now, when array to pointer conversion happens, s decays into a pointer of type char (*)[16]. Hence, your function needs to have the signature

void print(char (*s)[16], int n);

Which is equivalent to

void print(char s[][16], int n);

the [] is interpreted as a pointer by the compiler.

To make these complex types more readable, a type alias may be used.

using T = char[16];
void print(T s[], int n);

Addressing some concerns

As pointed out in the comments, std::string should almost always be preferred over a char array. If you have performance concerns, benchmark before doing this. I really doubt much performance gains can be observed in most cases.

Declaring an array with length n which is an int is not standard C++. It is an extension provided by your compiler, it is not portable and in most cases not necessary.

int n = 3;
char vla[n];  // this is a variable length array
char arr[3];  // this is just an array
char* darr = new char[3];  // this is a pointer pointing to dynamically allocated memory
std::string str;  // but instead, this is just better
Passer By
  • 19,325
  • 6
  • 49
  • 96
0

The compiler cannot extract from char ** the infomation about char[16]. You need to define a type char[16] and pass the pointer to this type to your print function.

#include <iostream>

typedef char str_t[16];

void print(str_t* s, int n)
{
        for (int i = 0; i < n; ++i)
        {
                std::cout << s[i] << std::endl;
        }
}

int main()
{
        int n = 3;
        char s[n][16]{ "Hello", "Bye", "Sky"};
        print(s, 3);
} 
Agrim Pathak
  • 3,047
  • 4
  • 27
  • 43
Sir J
  • 25
  • 4