bruce@graffiti.UUCP (Bruce Jilek) (08/08/85)
Why does printf insist that data->ut_line is a null string while putchar can display all of the characters of this array? Sorry if this is a trivial question, but it beats the hell out of me. (If it helps, this is on a Fortune 32:16 - sort of v7.) /**************************************************************** This program is a rough imitation of "who /usr/adm/wtmp". ****************************************************************/ #include <stdio.h> main() { struct utmp { /* This structure is straight out of utmp.h */ char ut_line[8]; /* tty name */ char ut_name[8]; /* user id */ long ut_time; /* time on */ }; FILE *f; int i; struct utmp *data; if (f = fopen ("/usr/adm/wtmp", "r")) { while ((fread (data, sizeof(struct utmp), 1, f)) > 0) { putchar('\t'); for (i = 0; i <= 7; i++) { putchar(data->ut_line[i]); } printf("\n"); printf("%s %s %ld\n", data->ut_name, data->ut_line, data->ut_time); } fclose(f); } } /********************************************************************** End of program **********************************************************************/ Sample output: tty03 /* This shows that data->ut_line isn't null */ bruce (null) 492299400 /* So what's the problem in this line? */ tty03 (null) 492299405 tty02 mike (null) 492299409 . . .
chris@umcp-cs.UUCP (Chris Torek) (08/09/85)
One question: to what does "data" point? You haven't ever *set* it to anything.... (Probably, "data" happens to point to a region on the stack, or in the data segment, that is clobbered by the call to printf(), but not by the macro invoked by putchar().) Lint would have caught that error, by the way. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251) UUCP: seismo!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@maryland
bc@cyb-eng.UUCP (Bill Crews) (08/10/85)
> Why does printf insist that data->ut_line is a null string while > putchar can display all of the characters of this array? Well, for one thing, the variable data is never initialized. The fread is surely clobbering things! -- / \ Bill Crews ( bc ) Cyb Systems, Inc \__/ Austin, Texas [ gatech | ihnp4 | nbires | seismo | ucb-vax ] ! ut-sally ! cyb-eng ! bc
ark@alice.UUCP (Andrew Koenig) (08/10/85)
> #include <stdio.h> > main() > { > struct utmp { /* This structure is straight out of utmp.h */ > char ut_line[8]; /* tty name */ > char ut_name[8]; /* user id */ > long ut_time; /* time on */ > }; > FILE *f; > int i; > struct utmp *data; > if (f = fopen ("/usr/adm/wtmp", "r")) { > while ((fread (data, sizeof(struct utmp), 1, f)) > 0) { > putchar('\t'); > for (i = 0; i <= 7; i++) { > putchar(data->ut_line[i]); > } > printf("\n"); > printf("%s %s %ld\n", data->ut_name, > data->ut_line, data->ut_time); > } > fclose(f); > } > } When you are reading into the thing pointed to by data, data doesn't point anywhere because you've never initialized it! Change the declaration of data: struct utmp data; and every place you now use data, use its address instead: while ((fread (&data, sizeof(struct utmp), 1, f)) > 0) { ... putchar (data.ut_line[i]); ... printf ("%.8s %.8s %ld\n", data.ut_name, data.ut_line, data.ut_time); ... Also notice the %.8s -- this defends against the field being completely full. In that case it would not have a null terminator.
andrew@grkermi.UUCP (Andrew W. Rogers) (08/10/85)
In article <117@graffiti.UUCP> bruce@graffiti.UUCP (Bruce Jilek) writes: >Why does printf insist that data->ut_line is a null string while >putchar can display all of the characters of this array? > > char ut_line[8]; /* tty name */ > ... > for (i = 0; i <= 7; i++) { > putchar(data->ut_line[i]); > printf("\n"); > printf("%s %s %ld\n", data->ut_name, > data->ut_line, data->ut_time); > ... > >Sample output: > > tty03 /* This shows that data->ut_line isn't null */ >bruce (null) 492299400 /* So what's the problem in this line? */ Are you *sure* it really isn't null? If the first character were \0, your 'putchar' loop would give no indication of that fact; the \0 would be invisible and the remaining characters would print normally. Of course, 'printf' would consider the string null. Try replacing the 'putchar...' with 'printf("\\%03o ", data->ut_line[i]);' and see what the characters in the string *really* are. Either that, or redirect the output to a file and get a binary/octal/hex dump of it. AWR
levy@ttrdc.UUCP (Daniel R. Levy) (08/10/85)
bruce@graffiti.UUCP (Bruce Jilek) <117@graffiti.UUCP>: > >Why does printf insist that data->ut_line is a null string while >putchar can display all of the characters of this array? >... > struct utmp { /* This structure is straight out of utmp.h */ > char ut_line[8]; /* tty name */ > char ut_name[8]; /* user id */ > long ut_time; /* time on */ > }; >... > for (i = 0; i <= 7; i++) { > putchar(data->ut_line[i]); > } > printf("\n"); > printf("%s %s %ld\n", data->ut_name, > data->ut_line, data->ut_time); >... printf expects strings which are null-terminated at the end. There is no guarantee that what you get in the arrays in struct utmp will be this way. Ergo, strange results. Putchar is the way to go unless you want to copy the data out of the struct utmp into something which is null terminated for the sake of printf. -- ------------------------------- Disclaimer: The views contained herein are | dan levy | yvel nad | my own and are not at all those of my em- | an engihacker @ | ployer, my pets, my plants, my boss, or the | at&t computer systems division | s.a. of any computer upon which I may hack. | skokie, illinois | | "go for it" | Path: ..!ihnp4!ttrdc!levy -------------------------------- or: ..!ihnp4!iheds!ttbcad!levy
guy@sun.uucp (Guy Harris) (08/11/85)
> > > >Sample output: > > > > tty03 /* This shows that data->ut_line isn't null */ > >bruce (null) 492299400 /* So what's the problem in this line? */ > Are you *sure* it really isn't null? Yes, he's sure; "printf" prints "(null)" if the pointer it's handed is itself null, not if it points to a null string. If the code posted was the entire program, and not just a fragment, the problem is, as Chris Torek pointed out, that the guy declared a pointer to the "utmp" structure, and used it, without making it point to anything. He should just have declared a structure and used it directly. Confusion about structures and pointers to them seems to be common. struct foo { ... } bar; struct foo *bletch = &bar; <code using "bletch"> is not necessarily better than struct foo { ... } bar; <code using "bar" directly> and is more obscure (since the pointer isn't necessary, and the person reading the code may waste time figuring out that fact). It may have some benefit if 1) "bletch" is in a register, 2) you have addressing modes like "short_displacement(Rn)", and 3) you *don't* have addressing modes like "short_displacement(SP)" or "short_displacement(FP)" or the displacement of the members of the structure from the SP or FP isn't "short". However, this is uncommon (the PDP-11, VAX, M68000, Z8000 and CCI Power 6 families use a general register as FP/SP, so it makes no difference; the NS32xxx family supports the use of "displacement(FP)" in the same way it supports "displacement(Rn)"; I suspect the WE32xxx family, and most of the other machines out there do one or the other also; the 80*86 family looks like it can with the proper choice of register to implement FP), and I'd be surprised if it made enough difference to make the obscure code (which isn't likely to run any faster on the machines mentioned above) worth it. Nevertheless, I've seen this kind of thing often enough that I suspect it's due to misunderstanding of C. I've also seen code like that in the original article, where the user seems to be under the impression that declaring a pointer automatically declares something for it to point to *and* sets it to point to that - I've seen it less, though, because it simply doesn't work. Guy Harris
roger@ll-sst (Roger Hale) (08/12/85)
> In article <117@graffiti.UUCP> bruce@graffiti.UUCP (Bruce Jilek) writes: > >Why does printf insist that data->ut_line is a null string while > >putchar can display all of the characters of this array? > > ... [ > main() { > struct { char ut_line[8]; ... } *data; ... /* in effect */ ] > } > > > >Sample output: > > > > tty03 /* This shows that data->ut_line isn't null */ > >bruce (null) 492299400 /* So what's the problem in this line? */ > > Are you *sure* it really isn't null? If the first character were \0, your > 'putchar' loop would give no indication of that fact; the \0 would be > invisible and the remaining characters would print normally. Of course, > 'printf' would consider the string null. > ... > AWR [I tried sending to graffiti!bruce@Berkeley.ARPA, but no luck.] In fact data->ut_line is a null \emphasis{pointer}, since printf is recognizing a null string pointer for %s and printing '(null)' instead. This check is in doprnt.s in our V7 PDP11 system, though not in 4.2bsd's. Data->ut_line is null because data is null and ut_line is at offset zero; data is null by accident because this is your first time at this stack level and the stack happens to start out zero filled. If any of this were different it would have printed 'tty03' instead, leaving no clue that *data had been trashed. All this is Murphy-dependent; be glad the symptoms were so clear! Your servant, Roger Hale <roger@ll-sst.arpa> (sorry, no usenet address)
peter@utah-gr.UUCP (Peter S. Ford) (08/13/85)
The declaration of f() { > struct foo { ... > } bar; > struct foo *bletch = &bar; ... } is very useful when the structure needs to be passed around as an argument to functions, and the life of the storage is for the length of the current procedure invocation. This does reveal my preference for passing a variable which is a pointer to the structure "qqsv(bletch);" rather than "qqsv(&bar);".
hosking@convexs.UUCP (08/15/85)
Among other things, it looks like you never allocate space for "data" in your program. You have a pointer, but it doesn't point to anything. On a Convex C-1, this leads to a core dump when "fread" tries to do a "bcopy" with a null pointer.... which is invalid as a user address. On VAXen and other machines, null pointers are valid user addresses, which can make finding such problems a lot more difficult. One of the biggest problems in porting 4.2 to a Convex C-1 was exactly this problem... programs tried to use pointers which had never been initialized. Hint: Learn about the program "lint" and use it regularly. It might have saved you a lot of work. Doug Hosking Convex Computer Corp. Richardson, TX
anton@ucbvax.ARPA (Jeff Anton) (08/16/85)
In article <353@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes: >bruce@graffiti.UUCP (Bruce Jilek) <117@graffiti.UUCP>: >> >>Why does printf insist that data->ut_line is a null string while >>putchar can display all of the characters of this array? >>... >> struct utmp { /* This structure is straight out of utmp.h */ >> char ut_line[8]; /* tty name */ >> char ut_name[8]; /* user id */ >> long ut_time; /* time on */ >> }; >>... >> for (i = 0; i <= 7; i++) { >> putchar(data->ut_line[i]); >> } >> printf("\n"); >> printf("%s %s %ld\n", data->ut_name, >> data->ut_line, data->ut_time); /* how about */ printf("%.8s\t%.8s\t%ld\n", .... >>... > >printf expects strings which are null-terminated at the end. There is no >guarantee that what you get in the arrays in struct utmp will be this way. >Ergo, strange results. Putchar is the way to go unless you want to copy >the data out of the struct utmp into something which is null terminated for >the sake of printf. Not really, you are forgetting that the format "%.8s" will cause printf to print up to a null or 8 chars max. Unfortunately this style of printf can not be used with sizeof for compile time format changes without run time code support. -- C knows no bounds. Jeff Anton U.C.Berkeley ucbvax!anton anton@berkeley.ARPA
guy@sun.uucp (Guy Harris) (08/17/85)
> Not really, you are forgetting that the format "%.8s" will > cause printf to print up to a null or 8 chars max. Unfortunately > this style of printf can not be used with sizeof for compile time > format changes without run time code support. True, you can't have the compiler generate the "printf" string, but you can do printf("%.*s", sizeof(thing), thing); since you can use "*" for a field width or precision and pick up the value from the argument list. Guy Harris
chris@umcp-cs.UUCP (Chris Torek) (08/18/85)
>... you are forgetting that the format "%.8s" will cause printf to >print up to a null or 8 chars max. Unfortunately this style of >printf can not be used with sizeof for compile time format changes >without run time code support. Well, you can always use: printf("%.*s", sizeof (utmp.ut_name), utmp.ut_name); or if you want exactly sizeof (utmp.ut_name) characters: #define NMAX (sizeof (utmp.ut_name)) printf("%-*.*s", NMAX, NMAX, utmp.ut_name); (The "-" gives "left adjustment"---blank filling at the right.) -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251) UUCP: seismo!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@maryland
guy@sun.uucp (Guy Harris) (08/20/85)
> You have a pointer, but it doesn't point to anything. On > a Convex C-1, this leads to a core dump when "fread" tries to do a "bcopy" > with a null pointer.... which is invalid as a user address. On VAXen and > other machines, null pointers are valid user addresses, which can make > finding such problems a lot more difficult. On several other machines, null pointers are not valid user addresses; Suns and CCI Power 5/20s come to mind (not surprisingly). On VAXes running VMS, null pointers are not valid user addresses; on VAXes running System V Release 2 Version 2, or 4.2BSD with John Bruner's changes, there's a loader option to build executable images where null pointers aren't valid. Use these options - they can catch problems before you have to move your code to a machine where you have no alternative (besides, the chances are non-negligible that your code has a problem even if you can dereference a null pointer). > Hint: Learn about the program "lint" and use it regularly. It might have > saved you a lot of work. Amen. MANY program bugs (even ones in the kernel - I can testify to this) can be caught simply by using "lint". There may be some things that "you just can't get lint to shut up about", but better too many complaints than too few... Guy Harris