1

I have the following C code which works:

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

int pw = sizeof(char*);     // width of pointer (to char)

int num;
int first = 1;
int size = 0;
int incr = 10;

char *(*arr)[];     // pointer to array of pointers to char */

test(char* s, int i)
{

  int j;
  char *(*newarr)[];        // pointer to array of pointers to char

  if (first) {          // first time
    arr = malloc(pw*incr);  // malloc array
    first = 0;          // skip from now on
    size = incr;        // save the size
  }


  if (i >= size) {          // out of space
    newarr = malloc(pw*(size+incr));    // get incr bigger space
    for (j=0; j<size; j++)      // copy the elements from the old
      (*newarr)[j] = (*arr)[j];     // array to new array
    free(arr);                  // free the old array space
    arr = newarr;           // point old array to new array
    size = size+incr;

  };

  int len = strlen(s);      // length of s
  (*arr)[i] = malloc(len+1);    // assign pointer to pointer array element
  strcpy((*arr)[i], s);     // copy s to array
                    // both arguments must be pointers

  printf("%d\t%s\n", i, (*arr)[i]);
};

main() 
{

  char* s = "this is a string";

  for (num=0; num<30; num++)    // add 30 pointers to s to *arr
    test(s, num);

  for (num=0; num<30; num++)
    printf("%d\t%s\n", num, (*arr)[num]); // print out what they point to
};

It prints out 'i\tthis is a string' for 'i' from 0 to 29 twice. What I want to do is pass 'arr' from the top of the file as an argument of 'test'. The reason I want to do that is because I want to pass several different arrays all of which are declared the same way. If I make the minimal changes to do that I get:

0   this is a string
Segmentation fault (core dumped)

Here is the output of the diff command which shows the minimal changes:

    13c13
< char *(*arr)[];       // pointer to array of pointers to char */
---
> char *(*jarr)[];      // pointer to array of pointers to char */
15c15
< test(char* s, int i)
---
> test(char* s, int i, char *(*arr)[])
52c52
<     test(s, num);
---
>     test(s, num, jarr);
54,55d53
<   for (num=0; num<30; num++)
<     printf("%d\t%s\n", num, (*arr)[num]); // print out what they point to

In other words everything is the same except for renaming 'arr' as 'jarr' and passing it to 'test'.

Thanks in advance, Mike

8
  • 1
    When you get a segmentation fault, or any other crash, your first reaction should be to run your program in a debugger. It will help you find the location of the crash, and also let you examine variables to what might have caused it. Commented Sep 5, 2012 at 14:05
  • Thanks for that. I should have said that it crashes at the line ' (*arr)[i] = malloc(len+1); // assign pointer to pointer array element' on the second pass through. Commented Sep 5, 2012 at 14:08
  • 2
    It looks like you're making this way more complicated than it needs to be - why not just use a char ** and allocate memory in the usual way ? Commented Sep 5, 2012 at 14:10
  • Also I think it's related to link. Commented Sep 5, 2012 at 14:10
  • 3
    Probably crashes there because you have one level of indirection to much. You should probably be using only char **arr;, and stop referencing it as (*arr). Commented Sep 5, 2012 at 14:11

3 Answers 3

1

The trouble occurs when you call:

test(s, num, jarr);

You are passing jarr by value. Inside the function, you are reallocating (the hard way — why not use realloc() which does the copying for you?) the array, but that change does not affect the value of jarr 'in main()' because it was passed by value. The second time through the loop, you are still passing a null pointer to the function, but you are then dereferencing that null pointer, which is bad news.

How to fix?

Fair question...I'm not sure if the old "well, if I want to get to there, I wouldn't start from here" gag passes muster.

The 'simplest' change is to revise the call:

jarr = test(s, num, jarr);

and then 'just' revise the function so that it returns a pointer to an array of character pointers. That is a very esoteric function. My brain's not awake (insufficient caffeine), so I used an intermediate typedef to get around the problem of how to write the function declaration and definition:

typedef char *(ArrayString[]);

ArrayString *test3(char *s, int i, char *(*arr)[]);

ArrayString *test3(char *s, int i, char *(*arr)[]) { (*arr)[i] = s; return arr; }

It compiles without warnings; that isn't a guarantee that it's correct.

The primary alternative is to pass a pointer to a pointer to an array of char pointers to the function, which is even more esoteric.


However, both of these are 'starting from here' solutions. You'd do better, on the whole, to devise a different way of handling things. Pointers to arrays are certainly a part of C, but they are at the outer edges of C and you should generally assume that if your design calls for their use, then your design is probably not the best. You should use a simpler char ** (or, perish the thought, char ***; triple indirection is best avoided too, but that isn't always possible).

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

2 Comments

Thanks Jonathan that worked perfectly. All of the answers hinge on the fact that the 'test' function has to deal in one level of indirection more than the declaration of the array (or a pointer to it). The other answers involve taking the address of the pointer to the array and modifying 'test' while yours removed one level of indirection in the declaration (from pointer to array to simply array). Your answer is the one I was looking for. As for C idioms it's a case of either triple indirection or pointer to array of pointers to do what I want to do.
In addition your answer is minimally different than what I posted.
1

You seem to have misunderstood how arrays and pointers works. Lets say you want a dynamic array of strings, that is basically a pointer to a pointer of char:

char **arr = NULL;

To allocate memory for that you do e.g.

arr = malloc(sizeof(char *) * current_size);

Now you have an "array" of character pointers. Lets say you want each of these to be a specific string str:

for (int i = 0; i < current_size; i++)
{
    arr[i] = strdup(str);
}

Oh, now you need to increase the number of strings, all initialized to the same string as before:

size_t new_size = current_size + 10;
arr = realloc(arr, sizeof(char *) * new_size);

for (int i = current_size; i < new_size)
{
    arr[i] = strdup(str);
}

The problem now is that you want to do all of the above in a separate function. It's first now that you have to add another indirection.

Comments

0

I think you can do a double check on the first malloc value assigned to jarr both in the test(s, 0, jarr) and out of the test(s, 0, jarr); the jarr assignement is not successful since you change the pointer value in the passing by value.

Comments

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.