Skip to content
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

Merged
merged 2 commits into from
Jul 25, 2020
Merged

Add 13, 17 #2

merged 2 commits into from
Jul 25, 2020

Conversation

codybartfast
Copy link
Owner

No description provided.

ch5/ex-5-13-tail.c Outdated Show resolved Hide resolved
#define INITIAL_BUFFSIZE (1 << 10)
#define DEFAULT_TAILSIZE 10

enum { OK, ERROR };

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.

Copy link
Owner Author

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.

Comment on lines 195 to 214
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;
}

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.

Copy link
Owner Author

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.

Comment on lines 137 to 140
/* copy info about current buffer before it's replaced */
char *pbuff = buff, *pcursor = cursor, *pbufflimit = bufflimit;
char *pfirstchr = *firstln;
ptrdiff_t pfirst2limit = pbufflimit - pfirstchr;

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.

Copy link
Owner Author

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 :-)

if (prevc == '\n') {
if (++currln == lineslimit)
currln = lines;
*currln = cursor;

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?

Copy link
Owner Author

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;

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?

Copy link
Owner Author

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.

Comment on lines +82 to +85
sortidxs = malloc(maxsortcount * sizeof(int));
compares = malloc(maxsortcount * sizeof(CHARCOMP));
folds = malloc(maxsortcount * sizeof(int));
dirsorts = malloc(maxsortcount * sizeof(int));

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 mallocs fail?

Copy link
Owner Author

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;

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?

Copy link
Owner Author

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') {

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?

Copy link
Owner Author

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++) {
Copy link

@klampworks klampworks May 10, 2020

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 \0s would interfere with that program.

Copy link
Owner Author

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[])

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.

Copy link
Owner Author

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[])

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?

Copy link
Owner Author

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.

Comment on lines +135 to +136
char **tmpfields = malloc((maxsortidx + 2) * sizeof(char *));
char **sortdata = malloc((nsortflds + 2) * sizeof(char *));

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.

Copy link
Owner Author

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;

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)

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);

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?

Copy link
Owner Author

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.

Comment on lines +201 to +207
int numcmp(char *s1, char *s2)
{
double v1 = atof(s1);
double v2 = atof(s2);

return v1 < v2 ? -1 : v1 > v2 ? 1 : 0;
}

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.

Copy link
Owner Author

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.

This was referenced May 11, 2020
int main(int argc, char **argv)
{
if (settailsize(argc, argv) != OK)
return 0;
Copy link
Collaborator

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?

Copy link
Owner Author

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.

Comment on lines 184 to 186
char c, *ptr;
unsigned int d;
unsigned long n = 0;
Copy link
Collaborator

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.

Copy link
Collaborator

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.)

Copy link
Owner Author

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 ;-)

return OK;
}

int settailsize(int argc, char **argv)
Copy link
Collaborator

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.

Copy link
Owner Author

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.

@codybartfast codybartfast merged commit efd2395 into fake-master Jul 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants