我正在尝试创建函数delete_comments()
。给出了CCD_ 2和CCD_。
实现函数
char *delete_comments(char *input)
,从存储在输入处的程序中删除C注释。输入变量指向动态分配的内存。该函数返回指向已抛光程序的指针。您可以为输出分配一个新的内存块,也可以直接在输入缓冲区中修改内容。您需要处理两种类型的评论:
由
/*
和*/
分隔的传统块注释。这些注释可能跨越多行。您应该只删除从/*
开始到*/
结束的字符,例如,保留以下任何换行符不变。行注释从
//
开始直到换行符。在这种情况下,换行符也必须删除。调用
delete_comments()
的函数只处理来自delete_comments()
的返回指针。它不为任何指针分配内存。实现delete_comments()
函数的一种方法是为目的字符串分配内存。但是,如果分配了新的内存,则输入中的原始内存必须在使用后释放。
我很难理解为什么我当前的方法是错误的,或者我得到奇怪输出的具体问题是什么。我试图创建一个新的数组来处理这个问题,在这个数组中使用新规则复制输入字符串。
#include "source.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* Remove C comments from the program stored in memory block <input>.
* Returns pointer to code after removal of comments.
* Calling code is responsible of freeing only the memory block returned by
* the function.
*/
char *delete_comments(char *input)
{
input = malloc(strlen(input) * sizeof (char));
char *secondarray = malloc(strlen(input) * sizeof (char));
int x, y = 0;
for (x = 0, y = 0; input[x] != ' '; x++) {
if ((input[x] == '/') && (input[x + 1] == '*')) {
int i = 0;
while ((input[x + i] != '*') && (input[x + i + 1] != '/')) {
y++;
i++;
}
}
else if ((input[x] == '/') && (input[x + 1] == '/')) {
int j = 0;
while (input[x + j] != 'n') {
y++;
j++;
}
}
else {
secondarray[x] = input[y];
y++;
}
}
return secondarray;
}
/* Read given file <filename> to dynamically allocated memory.
* Return pointer to the allocated memory with file content, or
* NULL on errors.
*/
char *read_file(const char *filename)
{
FILE *f = fopen(filename, "r");
if (!f)
return NULL;
char *buf = NULL;
unsigned int count = 0;
const unsigned int ReadBlock = 100;
unsigned int n;
do {
buf = realloc(buf, count + ReadBlock + 1);
n = fread(buf + count, 1, ReadBlock, f);
count += n;
} while (n == ReadBlock);
buf[count] = 0;
return buf;
}
int main(void)
{
char *code = read_file("testfile.c");
if (!code) {
printf("No code read");
return -1;
}
printf("-- Original:n");
fputs(code, stdout);
code = delete_comments(code);
printf("-- Comments removed:n");
fputs(code, stdout);
free(code);
}
您的程序存在根本问题。
-
它无法标记输入。注释起始序列可以出现在字符串文字中,在这种情况下,它们不表示注释:
"/* not a comment"
。 -
你有一些基本的错误:
if ((input[x] == '/') && (input[x + 1] == '*')) { int i = 0; while ((input[x + i] != '*') && (input[x + i + 1] != '/')) { y++; i++; } }
这里,当我们进入循环时,使用
i = 0
,input + x
仍然指向开口/
。我们没有跳过开头的*
,已经在寻找结尾的*
。这意味着序列/*/
将被识别为一个完整的注释,而事实并非如此。这个循环还假设每个
read_file()
0注释都是正确关闭的。它不是在检查可以终止输入的null字符,所以如果注释没有关闭,它将超出缓冲区的末尾。 -
C有换行符。在ISO C转换阶段2中,删除所有间隙换行序列,将一条或多条物理线转换为逻辑线。这意味着
//
注释可以跨越多个物理行:// this is an extended comment
顺便说一句,您可以看到StackOverflow用于语法高亮显示的自动语言检测器正在做到这一点!
行的延续是独立于标记化的,这要到翻译阶段3才会发生。意思是:
/ / this is an extended comment
这一点击败了StackOverflow的语法高亮显示。此外,行延续可以在任何令牌中发生,可能发生多次:
" this is a string literal "
如果你真的想让它100%正确地工作,你需要解析输入。通过";解析";我指的是一个更正式、更严格的检测程序,它可以理解它在读什么,在读它的上下文中
例如,在许多情况下,此代码可能会失败。
printf("答案是%d//%d\n",a,b);
可能会绊倒您的CCD_ 22检测并剥去CCD_ 23的末端。
有两种解决上述问题的通用方法:
-
找到每个可以使用注释类字符的角落案例,并在剥离之前编写条件语句以避免它们。
-
完全解析语言,这样你就会知道你是否在一个字符串或其他包装注释类字符的上下文中,或者你是否在顶级上下文中,这些字符真正的意思是";这是一条评论";
要了解解析,我通常建议"《龙书》;但除非你学过一点离散数学,否则这本书很难阅读。它涵盖了许多不同的解析技术,在这样做的过程中,它没有太多的页面可供示例使用。这意味着这是一本你必须阅读、思考,然后编写一个小例子的书。如果你沿着这条路走,就没有你无法处理的输入。
如果你在解决方案中很务实,并且不是为了学习解析,而是为了剥离注释,我建议你找到一个构造良好的C语法分析器,然后学习如何在发射器中遍历抽象语法树,因为它无法发出注释。
有些项目已经这样做了;但是,我不知道它们是否有合适的结构,便于修改。lint
以及其他";漂亮的打印机";GCC当然有解析代码,但我听说GCC的抽象语法树不容易学习。
您的解决方案有几个问题:
最糟糕的问题
作为delete_comments()
中的第一条指令,您使用malloc()
返回的新指针覆盖input
,该指针指向随机内容的内存。
因此,实际输入的地址丢失。
哦,如果您调用malloc()
,请检查返回的值。
未能正确增加评论中的扫描位置
您正在按索引x
扫描输入,但如果检测到注释,则不会更改它。
您实际上正在推进main
0,但这仅用于复制。
想想这样的行:
int x; /* some /* weird /* comment */
///////////////////////////////
for (;;) { }
忽略字符和字符串文字
您的解决方案应该考虑字符和字符串文字。
例如:
int c_plus_plus_comment_start = '//'; /* multi character constant */
const char* c_comment_start = "/*";
注意:还有更多。学习使用调试器,或者至少在"调试器"中插入大量printf()
;有趣的";地方