1

I was playing with double pointers in C and was wondering if I create a function that initializes the table, it crashes on going back to main when I try to make use of the memory allocated by InitStringTable. I believe a simple fix is to make strTable global and then I believe its OK, but I prefer not to do so as this is more of a learning exercise for me in passing the table around for modification i.e. I should be able to modify strTable from main or another function modifyTable after InitStringTable. Thanks for any help you can give.

int main()
{
    char** strTable;

    // Allocates memory for string table.
    InitStringTable(strTable);
    // Below lines should be able to copy strings into newly allocated table.
    // Below lines cause crash however.
    strcpy(strTable[0], "abcdef");

    strcpy(strTable[1], "xy");
}

// Allocates memory for the string table. This function should create a table
// of size 10 strings with each string 50 chars long. The code compiles fine.
void InitStringTable(char** table)
{
   int i = 0;

   table = (char**)malloc(sizeof(char)*10);

   for(i = 0; i < 10; i++)
   {
      table[i] = (char*)malloc(sizeof(char)*50);
   }

   for(i = 0; i < 10; i++)
   {
      memset(table[i], 0, 50);
   }

   strcpy(table[0], "string1");
}
5
  • 2
    Other than the fact you cast malloc (which you should not do in C), how is this related to C++ where you should be using std::string, std::vector, and not using raw pointer? Commented Sep 10, 2014 at 15:33
  • It looks like you need to change table = (char**)malloc(sizeof(char)*10); to table = (char**)malloc(sizeof(char*)*10);, seeing as table is an array of char *. Commented Sep 10, 2014 at 15:33
  • 1
    Why is your function void? Why not use the return value for something useful? Commented Sep 10, 2014 at 15:47
  • Indirection madness... if you want InitStringTable to change the variable you pass as an argument, you'll have to pass a pointer to that variable, ie: &strTable, which means you'll have to change InitStringTable to take char ***table. But really: three levels of indirection is not what you want Commented Sep 10, 2014 at 15:48
  • Yes this should not be C++. My mistake. And yes I should be allocating for char* and not char. Looks like I can either return char** or take in char*** to fix my problem. Thanks. Commented Sep 10, 2014 at 23:48

3 Answers 3

5

C is pass by value.

The value assigned to table is lost on returning from InitStringTable().


Also when allocating pointers to char ask for room for pointers to char.

So this:

... = (char**)malloc(sizeof(char)*10);

shall at least be (assuming C):

... = malloc(sizeof(char*)*10);

A possible approach to this would be:

#include <stdlib.h>
#include <string.h>
#include <errno.h>

int InitStringTable(char *** ppptable, const size_t n, const size_t l)
{
   int result = 0;

   if (NULL == ppptable)
   {
     result = -1;
     errno = EINVAL;
   }
   else
   {
     (*ppptable) = malloc(n * sizeof(**ppptable));
     if (NULL == (*ppptable))
     {
       result = -1;
     }
     else
     {
       size_t i = 0;
       for(; i < n; ++i)
       {
         (*ppptable)[i] = calloc(l, sizeof(*(*ppptable)[i]));
         if (NULL == (*ppptable)[i])
         {
           result = -1; 

           /* Failing in the middle requires clean-up. */
           for (; i > 0; --i)
           {
             free((*ppptable)[i-1]);
           }

           free(*ppptable); 
           (*ppptable) = NULL;

           break;
         }
       }
     }
   }

   return result;
 }

Call it like this:

#include <stdlib.h>
#include <stdio.h>

int InitStringTable(char *** ppptable, const size_t n, const size_t l);

int main(void)
{
  int result = EXIT_SUCCESS;
  char ** strTable = NULL;

  if ( -1 == InitStringTable(&strTable, 10, 42)) //* Allocate array with 10 "strings" à 42 chars. */
  {
    perror("InitStringTable() failed");
    result = EXIT_FAILURE;
  }
  else
  {
    strcpy(strTable[0], "abcdef");
    strcpy(strTable[1], "xy");
  }

  return result;
}

And no, I won't get into this ridiculous "You don't wanna be a 3-star-programmer!" discussion.

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

2 Comments

Thanks for the solution. Yes I made a mistake in the code in allocating a array of char where it should've been array of char*. Your solution is elegant as I forgot to add the NULL checking after calls to malloc as well. The char *** may look funny, but I think it helps reinforce that C is call by value by default which I had forgotten in coding this case. Thanks!
You don't have to free strTable[*] and strTable at the end of main(), since its memory has been allocated with malloc/calloc ? I have a bit of difficulty to understand exactly when do I need free pointers when memory is allocated in another function.
0

You have a pointer issue.

It's like if you say:

void inc(int a){
    a++;
}

int main(){
    int a = 0;
    inc(a);
    printf ("%d\n", a); // will display 0, not 1
}

does not work.

You must pass &strTable instead of strTable as InitStringTable argument, and change other things in InitStringTable consequently ..
Or just do strTable = InitStringTable(); , and return a char** from InitStringTable.

1 Comment

eh... why no indention?
0

The lines below InitStringTable() crash, because they are trying to perform operations on a memory address that is neither in the same scope as theirs nor have any reference to
that memory address.

The function InitStringTable() allocates memory to the table, but cannot be accessed by the
calling function (here main), because the memory is local to the function in which it
allocated.
Therefore in order to use the same memory address for operations in the
calling function you must pass a reference of that address to the calling function.

In your program you can do it as under :
Declare the function as :-

char **InitStringTable(char **);


int main()
{
    char** strTable;
    strTable = InitStringTable(strTable);   
    strcpy(strTable[0], "abcdef");   
    strcpy(strTable[1], "xy");
}   

char **InitStringTable(char** table)
{
   int i = 0;

   table = (char**)malloc(sizeof(char)*10);

   for(i = 0; i < 10; i++)
   {
      table[i] = (char*)malloc(sizeof(char)*50);
   }

   for(i = 0; i < 10; i++)
   {
      memset(table[i], 0, 50);
   }

   strcpy(table[0], "string1");


   /* after the function has finished its job, return the address of the table */    
   return table;
}

1 Comment

Thanks for this solution as well. I see merit in returning char**, but I see further understanding from having a char*** argument.

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.