Bug: String escaping
In C, "foo \" bar"
is a legal string, but it will confuse the state of your program (so " bar"
will be seen as not in a string, while everything after the end of the string will be seen as inside a string). You need to handle at least the escape sequence \"
within strings.
Bug: Comments as token separators
If you have the (admittedly weird) C:
int/**/bar;
That will parse as int
, bar
, ;
(declaration of an integer called bar). With comments removed as by this program, it will come out as:
intbar;
Which is probably a parse error.
Bug: potentially out-of-bounds access through arr[1]
c
should be checked to make sure it's greater than 1 before accessing through arr[1]
.
Suggestion: State machine
The above-mentioned bug is part of a larger issue: there's a lot of "hidden" state to do with things like "what was the previous character" etc.
The nice thing about most programming languages (including C) is that they are designed so that lexical analysis (which is what you need to do to remove comments) is very simple, and can be done with a finite-state automaton (often just called a state machine).
In some sense your program is already a finite-state machine, with the state being the combination of the String
flag, the prev
variable, and which function you're in---but it's a little hard to keep track of. Introducing an explicit state machine will make the code clearer, and make it easier to handle cases like escaped quotes in strings.
There are various implementation patterns for state machines; a common one for small/simple state machines, such as this one, is to use an enum to name some states:
enum { START, COMMENT_START, COMMENT, COMMENT_END, LINE_COMMENT, STRING, STRING_ESCAPE, };
and then use a switch statement to behave differently depending on the state:
int state = START;
...
switch (state) {
case START:
if (c == '/') state = COMMENT_START;
/* other branches */
break;
case COMMENT_START:
if (c == '*') state = COMMENT;
else if (c == '/') state = LINE_COMMENT;
/* other branches */
break;
.
. /* etc */
.
}
Convention: standard names for arguments to main
The almost universal convention is for the signature of main to be
int main(int argc, char *argv[])
There isn't anything inherently wrong with using different names for those variables, but using argc
/argv
makes it slightly easier for other people to read.
Pedantry: whitespace
Whitespace around punctuation seems a little...haphazard. Regardless of which style you prefer (which is an opinion thing), the inconsistency makes it more difficult to read.
Feature: not-in-place modification
A useful behaviour (and the behaviour I'd expect) would be to be able to call
commentremover foo.c
and have it print the comment-removed version of the program to STDOUT, without clobbering foo.c
.