ogdev1234
ogdev1234

Reputation: 59

C++ a value of type "char" cannot be assigned to an entity of type "char *"

I'm getting the error from the title when I try to initialize a two dimensional array.

//strX and strY are strings
int m = strX.length();
int n = strY.length();

//declare two dynamic 2-Dimensional array of variable length B is m X n
char** B;

InitializeTable(&B, m, n);

//function below
InitializeTable(char***B, int m, int n)
{
for (int i = 0; i < m; i++)
{
    for (int j = 0; j < n; i++)
    {
        B[i][j] = ' '; //getting the error on the equals sign
    }
}

I think is something very simple I'm missing. Thank you!

Upvotes: 0

Views: 1667

Answers (4)

Remy Lebeau
Remy Lebeau

Reputation: 598349

Inside of InitializeTable(), you need to dereference B before you can then index into the array. Also, you are not allocating any memory for the array. Try something like this:

void InitializeTable(char** *B, int m, int n)
{
    *B = new char*[m];

    for (int i = 0; i < m; i++)
    {
        (*B)[i] = new char[n];

        for (int j = 0; j < n; i++)
        {
            (*B)[i][j] = ' ';
        }
    }
}

...

int m = strX.length();
int n = strY.length();

char** B;
InitializeTable(&B, m, n);

...

for (int i = 0; i < m; ++i)
    delete[] B[i];
delete[] B;

Alternatively, pass B by reference instead of by pointer:

void InitializeTable(char** &B, int m, int n)
{
    B = new char*[m];

    for (int i = 0; i < m; i++)
    {
        B[i] = new char[n];

        for (int j = 0; j < n; i++)
        {
            B[i][j] = ' ';
        }
    }
}

...

int m = strX.length();
int n = strY.length();

char** B;
InitializeTable(B, m, n);

...

for (int i = 0; i < m; ++i)
    delete[] B[i];
delete[] B;

That being said, a better solution would be to not use raw pointers at all, though. You should use std::vector instead:

void InitializeTable(std::vector<std::vector<char>> &B, int m, int n)
{
    B.resize(m);

    for (int i = 0; i < m; i++)
    {
        B[i].resize(n);

        for (int j = 0; j < n; i++)
        {
            B[i][j] = ' ';
        }
    }

    // alternatively:
    // B = std::vector<std::vector<char>>(m, std::vector<char>(n, ' '));
}

...

int m = strX.length();
int n = strY.length();

std::vector<std::vector<char>> B;
InitializeTable(B, m, n);

...

Upvotes: 0

David C. Rankin
David C. Rankin

Reputation: 84652

Your "Doahh...." moment is:

for (int j = 0; j < n; i++)

Why are you using i++ in a j loop?

Beyond the typo, you would generally want to pass only m and n to InitializeTable, declare B local to InitializeTable, allocate m pointers and then n chars and assign the beginning address for each allocation of chars to the successive pointer and return B and assign the return back in main().[1] This prevents passing the address of B as a parameter and thereby becoming a 3-Star Programmer (not a compliment). That said, there is educational purpose in the exercise.

When you declare char **B; in main(), B is an unitialized pointer to pointer to char. It has its very own address (the pointer B), but it points nowhere (actually the address held by B is indeterminate and likely just whatever was at the address of B at the time it was declared. You cannot use B for any other purpose at this point other than assigning the address of another char ** pointer that has been properly initialized.

When you pass the address of B to InitializeTable, e.g.

    InitializeTable (&B, m, n);

and B receives the address, you must allocate m pointers and assign the beginning address for the pointers as the value held by B (not as the 3-star pointer address). To do this, you must dereference B in InitializeTable. (just as you would declare int *a, b = 5; and then make a point to b with a = &b, to change the value pointed to by b you would dereference and assing, e.g. *b = 10;) Example:

void InitializeTable (char ***B, int m, int n)
{
    *B = new char*[m];

By using the new operator, you have allocated storage for m pointers (char*) and assigned the beginning address to the pointer B in main(), *B in InitializeTable.

Now you need to allocate n chars for each pointer and assign the beginning address for each block to each pointer. However, since we are 3-star programmers, and have one additional level of indirection to deal with, instead of assigning to B[i], we must derefence B first -- but, C++ Operator Precedence causes [] to bind tighter (has higher precedence) than the '*' dereference operator, so you must enclose *B in parenthesis first, e.g. (*B)[i] with:

     for (int i = 0; i < m; i++) {
        (*B)[i] = new char[n];

Now you can assign your spaces as the characters to initialize the character values in (*B)[i], e.g.

        for (int j = 0; j < n; j++)
            (*B)[i][j] = ' ';

(note: all js in the loop definition)

That's all there is to it. Putting it altogether, you could do:

#include <iostream>
#include <string>

void InitializeTable (char ***B, int m, int n)
{
    *B = new char*[m];
    for (int i = 0; i < m; i++) {
        (*B)[i] = new char[n];
        for (int j = 0; j < n; j++)
            (*B)[i][j] = ' ';
    }
}

int main (void) {

    std::string strX = "cats",
                strY = "dogs";

    //strX and strY are strings
    int m = strX.length();
    int n = strY.length();

    //declare two dynamic 2-Dimensional array of variable length B is m X n
    char **B;

    InitializeTable (&B, m, n);

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++)
            std::cout << " '" << B[i][j] << "'";
        std::cout << '\n';
        delete[] B[i];      /* free each block of characters */
    }
    delete[] B;     /* free pointers */
}

(don't forget to free both the memory holding the characters, as well as the pointers you allocate)

Example Use/Output

$ ./bin/threestarc++
 ' ' ' ' ' ' ' '
 ' ' ' ' ' ' ' '
 ' ' ' ' ' ' ' '
 ' ' ' ' ' ' ' '

Memory Use/Error Check

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ ./bin/threestarc++ ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '

$ valgrind ./bin/threestarc++
==784== Memcheck, a memory error detector
==784== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==784== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==784== Command: ./bin/threestarc++
==784==
 ' ' ' ' ' ' ' '
 ' ' ' ' ' ' ' '
 ' ' ' ' ' ' ' '
 ' ' ' ' ' ' ' '
==784==
==784== HEAP SUMMARY:
==784==     in use at exit: 0 bytes in 0 blocks
==784==   total heap usage: 8 allocs, 8 frees, 72,810 bytes allocated
==784==
==784== All heap blocks were freed -- no leaks are possible
==784==
==784== For counts of detected and suppressed errors, rerun with: -v
==784== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

footnotes:

1. Actually you would want to declare a vector of vector <char> (e.g. std::vector<std::vector<char>> in main() and pass a reference to InitializeTable for initialization letting C++ handle the memory management for you.

Upvotes: 3

flu
flu

Reputation: 556

When passing multidimensional arrays, I think it's easier to think of them as references. So in InitializeTable() you can just declare B to be a reference to char**, ie. char**&.

//strX and strY are strings
int m = strX.length();
int n = strY.length();

//declare two dynamic 2-Dimensional array of variable length B is m X n
char** B;

InitializeTable(B, m, n);               // Just pass in B, which will be a reference to char**.

//function below
InitializeTable(char**&B, int m, int n) // Declare B to be a refernce to char**.
{
    B = new char * [m];                 // Allocate for first dimension, note these are pointers.
    for (int i = 0; i < m; i++)
    {
        B[i] = new char[n];             // Allocate for second dimension, note these are chars.
        for (int j = 0; j < n; j++)     // Note: you had i++ here.
        {
            B[i][j] = ' ';
        }
    }
}

Upvotes: 0

John3136
John3136

Reputation: 29266

Think of it like this: Each set of [] removes a *. Simple case: char *string = "fred"; string[0] removes a [] so you get the data (f)

You have ***B and B[i][j] so you still have one * to go...

The fact that you have not initialized any memory is also a problem...

You should also give a SSCCE.

Upvotes: 1

Related Questions