darcy@druid.uucp (D'Arcy J.M. Cain) (05/26/90)
In article <1990May25.012342.10144@csis.dit.csiro.au> peterf@csis.dit.csiro.au (Peter A Fletcher) writes: > >I would like to be able to write a routine which can pass >a block of memory back to the caller, so the obvious way >to do this is by passing a pointer to a pointer of any >type - 'void **' seems the natural way to do this (using >'void *' would allow passing addresses of non-pointers, >which is a definite no-no). > Doesn't seem natural at all. >Here's an example: > >#include <stddef.h> >#include <stdlib.h> > >void problem(void **a, int l) >{ > *a = malloc(l); >} >typedef char fiftychars[50]; >int main(int argc, char *argv[]) >{ > fiftychars *a; > problem(&a, 50); > return 0; >} >void.c: In function main: >void.c:18: warning: argument passing between incompatible pointer types First of all the typedef says that an array of fifty character is created when fiftychars is declared. However, you declare a to be a *pointer* to this array of 50 chars. In effect your declaration has become: char **a; since the 50 characters have not actually been allocated. Note that you *must* have the extra size parameter for problem. Since a is char **, &a is char ***. That is why the type mismatch. The call should be "problem(a, 50);" However the whole code seems clumsy aside from all that. I would code something like the following. Note that I am assuming that problem is not as trivial as your stripped down example indicates so I have not reduced the call to problem to a call to malloc. #include <alloc.h> void *problem(int l) { return(malloc(l)); } int main(int argc, char *argv[]) { char *a; a = problem(50); return 0; } Followups to comp.lang.c -- D'Arcy J.M. Cain (darcy@druid) | Government: D'Arcy Cain Consulting | Organized crime with an attitude West Hill, Ontario, Canada | (416) 281-6094 |
raeburn@athena.mit.edu (Ken Raeburn) (05/27/90)
In article <1990May25.012342.10144@csis.dit.csiro.au> peterf@csis.dit.csiro.au (Peter A Fletcher) writes: |> > |> >I would like to be able to write a routine which can pass |> >a block of memory back to the caller, so the obvious way |> >to do this is by passing a pointer to a pointer of any |> >type - 'void **' seems the natural way to do this (using |> >'void *' would allow passing addresses of non-pointers, |> >which is a definite no-no). That does seem like the logical way to do it, assuming the return value of the routine is used for some other purpose. |> >void problem(void **a, int l) |> >{ |> > *a = malloc(l); |> >} |> >typedef char fiftychars[50]; |> >int main(int argc, char *argv[]) |> >{ |> > fiftychars *a; |> > problem(&a, 50); |> >void.c:18: warning: argument passing between incompatible pointer types In article <1990May26.011714.7624@druid.uucp>, darcy@druid.uucp (D'Arcy J.M. Cain) confuses the issue by writing: |> First of all the typedef says that an array of fifty character is created |> when fiftychars is declared. However, you declare a to be a *pointer* to |> this array of 50 chars. In effect your declaration has become: |> char **a; |> since the 50 characters have not actually been allocated. Note that you |> *must* have the extra size parameter for problem. ... and continues to get more confused from there. Chris Torek posted a good detailed article a little while ago about arrays. I don't have a copy handy, but I'll try to briefly explain. For purposes of explanation, I will use "fiftyints" instead of "fiftychars"; some similarities between "void *" and "char *" are required, but they are distinct, and the similarities can cause confusion. In main: *a is array[50] of int a is ptr to array[50] of int &a is ptr to ptr to array[50] of int In problem: a is ptr to ptr to void The "char **" explanation is comparable to saying that by modifying *a, one could modify the address of the array. This doesn't make sense, but neither does the conversion of array to pointer in this context. The variable "a" is not pointing to a pointer, but to an array. (It's sort of akin to the error of claiming that "int x[3][3]" and "int **x" are equivalent.) The general reason for requiring the type mismatch is that the pointers involved are pointing to foo * and void * which can have different representations. If "int *" and "void *" are represented differently, then converting between the two is a (presumably) simple operation that the compiler can incorporate. However, "int **" and "void **" are not at all compatible, since you'd wind up with a pointer of type "void **" that points to memory in which you want to store a pointer in "int *" format (or vice versa), and that information cannot be maintained. Although "char *" and "void *" are pretty much constrained to have the same representation, they aren't exempted from this compatibility constraint. The fix I would suggest would be: void problem (void **a, int l) { *a = malloc (l); } typedef char fiftychars[50]; int main () { fiftychars *a; void *vp; problem (&vp, 50); a = vp; /* ... */ } or something similar.... Chris, as I recall, your article explained things quite clearly; do you have a copy you can repost?
darcy@druid.uucp (D'Arcy J.M. Cain) (05/28/90)
In article <1990May26.203219.10343@athena.mit.edu> Ken Raeburn <Raeburn@MIT.Edu> writes: > >In article <1990May25.012342.10144@csis.dit.csiro.au> >peterf@csis.dit.csiro.au (Peter A Fletcher) writes: > >|> >void problem(void **a, int l) >|> >{ >|> > *a = malloc(l); >|> >} >|> >typedef char fiftychars[50]; >|> >int main(int argc, char *argv[]) >|> >{ >|> > fiftychars *a; >|> > problem(&a, 50); > >|> >void.c:18: warning: argument passing between incompatible pointer types > >In article <1990May26.011714.7624@druid.uucp>, darcy@druid.uucp (D'Arcy >J.M. Cain) confuses the issue by writing: > >|> First of all the typedef says that an array of fifty character is created >|> when fiftychars is declared. However, you declare a to be a *pointer* to >|> this array of 50 chars. In effect your declaration has become: >|> char **a; >|> since the 50 characters have not actually been allocated. Note that you >|> *must* have the extra size parameter for problem. > >... and continues to get more confused from there. OK, I assumed that "fiftychars *a" was equivalent to "char *a[50]" since "fiftychars a" would be the same as "char a[50]." This would have made a an array of 50 pointers to char. That means that a would be a pointer to a character array. So then I tried to think of what else it could mean. It didn't seem reasonable that it could mean that a was a pointer to an array of 50 characters unless the 50 characters already existed in which case why malloc them? At that point I wrote a little test program and studied the assembler output to see if I could glean the meaning. Well, according to AT&T, Borland and GNU, the value of a is the same as *a. A little thought shows why this is true. The declaration creates an array of 50 characters and creates a pointer to them which is what a is. Since a is a pointer, it can be assigned to which is what happens when problem is called. At this point the array that was originally created is still in memory but is inaccessible to the program. I can't help but feel that this is not was the original poster had in mind. -- D'Arcy J.M. Cain (darcy@druid) | Government: D'Arcy Cain Consulting | Organized crime with an attitude West Hill, Ontario, Canada | (416) 281-6094 |