You mention being fluent in Pascal, and perhaps that is where some of your style comes from. I have some suggestions unrelated to the actual subject of your question. Consider this snippet of your code:
Code:
/* Return a token from the line if there's at least one. Otherwise, set the stating topint to -1 */
const char *a_token_from(const char line[], int * start)
{
Remember that code is for humans.
The declaration style here is very dense, and even with code highlighting (which rarely works in a useful way) it doesn't get much less dense.
Here are some things that readers want from a declaration or definition:
1. They want to be able to find the declaration or definition. "Take me to where this function is defined/declared."
2. They want to know the order of the parameters.
3. They want to know the return type.
4. They want to know the name of the function.
5. They want to know the exact type of one of the parameters.
6. They want to know the expectations about the parameters.
7. They want to know the behavior of the function.
The style you are using is optimized for answering question #3, at the expense of pretty much everything else.
I suggest you adopt a more readable style, with some objective rules that provide measurably better results:
- Put the return type on a separate line.
- Put any non-return-type specifiers on another separate line.
- Use macros to replace keywords with semantic labels ("PRIVATE" vs. "static")
- Put the function name at the leftmost column of the line (zero or one, depending on how you count), so that grep "^name" will find it.
- Put parameters on separate lines.
- Indent the parameters so they are distinct from the function name.
Here's an example:
Code:
/* Return a token from the line if there's at least one. Otherwise, set the stating topint to -1
*/
static
const char * // #3
a_token_from( // #1, #4
const char line[], // #2, #5
int * start)
{
Notice this addresses #1..#5 nicely. It does not address #6 or #7, but you can do that by expanding your header comment, or adding extra structure to the parameter declarations, or both.
Now, with that said, let's look for bugs! Oh, here's one:
Code:
int main(void)
{
char line[MAX];
while (fgets(line, MAX, stdin) != NULL)
{
puts("Please type the string to tokenize.");
tokenize(line);
}
return 0;
}
This reads as "get a line of input, then ask the user for input, then tokenize the input."
What you probably want is "ask the user, then get the line, then tokenize".
Being fluent in Pascal, you know about repeat ... until but in C that is called
do ... while. Worse, the loop depends on the input, which is only available in the
middle of the function. So really it's something like loop ... if no input break; ... forever.
There's no C (or Pascal, IIRC) syntax for that. We have to jumble up a bunch of control keywords,
or "unroll" part of the loop in order to get what we want.
Code:
char line[MAX];
puts("Please type the string to tokenize.");
while (fgets(line, MAX, stdin) != NULL)
{
tokenize(line);
puts("Please type the string to tokenize.");
}
Observing that the prompt string never changes, and that you never use
line except to pass it as a parameter, we can easily convert the prompt-and-get-input
into a single function call (which you can rename to something shorter!):
Code:
char *
prompt_and_read_line(
const char *prompt)
{
static char buffer[MAX];
fputs(prompt, stdout);
return fgets(buffer, sizeof (buffer), stdin);
}
int
main(void)
{
const char *msg = "Please type the string to tokenize: ";
char * line;
while ((line = prompt_and_read_line(msg)) != NULL)
tokenize(line);
}