-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add 13, 17 #2
Add 13, 17 #2
Conversation
ch5/ex-5-13-tail.c
Outdated
#define INITIAL_BUFFSIZE (1 << 10) | ||
#define DEFAULT_TAILSIZE 10 | ||
|
||
enum { OK, ERROR }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these values are being compared directly to 0
it might be a good idea to make it explicit like this enum { OK=0, ERROR };
.
There is also stdbool.h
which defines true
and false
if that appeals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think they are compared to 0
in this case, but some random idiot™ might come along and assume they can be - will change.
stdbool's not in the toolbox yet.
ch5/ex-5-13-tail.c
Outdated
while ((c = *ptr++) != '\0') { | ||
if (!isdigit(c)) { | ||
printf("error: Expected decimal integer: %s\n", | ||
argv[1] + 1); | ||
return ERROR; | ||
} | ||
if (n > (max / 10)) { | ||
printf("error: Argument too large, max: %lu\n", | ||
max); | ||
return ERROR; | ||
} | ||
n *= 10; | ||
d = (c - '0'); | ||
if (d > max - n) { | ||
printf("error: Argument too large, max: %lu\n", | ||
max); | ||
return ERROR; | ||
} | ||
n += d; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a standard library function atoi
which does this too but I think this is ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a couple of concerns about atoi:
- I think it's only an int and I was going trying to go overboard on supporting "unreasonable" input with ulong.
- itoa might produce unexpected results if the string value were greater than the max for the type.
ch5/ex-5-13-tail.c
Outdated
/* copy info about current buffer before it's replaced */ | ||
char *pbuff = buff, *pcursor = cursor, *pbufflimit = bufflimit; | ||
char *pfirstchr = *firstln; | ||
ptrdiff_t pfirst2limit = pbufflimit - pfirstchr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe consider have one pointer and then making the other variables int
offsets instead.
I think it will make this function much simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Orignally I thought I was saving a bunch of pointer arithmatic in the char loop. But now you make me think about it... if everything is an offset then most of the time offsets can be compared directly with each other. The tiny bit of extra pointer arithmatic in the read char loop would, presumably, have virtually no overhead on a modern processor, but I do have one customer from Belgium who is already expressing concerns about performance :-)
ch5/ex-5-13-tail.c
Outdated
if (prevc == '\n') { | ||
if (++currln == lineslimit) | ||
currln = lines; | ||
*currln = cursor; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the contents of *currln
is not used?
Does removing this line change anything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need this. This is the only line that modifies the value of the line pointers (offsets?).
void parseargs(int argc, char *argv[]) | ||
{ | ||
int i; | ||
int maxsortcount = argc - 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would happen if argc
is 0
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh it'll break :-)
Tail is the only exercise so far that talked about handling 'unreasonable' input. Without that requirement I'm back in cowboy mode. I did start writing a check for 0
but then thought either check input properly or not at all, and doing it properly for this execice would be a lot of work, there are lot of ways to break it.
sortidxs = malloc(maxsortcount * sizeof(int)); | ||
compares = malloc(maxsortcount * sizeof(CHARCOMP)); | ||
folds = malloc(maxsortcount * sizeof(int)); | ||
dirsorts = malloc(maxsortcount * sizeof(int)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would happen if these malloc
s fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doh! 🤦
else if (reverse) | ||
compare = strcmpr; | ||
else | ||
compare = (CHARCOMP)strcmp; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need this cast? Does removing it cause an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing it causes a compiler warning. (char *, char *) vs (const char *, const char *). The natural solution is probably to make all the compare signatures (const char *, ...), but I don't think that int the knr toolbox.
field = fieldspace; | ||
fieldidx = 0; | ||
for (chr = fieldspace; *chr; chr++) { | ||
if (*chr == '\t' || *chr == '\n') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will happen if the last field does not end with \t
or \n
?
It looks like it will get missed out.
Is that a concern or just user error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just user error. "Check all input or check none".
/* split line into fields and create temporary pointer for each field */ | ||
field = fieldspace; | ||
fieldidx = 0; | ||
for (chr = fieldspace; *chr; chr++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assumes the input is a \0
terminated string but where is it coming from?
It looks like the readlines
function from tail
does not \0
terminate the string, but on the other hand I can see how \0
s would interfere with that program.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry my bad, I didn't include lines.c
. Readlines is similar to tail, but it does add a \0
. Yes tail doesn't need '\0', which, iirc, was refactor #57 once I realised this.
return sortdata; | ||
} | ||
|
||
char *line(char *sortdata[]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used? Consider adding static
to functions you only intend to use in this C file so the compiler can tell you it's unused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is used, (line 58) to get the original line back from the sortdata after the sort.
Ah! I think I did try making some functions static but got the error about them being undefined - didn't read the error properly so removed the static
again :-(
As a general principle I'm guessing if you can make file-level functions and values static you should? (Not in functions and blocks).
return sortdata[nsortflds]; | ||
} | ||
|
||
char *fieldspace(char *sortdata[]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function doesn't appear to be used either?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used by free in freestuff at end.
char **tmpfields = malloc((maxsortidx + 2) * sizeof(char *)); | ||
char **sortdata = malloc((nsortflds + 2) * sizeof(char *)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these need to be **
? You can use &
to get the address of a variable at any time, you don't necessarily need use the maximum number of stars in the declaration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going to have to think about this one...
int main(int argc, char *argv[]) | ||
{ | ||
int i, nlines; | ||
char *buff, **lines, ***linedata; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think these need to be **
and ***
either.
return numcmp(s2, s1); | ||
} | ||
|
||
void freestuff(char ***linedata, int nlines) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
freestuff
? I didn't know you were communist.
for (i = 0; i < nlines; i++) | ||
linedata[i] = getsortdata(lines[i]); | ||
|
||
quicksort((void **)linedata, 0, nlines - 1, (VOIDCOMP)compare); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this cast is needed either. Does removing (VOIDCOMP)
cause an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just avoids a compiler warning.
int numcmp(char *s1, char *s2) | ||
{ | ||
double v1 = atof(s1); | ||
double v2 = atof(s2); | ||
|
||
return v1 < v2 ? -1 : v1 > v2 ? 1 : 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine.
But if you wanted to convert the floats outside the function, you could change it to
int numcmp(void *s1, void *s2)
{
double v1 = *(double*)s1;
double v2 = *(double*)s2;
...
And the strcmpr
version would be the same but with char
instead of double
.
That way the two functions would have the same signature and be assigned to the same function pointer.
Bypassing the type system when it gets in the way like this is called type erasure in C and is considered very cool.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definately going to have to think about/play with that one.
ch5/ex-5-13-tail.c
Outdated
int main(int argc, char **argv) | ||
{ | ||
if (settailsize(argc, argv) != OK) | ||
return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered returning a non-0 error code in case things go wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point in the book we're not explicitly returning values from main, I'm just doing it shut the compiler up. But since I am making it explicit it wouldn't do any harm to make it meaningful.
ch5/ex-5-13-tail.c
Outdated
char c, *ptr; | ||
unsigned int d; | ||
unsigned long n = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More descriptive variable names might might your code easier to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a related note: For low-level code I think specifically writing out the loop invariants might help both to reduce the number of bugs, and to understand the code when you return to it. (Interested to hear what @klampworks thinks about this.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. My defense is that the book itself has very terse names. In particlar 'c' is always the name used for a single char. Also 8 space tabs! There's no room left on the screen for 2 letter var names ;-)
ch5/ex-5-13-tail.c
Outdated
return OK; | ||
} | ||
|
||
int settailsize(int argc, char **argv) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think parsearguments
would be a better name to explain what this function does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was parseargs, then I changed it becasuse I was didn't like a funciton called 'parse...' having side effects. But given the whole program is constructed on the solid foundation of mutating shared state I should just go with the flow.
No description provided.