stan@Dixie.Com (Stan Brown) (03/03/91)
I have a problem with a program that I am working on. It compiles and works correctly, but generates a compiler warning message of the form warning : structure/union or structure/union pointer required Since I plan on moving this code to a new machine when I get it I dam concerned aboyt portabilty. The environment that I am working in is a version 7 machine (Fortune 32:16 if you ever heard of it). What rellay pzzles me is that I think there is one more level of inderction in the compare function than I think is required, yet it works. If someone could explain my miscomception here, I would greatly appreciate it. ** The structure /* structure of fileing location information */ struct file_loc { char *key; char *description; char *long_description; int qty; } ; ** The array decleration main(argc,argv) int argc; char *argv[]; { struct file_loc *array_of_loc_struct[MAX_LOCS]; ** The call from main sort_loc(array_of_loc_struct); ** The call to qsort void sort_loc(array) struct file_loc *array[]; { extern int cmp_loca(); int qty = 0; while(array[++qty] != NULL) /* Find Qty */ ; qsort(&array[0], qty, sizeof(struct series *), cmp_loca); } ** The compare function int cmp_loca(struct1,struct2) void *struct1; void *struct2; { return strcmp((*(struct file_loc *)struct1)->key, (*(struct file_loc *)struct2)->key); } -- Stan Brown P. c. Design 404-363-2303 ateche!sdba!stan "vi forever"
scs@adam.mit.edu (Steve Summit) (03/03/91)
In article <7519@rsiatl.Dixie.Com> stan@Dixie.Com (Stan Brown) writes: > I have a problem with a program that I am working on. It compiles >and works correctly, but generates a compiler warning message of the form >warning : structure/union or structure/union pointer required > Since I plan on moving this code to a new machine when I get it I >dam concerned aboyt portabilty. An excellent attitude. It can be tricky to call qsort, and to write an appropriate comparison function, correctly even in simple cases. (For instance, handing qsort an array of pointers to characters and attempting to use strcmp as a comparison function is an obvious and attractive technique that won't work, for reasons which I ought to discuss in a future update to the FAQ list.) Stan is trying to sort an array of pointers: > struct file_loc *array_of_loc_struct[MAX_LOCS]; to a structure: > struct file_loc { > char *key; ... with the call to qsort: > qsort(&array[0], qty, sizeof(struct series *), cmp_loca); Here is a minor problem. The array being sorted is an array of pointers, so sizeof(struct *) is the right sort of number to be passing to qsort as the element width. However, it should be sizeof(struct file_loc *), not sizeof(struct series *). On virtually all machines, all structure pointers (regardless of the struct type pointed to) are, in some internal sense, "the same;" in particular they tend all to be of the same size. The series/file_loc mismatch probably isn't causing any trouble, but it should definitely be cleaned up. (It's confusing, if nothing else.) A warning message is correctly generated within the comparison routine, however: > int cmp_loca(struct1,struct2) > void *struct1; > void *struct2; > { > return strcmp((*(struct file_loc *)struct1)->key, > (*(struct file_loc *)struct2)->key); > } This code manages not to make the most common qsort mistake, which is to declare the two pointer arguments to the comparison function as pointers to the item of interest. They must, rather, always be declared as pointers to void (or pointers to char, under a pre-ANSI compiler). qsort does not know what it is sorting or how big the elements are. (This is why it must be given the element width.) It operates exclusively with pointers, and can be used to sort arrays of structures (where the element width would generally be much larger than the size of a single int or pointer). While sorting, it does not pass, to the comparison routine, two elements to be compared; it passes *pointers* to two elements to be compared. (Read it a second time. You are in a twisty little maze of "to"'s, all different, and significant.) Here, however, we are sorting an array of pointers to structures, so the pointers being passed to the comparison routine must be considered as pointers to pointers to structures. Let's look first at part of the comparison routine, as written: > void *struct1; > return strcmp((*(struct file_loc *)struct1)->key, struct1 is a pointer (a fairly undifferentiated pointer, which is why it is a void *). By putting the cast (struct file_loc *) in front of it, we coerce it to a pointer to a struct file_loc. The * in front of the cast gives us a struct file_loc. The struct file_loc which we've arrived at then appears as the left-hand- side of the -> operator. But you're supposed to use . to pick apart structures, not -> . (-> is for *pointers* to structures.) It is this mismatch that the compiler was complaining about. What went wrong? Let's back up. The comparison routine was actually handed a pointer to a pointer to a structure, coerced to a void *. First, therefore, we should coerce it back to the pointer to a pointer to a structure that it really is: (struct file_loc **)struct1 Putting a * in front: *(struct file_loc **)struct1 dereferences one pointer level; we are left with a pointer to a struct file_loc, which is a perfectly acceptable left-hand-side for the -> operator: (*(struct file_loc **)struct1)->key (The extra parentheses are of course required because the precedence of that leading * is lower than -> .) Whenever I write a qsort comparison routine, it always strikes me as being a little funny that I "put on" what looks like an extra level of indirection in the cast (the (struct file_loc **), in this case), only to take it right back "off" again with the leading *. But it's the way qsort comparison functions always end up working, because they are handed a pointer to the things they are to compare, not the things themselves. (When the things are themselves pointers, it's very easy to drop a level of indirection in your thinking.) I would recommend naming the two pointer parameters in the comparison routine "p1" and "p2", rather than "struct1" and "struct2", to minimize confusion. Steve Summit scs@adam.mit.edu
browns@iccgcc.decnet.ab.com (Stan Brown) (03/05/91)
In article <7519@rsiatl.Dixie.Com>, stan@Dixie.Com (Stan Brown) writes:
^^^^^^^^^^
<<chuckle>> Clone technology marches on!
My opinions are mine: I don't speak for any other person or company.
email: browns@iccgcc.decnet.ab.com
Stan Brown, Oak Road Systems, Cleveland, Ohio, USA +1 216 371 0043