4

I am very much a c newbie and I am learning by following CS107 videos from Standford (I am not a Student there).

Links are below if anyone is interested

Looking at the below implementation of strtok, I am not sure why the first if statement is written this way: if (s == NULL && ((s = p) == NULL))

char *strtok(char *s, const char *sep)
{
    static char *p = NULL;

    if (s == NULL && ((s = p) == NULL))
        return NULL;
    s += strspn(s, sep);
    if (!*s)
        return p = NULL;
    p = s + strcspn(s, sep);
    if (*p)
        *p++ = '\0';
    else 
        p = NULL;
    return s;
}

Here's what I have identified so far:

1) the static p is a local static so that it persists once a token is found, and reset to NULL when no more are found

2) subsequent calls of strtoken pass in s as NULL

Therefore wouldn't the first check be the same if it were written as:

if (s == NULL && p == NULL)

Or is there a reason it is written the way it is?

Finally: return p = NULL; is this just a shorthand for:

p = NULL;
return p;

CS107 Winter 18 Screencasts

Amazing CS107 Lectures from 2011

An Rpi based CS107e

Sam Hammamy
  • 10,819
  • 10
  • 56
  • 94
  • 1
    If you're just learning C try and stay away from doing assignments inside of `if` conditions. – tadman Feb 22 '18 at 03:09
  • I agree, I have good `python` experience and I have not seen or used that very much in `python` -- which is why it was intimidating when I first read this implementation. – Sam Hammamy Feb 22 '18 at 03:29
  • 1
    This is some unusually ugly code even by C standards, so the author of this code was certainly not aiming for clarity. – tadman Feb 22 '18 at 03:38

2 Answers2

4
if (s == NULL && ((s = p) == NULL))

is not really the same as

if (s == NULL && p == NULL)

although they have the same result, when s and p are NULL.

You've identified correctly that subsequent calls of strtok must be passed with NULL, however if the are still tokens to be returned, p is not going to test for NULL.

So the ((s = p) == NULL) is a clever trick to assign s to point to the rest of the string where more token might be found.

In C when you evaluate an expression like A && B, A gets evaluated and B gets only evaluated if and only if A was evaluated as true, the reason for this are sequent points.

If s is not NULL, then ((s = p) == NULL) is never evaluated, s = p is never assigned.

If s is NULL, then ((s = p) == NULL) gets evaluated. First s = p is evaluated and this is an assignment. If p is not NULL, then s points to where p is pointing (the rest of the source) and when compared to NULL, it is evaluated to false, thus the return NULL is not executed and the function continues looking for tokens.

If however p is also NULL, s is set to point to NULL and when compared to NULL it evaluates to true, the whole if evaluates to true and return NULL is executed. When s initially is NULL and p is NULL, then that means that all token have been found and return, so the function should return NULL. It can be rewritten as:

if(s == NULL)
{
    if(p == NULL)
        return NULL;

    s = p;
}
...

Note that the value of an assignment is the value of the assigment itself.

return a = b;

is the same as doing

a = b;
return b;
Pablo
  • 13,271
  • 4
  • 39
  • 59
  • Great answer! Here's what I have now: On the first call `s += strspn(s, sep); `: the `s` pointer will move by `1` if `sep` is found. Then `p = s + strcspn(s, sep);` will set `p` to just after the separator in `s`. So second call `p` is not null. But in second call `s` is set to `p` which was set in the previous call. So `s += strspn(s, sep); ` is really `p += strspn(p, sep); ` because as you said, `s=p` happens during the second call when `s` is ` NULL`; – Sam Hammamy Feb 22 '18 at 03:22
1

Therefore wouldn't the first check be the same if it were written as:

if (s == NULL && p == NULL)

When p is NULL, yes. When p is not NULL, no. Because then the side effect of s=p doesn't happen. And this value s is used later when the if statement is false.

Finally: return p = NULL; is this just a shorthand for:

p = NULL;
return p;

Yes.

MFisherKDX
  • 2,840
  • 3
  • 14
  • 25