需求拆解
- 讀取 .c 文件,移除所有註釋(
// 單行、/* */ 多行),寫入新文件。
- 核心邏輯封裝為動態鏈接庫(
.so/.dll)。
- 單元測試覆蓋邊界情況;通過 MISRA C 檢查;符合編碼規範。
核心算法:狀態機
typedef enum { NORMAL, IN_SINGLE_LINE, IN_MULTI_LINE, IN_STRING, IN_CHAR } State;
void strip_comments(FILE *in, FILE *out) {
State state = NORMAL;
int c, prev = 0;
while ((c = fgetc(in)) != EOF) {
switch (state) {
case NORMAL:
if (prev == '/' && c == '/') state = IN_SINGLE_LINE;
else if (prev == '/' && c == '*') state = IN_MULTI_LINE;
else {
if (prev) fputc(prev, out);
if (c == '"') state = IN_STRING;
else if (c == '\'') state = IN_CHAR;
}
break;
case IN_SINGLE_LINE:
if (c == '\n') { state = NORMAL; fputc(c, out); }
break;
case IN_MULTI_LINE:
if (prev == '*' && c == '/') state = NORMAL;
break;
case IN_STRING:
fputc(prev, out);
if (c == '"' && prev != '\\') state = NORMAL;
break;
case IN_CHAR:
fputc(prev, out);
if (c == '\'' && prev != '\\') state = NORMAL;
break;
}
prev = c;
}
if (prev && state == NORMAL) fputc(prev, out);
}
關鍵邊界處理
- 字符串/字符常量內的
// /* 不視為註釋。
- 轉義字符
\\\" \\' 不結束字符串/字符常量。
- 多行註釋嵌套(C 標準不支持嵌套,遇首個
*/ 結束)。
- 行綴續符
\\ 導致單行註釋延續。
動態庫導出接口
// strip.h
#ifdef _WIN32
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __attribute__((visibility("default")))
#endif
EXPORT int strip_comments_file(const char *in_path, const char *out_path);
MISRA C 合規要點
- Rule 8.1:函數原型必須聲明(
static 內部函數除外)。
- Rule 10.3:隱式類型轉換需顯式轉換。
- Rule 17.4:指針算術僅用於數組索引。
- Rule 21.1:
malloc/free 成對使用,檢查返回值。
- 禁用
gets、sprintf 等不安全函數,改用 fgets、snprintf。
單元測試覆蓋
| 用例 | 輸入片段 | 期望輸出 |
| 單行註釋 | int a; // comment | int a; |
| 多行註釋 | /* comment */ int b; | int b; |
| 字符串內類註釋 | char *s = "// not comment"; | 保持原樣 |
| 字符常量 | char c = '/'; | 保持原樣 |
| 行綴續 | // comment \\\n continued | 整行移除 |
| 嵌套註釋 | /* outer /* inner */ end */ | end */(按標準) |
構建與集成
# CMakeLists.txt
add_library(strip_comments SHARED strip.c)
target_include_directories(strip_comments PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# 啟用 MISRA 檢查(需外部工具如 cppcheck --enable=misra 或 PC-lint)