wayne@cs.odu.edu (C Wayne Huling) (03/21/91)
I keep getting errors from this, I don't understand the structure used for namelist?? How do I access this? I keep getting: "t.c", line 14: warning: struct/union or struct/union pointer required #include <stdio.h> #include <sys/types.h> #include <sys/dir.h> main (){int numfiles,i; struct direct *(*namelist[100]); if ((numfiles = scandir(".",namelist,NULL,NULL)) == -1){ fprintf (stderr,"ERROR: Couldn't open directory.\n"); exit(1); } else printf ("Yeaaaa..... \n"); fprintf(stderr,"%d \n",numfiles); for (i=0;i<=numfiles;i++) printf("%s\n",namelist[i]->d_name); fflush(stdout); }
rjohnson@shell.com (Roy Johnson) (03/22/91)
In article <WAYNE.91Mar20115318@robyn.cs.odu.edu> wayne@cs.odu.edu (C Wayne Huling) writes: > I keep getting errors from this, I don't understand the structure used > for namelist?? How do I access this? I keep getting: > "t.c", line 14: warning: struct/union or struct/union pointer required > [...] > struct direct *(*namelist[100]); > [...] > printf("%s\n",namelist[i]->d_name); namelist is array of pointers to pointers to struct direct, but you are accessing it as if it's array of pointers to struct. One more level of indirection: printf("%s\n",(*namelist[i])->d_name); should do it. This post valid unless superceded by a post by Chris Torek, Doug Gwyn, or Karl Heuer (where is he, anyway?), or other comp.lang.c gurus. -- ======= !{sun,psuvax1,bcm,rice,decwrl,cs.utexas.edu}!shell!rjohnson ======= Feel free to correct me, but don't preface your correction with "BZZT!" Roy Johnson, Shell Development Company
torek@elf.ee.lbl.gov (Chris Torek) (03/22/91)
In article <WAYNE.91Mar20115318@robyn.cs.odu.edu> wayne@cs.odu.edu (C Wayne Huling) writes: > struct direct *(*namelist[100]); > if ((numfiles = scandir(".",namelist,NULL,NULL)) == -1) ... Chances are you wrote this based on a correct, but highly misleading (BECAUSE IT USES EMPTY ARRAY BRACKETS! [okay, done shouting :-) ]), version of the `scandir' manpage. This particular version probably said: scandir(dirname, namelist, select, compar) char *dirname; struct direct *(*namelist[]); int (*select)(); int (*compar)(); in its `SYNOPSIS' section. Before you can use this manual page, you must note two things: A. The man page shows the declaration in the callee, not the caller. For instance, when you see something like: fstat(fd, buf) int fd; struct stat *buf; you should think: routine_that_calls_fstat(int fd) { int result; struct stat st; result = fstat(fd, &st); } I.e., where the function demands a `pointer to T', you provide a pointer that already points to at least one instance of T. (After all, if your pointer has not been set to point anywhere, why are you providing it at all? If the callee is to make something up, it can bloody well make it up all by itself.) B. The declaration for `namelist' has empty array brackets. RED ALERT! Empty array brackets should always scare you. You must examine them twice, and then maybe twice more, to make sure they mean what you think they mean. Here is the declaration again: struct direct *(*namelist[]); In this case, the empty array brackets are being used to `declare' the argument to the scandir() function. They are also the `innermost' level, exactly as if the parentheses were not there. In fact, the parentheses are almost deliberately misleading, because there is a different case where they are necessary: char (*p)[10]; declares `p' as a `pointer to array 10 of char', and the parentheses are required to prevend the declaration from being `array 10 of pointer to char'. Anyway, since the parentheses are redundant, the first thing to do is get rid of them---they are just a distraction. struct direct **namelist[]; Now we have what looks like `array ? of pointer to pointer to struct direct'. But we know (by The Rule) that there is no such thing as an `array' parameter. The compiler adjusts the declaration for any parameter declared as `array N of T', and pretends you wrote `pointer to T'. (It is allowed, but not required, to drop the constant value N, and as a result you, the programmer, are not required to include the constant N. This is a very special case.) So the compiler will adjust this declaration as if we wrote `pointer to T' instead of `array of T'. Here `T' is `pointer to pointer to struct direct', and the result is a `pointer to pointer to pointer to struct direct', or struct direct ***namelist; Finally (after about 60 lines) we know what it is that scandir really wants: it wants a struct direct ***namelist; Now you can go back to point `A' above: functions that expect a pointer need one that points to something. In order to have something for this pointer to point to, we need to declare an object with one fewer `*'s: doit(char *dir) { struct direct **names; int i, nnames; Now we can call scandir, passing the address of our `names' variable: nnames = scandir(dir, &names, NULL, NULL); and scandir will then make `names' point to the first of `nnames' objects, each of which is a `pointer to struct direct'. Scandir will further make each of those point to a single `struct direct' object. for (i = 0; i < nnames; i++) printf("%s\n", names[i]->d_name); Scandir uses malloc() to allocate memory for each pointer, so to free all allocated memory, we must first free each `struct direct' object: for (i = 0; i < nnames; i++) free(names[i]); and then free the pointer-that-points-to-nnames-pointers-to-struct-direct: free(names); } Note that we depend heavily on prototypes above; if we have not put the proper prototypes in scope, we would have to write: nnames = scandir(dir, &names, (int (*)())NULL, (int (*)())NULL); and for (i = 0; i < nnames; i++) free((char *)names[i]); free((char *)names); (this assumes that the lack of prototypes implies a lack of `void *'; by using the prototypes, we avoid the entire issue of the proper type for free). As a side note, the SunOS 4.1 scandir man page reads: scandir(dirname, &namelist, select, compar) char *dirname; struct direct **namelist; int (*select)(); int (*compar)(); which is correct but is a complete change of perspective (now we are viewing `scandir' from the caller, not from scandir itself), but also goes on to write: alphasort(d1, d2) struct direct **d1, **d2; when in fact `alphasort' (or any sort you give) is simply passed directly to qsort, and therefore would have to be alphasort(d1, d2) void *d1, *d2; to work correctly. (This is not a problem on all the machines on which SunOS runs, since they all have only one `kind' of pointer; and to be fair, they inherited this particular error from 4BSD, although it has since been fixed there.) (One has to wonder why, when the scandir man page was changed to have this new point of view, the corresponding section 2 man pages were not changed the same way. Certainly these should be consistent.) -- In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427) Berkeley, CA Domain: torek@ee.lbl.gov