views:

489

answers:

4

Hi I'm working with C and I have a question about assigning pointers.

struct foo
{
   int _bar;
   char * _car[SOME_NUMBER]; // this is meant to be an array of char * so that it can hold pointers to names of cars
}

int foofunc (void * arg)
{
   int bar;
   char * car[SOME_NUMBER];

   struct foo * thing = (struct foo *) arg;

   bar = thing->_bar; // this works fine
   car = thing->_car; // this gives compiler errors of incompatible types in assignment
}

car and _car have same declaration so why am I getting an error about incompatible types? My guess is that it has something to do with them being pointers (because they are pointers to arrays of char *, right?) but I don't see why that is a problem.

when i declared char * car; instead of char * car[MAXINT]; it compiles fine. but I don't see how that would be useful to me later when I need to access certain info using index, it would be very annoying to access that info later. in fact, I'm not even sure if I am going about the right way, maybe there is a better way to store a bunch of strings instead of using array of char *?

EDIT: I didn't mean to use INT_MAX (maximum value of int), it's just some other int, which is about 20.

+5  A: 

car and _car are both arrays, and you cannot assign arrays in C (except when the array is embedded in a structure (or union) and you do a structure assignment).

They are also arrays of pointers to char, rather than pointers to arrays of char. What you have written in the code is probably what you want - you could store pointers to up to MAXINT names in the array. However, you should describe the type correctly - as an array of pointers to char or char pointers.

A pointer to an array of characters would look like:

char (*car)[MAXINT];

And a point to an array of character pointers (thanks, Brian) would look like:

char *(*car)[MAXINT];

Be careful of MAXINT; that could be a very large array (on Linux, <values.h> defines MAXINT as INT_MAX, which is at least 231-1).


The code looks like:

struct foo
{
   int _bar;
   char * _car[MAXINT];
}

int foofunc (void * arg)
{
   int bar;
   char * car[MAXINT];
   struct foo thing = (struct foo *) arg;
   bar = arg->_bar; // this works fine
   car = arg->_car; // this gives compiler errors of incompatible types in assignment
}

Neither the assignment to bar nor car should compile at all - arg is a void *. You presumably meant to use thing in some shape or form. As Brian noted, there are problems there, too:

You either want:

int foofunc(void *arg)
{
    int bar;
    char *car[MAXINT];
    struct foo thing = *(struct foo *)arg;
    bar = thing._bar; // this works fine
    car = thing._car; // this is still an array assignment
    ...other code using bar and car...
}

Or you want:

int foofunc(void *arg)
{
    int bar;
    char *car[MAXINT];
    struct foo *thing = (struct foo *) arg;
    bar = thing->_bar; // this works fine
    car = thing->_car; // this is still an array assignment
    ...other code using bar and car...
}

Or, indeed:

int foofunc(void *arg)
{
    struct foo *thing = (struct foo *) arg;
    int bar           = thing->_bar; // this works fine
    char *car[MAXINT] = thing->_car; // this is still an array assignment
    ...other code using bar and car...
}

Finally, dealing with the array assignment, in C you can reasonably use memmove() to do this:

int foofunc(void *arg)
{
    struct foo *thing = (struct foo *) arg;
    int bar           = thing->_bar; // this works fine
    char *car[MAXINT];
    memmove(car, thing->_car, sizeof(car));
    ...other code using bar and car...
}

The similar function memcpy() does not have reliable semantics if the areas to be copied overlap, whereas memmove() does; it is simpler to always use memmove() because it always works correctly. In C++, you need to be cautious about using memmove() (or memcpy()). In this code, it would be safe enough, but understanding why is non-trivial.

You do need to be aware that you are just copying pointers here - you are not copying the strings that the pointers point at. If something else changes those strings, it affects both the values seen via car and the variable in the calling code.

One last point - for now: are you sure you need the argument to the function as a void *? It opens up the code to all sorts of abuse which can be prevented if the function is declared to take a 'struct foo *' instead (or even a 'const struct foo *').

Jonathan Leffler
I think he wants an array of pointers to char, since each pointer-to-char seems to represent a car name. However, Brian has a point that he may be able to use a pointer to the array, rather than copying it.
Matthew Flaschen
@Matthew: I agree that he probably wants what he has written in the code. However, his description of what he has is erroneous - which is what I tried to say (but evidently didn't make clear enough).
Jonathan Leffler
@Jonathan: A pointer to an array of characters is not the same as a pointer to an array of character pointers.
Brian R. Bondy
+2  A: 

You can't assign to an array the way you're doing. You can do an element-wise copy.

for(int i = 0; i < MAXINT; i++)
{
  car[i] = (arg->_car)[i]
}

Note that if if the strings aren't constant, you may need to use strcpy.

Matthew Flaschen
If he used `strcpy()`, he'd have to be careful to ensure he space for the strings to be copied into. However, if the strings aren't constant, then changes made in one place (via one variable) might appear somewhat unexpectedly in the other (the strings pointed to by the other variable would also change since both sets of pointers point to the same space).
Jonathan Leffler
+3  A: 

You are creating a new array of size MAXINT. I think you want to create a pointer to an array of size MAXINT.

Creating a pointer to an array of char*'s:

The following is an array of size MAXINT to char* elements:

char * car[MAXINT]; 

The following is a pointer to: an array of size MAXINT to char* elements:

char* (*car)[MAXINT];

the following is how you set a pointer to: an array of size MAXINT to char* elements:

char* (*car)[MAXINT];
car = &arg->_car;

Other syntax errors in the question:

  • You need to have a semicolon after your struct definition.
  • You should be using foo* not foo. So it should be:
    struct foo* thing = (struct foo *) arg;
  • You should be using thing not arg:
    bar = thing->_bar;
    car = thing->_car;
Brian R. Bondy
Thanks, what I needed was pointer to the array of char *. I think this is what I needed (crosses finger)
Fantastic Fourier
@Fantastic Fourier: Agree you needed a pointer to an array, who's elements are char*. some other answers mentioned a pointer to a char* array which is not the same.
Brian R. Bondy
A: 

Array notation in C is legitimately confusing; your code doesn't mean what you think it means.

arg->_car means "the address of the array _car". Similarly, car means "the address of the array car". If you're trying to copy the contents of _car to car, then this will do it:

memcpy(car, _car, MAXINT);

But your real question, I think, is "what's the best way to store a list of strings?" This answer is: a dynamic list (one that grows automatically as you add items).

You'd declare it like this:

#define CARSIZE 65
int numCars = 0;
char **car; /* a list of addresses, each one will point to a string */

To add a car:

char *newCar = malloc(CARSIZE); /* make room */
strncpy(newCar, "Mercedes", CARSIZE); /* newCar has the address of the string */
car[numCars++] = newCar; /* store it */

To list the cars:

int n;
for (n = 0; n < numCars; ++n)
    printf("%s\n", car[n]);

To remove the car at position n:

free(car[n]); /* release the memory */
/* condense the list of pointers */
for ( ; n < numCars - 1; ++n)
    car[n] = car[n+1];

This is completely routine in C. NOTE: The above is off the top of my head and not copied from a working program, so I can't promise all the * are in the right place. I suspect this is a homework assignment, so I don't want to give you everything...

egrunin
it is for my networks where the array is used to keep names of incoming connections (the number of connections is maxxed out at SOME_NUMBER which is 20). So I think I should stick with static, rather than dynamic.
Fantastic Fourier