This is a very good effort, but, I am sorry to say, I was able to
understand it eventually.
No need to apologize. I'm glad that you eventually understood it
without any comments to follow along with. If it had been a more
instructive example, it would have included comments.
You might want to study the code posted by
Stefan Ram.
I studied it before typing my code.
His code walks to the end of the string via 'strlen' and then walks
backwards to the first non-space. My code does not walk over elements
of the original string twice until the 'memcpy'.
His code does not return a trimmed copy of the original string. Mine
attempts to, akin to the OP's Python reference, which the OP apparently
enjoys.
His code does not accept a set of characters to be used for trimming the
right side of a string. Mine attempts to, akin to the OP's Python
reference, which the OP apparently enjoys.
He is undoubtedly ahead of you in matters of pure layout,
What do you mean? Stylistically? In regards to program flow? In
regards to object usage? His program and mine do different things, so...
but your use of a little-used statement in a position where it is both
suggestive and pointless is a stoke of genius!
Are you referring to the keyword (not statement) 'register'? If so,
it's a storage-class specifier. While it suggests that access to
so-specified objects "be as fast as possible," it also ensures that the
address of a so-specified object cannot be taken. In this fashion, it
is used in my code to prevent accidentally taking the address of the
corresponding objects. I believe that this is similar to how 'const'
can be removed from a well-behaved program without changing the
behaviour. Am I mistaken?
If you want to develop your own layout further, I'd humbly suggest that
indenting a closing } by half the indent used for the body could be
improved. Yes, there's nothing to line the } up with visually, but an
indent of 2 is just about enough for the human eye to follow without
that clue.
If you don't like it, I'm sorry about that. That's currently my
preference and maybe that will change some day. My rationale is that
any continuation of a statement be indented further than the beginning
line of the statement, as I think you noticed. I also sometimes do:
int ok;
int ok = (
condition1 &&
condition2 &&
condition3 ||
condition4
);
if (ok)
foo();
else
bar();
instead of:
if (condition1 && condition2 &&
condition3 || condition4) {
foo();
}
else {
bar();
}
Whose 'else' is that, anyway?
If the conditions are someday changed, the 'diff' for the former looks a
bit different than the 'diff' of the latter. I enjoy the former more.
As for the semantics, I thought the idea of keeping "one_past_end" one
greater than it needs to be (thereby toying, teasingly, with UB all the
time) was a very nice touch -- it's always nice to see a '+ 2' in a
string-walking loop.
Why is it one greater than it needs to be? If 'c' is not the null
terminator, then one past the end of the string is at least two
characters away. That distance seems useful for the 'malloc'.
And what can I say about the omission of #include
<ctype.h>? I had to check the standard so see if the implied
declaration, together with the default argument promotions, made the
code valid. A masterful flourish.
Actually, it was absolutely unintentional. That's what I get for
hastily pasting code at 3:52 am. Thank you for pointing that out. I'm
glad that it worked out anyway.
Some things are low-blows, though. Did you really think anyone would be
confused by the fact that only one of the parameters to rstrip is const?
Shame on you! The register declarations hide it, but it's a fair bet
the people won't even notice!
Another unintentional. At one point I had the 'const' in, but was
trying to save objects and considered re-purposing 'string', so I took
it out. I forgot to put it back in. Thank you for pointing that out,
too. Fortunately, 'const' and 'register' can be removed from the
entirety, but can be used to prevent accidents... Except for forgetting
to use them.
Why didn't you point out the redundancy of my 'continue's? Doesn't it
seem like another "extra" like 'register' and 'const'?
I originally started writing the whole thing as a 'while' with no body
but a complex controlling expression. Then I thought it'd be clearer
without that.
Here is the corrected code:
/* Also available at
http://ideone.com/Y5sz6 */
#include <stddef.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static int in_set(
register const int c,
register const char * chars
) {
register int test;
while ((test = *chars)) {
if (c == test)
return 1;
++chars;
continue;
}
return 0;
}
char * rstrip(
register const char * const string,
register const char * const chars
) {
register char c;
register const char * cur_pos = string;
register const char * one_past_end = string + 1;
register ptrdiff_t diff;
register char * copy;
if (!chars) {
while ((c = *cur_pos)) {
if (!isspace(c))
one_past_end = cur_pos + 2;
++cur_pos;
continue;
}
} else {
while ((c = *cur_pos)) {
if (!in_set(c, chars))
one_past_end = cur_pos + 2;
++cur_pos;
continue;
}
}
diff = one_past_end - string;
copy = malloc(diff);
if (!copy)
return NULL;
--diff;
copy[diff] = '\0';
return memcpy(copy, string, diff);
}
int main(void) {
char *tests[] = {
"",
" ",
" ",
"f",
" f",
" f",
" f ",
" f ",
"f ",
"f ",
"foo bar baz",
" foo bar baz",
" foo bar baz",
" foo bar baz ",
" foo bar baz ",
"foo bar baz ",
"foo bar baz ",
"mississippi",
};
int i;
char * stripped;
for (i = 0; i < (sizeof tests / sizeof *tests) - 1; ++i) {
stripped = rstrip(tests
, NULL);
if (stripped)
printf("[%s]\n", stripped);
free(stripped);
continue;
}
stripped = rstrip(tests, "ipz");
if (stripped)
printf("[%s]\n", stripped);
free(stripped);
return EXIT_SUCCESS;
}