C语言汇编器开发:如何读取汇编行并拆分标签、操作码等内容
搞定汇编器第一遍扫描的行拆分问题
嘿,这个问题我熟!我之前也写过类似的汇编器扫描模块,刚好能帮你搞定。你遇到的核心问题是scanf/sscanf没法处理带空格的注释,而且没法灵活应对汇编行里可选的label/operand字段——毕竟不是每一行都有三个完整的字段。
用fgets读整行再手动拆分绝对是正确的思路,下面我给你一套实用的方案,还会考虑各种边界情况:
核心思路
- 用
fgets读取完整一行,先处理掉末尾的换行符 - 拆分出最多三个空白分隔的字段(label、opcode、operand),但要能区分“第一个字段是label还是opcode”(毕竟有些行没有label,开头直接是opcode)
- 把剩余的所有内容(跳过开头空白后)作为注释
完整代码实现
#include <stdio.h> #include <string.h> #include <ctype.h> #define MAX_LINE 256 // 这里放你的汇编语言完整操作码列表,示例用你给的几个 const char *VALID_OPCODES[] = {"START", "STL", "JSUB", "LDA", "COMP", "JEQ", "J", NULL}; // 辅助函数:判断字符串是否是合法操作码 int is_valid_opcode(const char *str) { for (int i = 0; VALID_OPCODES[i] != NULL; i++) { if (strcmp(str, VALID_OPCODES[i]) == 0) { return 1; } } return 0; } int main() { FILE *fileptr1 = fopen("assembly.txt", "r"); if (!fileptr1) { perror("Failed to open assembly file"); return 1; } char line[MAX_LINE]; char label[MAX_LINE] = {0}; char opcode[MAX_LINE] = {0}; char operand[MAX_LINE] = {0}; char comment[MAX_LINE] = {0}; while (fgets(line, MAX_LINE, fileptr1) != NULL) { // 重置所有缓冲区,避免上一行数据残留 memset(label, 0, sizeof(label)); memset(opcode, 0, sizeof(opcode)); memset(operand, 0, sizeof(operand)); memset(comment, 0, sizeof(comment)); // 去掉行尾的换行符 size_t line_len = strlen(line); if (line_len > 0 && line[line_len - 1] == '\n') { line[line_len - 1] = '\0'; } char *ptr = line; // 跳过行开头的空格/制表符 while (isspace((unsigned char)*ptr)) { ptr++; } if (*ptr == '\0') { // 空行直接跳过 continue; } // 用strtok_r拆分前三个字段(线程安全,比strtok更可靠) char *saveptr; char *token1 = strtok_r(ptr, " \t", &saveptr); char *token2 = strtok_r(NULL, " \t", &saveptr); char *token3 = strtok_r(NULL, " \t", &saveptr); // 判断第一个token是label还是opcode if (is_valid_opcode(token1)) { // 没有label,第一个token就是opcode strcpy(opcode, token1); // 如果有第二个token,就是operand if (token2 != NULL) { strcpy(operand, token2); } // 剩余内容作为注释(跳过开头空白) if (saveptr != NULL && *saveptr != '\0') { while (isspace((unsigned char)*saveptr)) saveptr++; strcpy(comment, saveptr); } } else { // 第一个token是label strcpy(label, token1); // 第二个token是opcode if (token2 != NULL) { strcpy(opcode, token2); } // 第三个token是operand if (token3 != NULL) { strcpy(operand, token3); } // 剩余内容作为注释 if (saveptr != NULL && *saveptr != '\0') { while (isspace((unsigned char)*saveptr)) saveptr++; strcpy(comment, saveptr); } } // 打印测试结果,你可以替换成自己的存储逻辑 printf("Label: '%s' | Opcode: '%s' | Operand: '%s' | Comment: '%s'\n", label, opcode, operand, comment); } fclose(fileptr1); return 0; }
关键细节说明
- 操作码判断:用
is_valid_opcode函数区分label和opcode,你只需要把你的汇编语言所有操作码加到VALID_OPCODES数组里就行,这样就能准确识别哪些行开头是opcode(没有label)。 - 空白处理:无论是行开头的空白,还是字段之间的空格/制表符,都能正确跳过,注释部分也会自动去掉开头的空白。
- 边界情况覆盖:
- 空行会直接跳过
- 只有opcode的行(比如理论上的空操作行)
- 有opcode和operand但没有label的行(比如你的
COMP ZERO) - 完整的label-opcode-operand-注释行(比如你的
COPY START 1000 ...)
额外建议
- 如果你的汇编语法允许operand包含特殊字符(比如逗号
LDA VAR,X),你需要调整strtok_r的分隔符,或者改用手动解析的方式来拆分operand。 - 可以把
MAX_LINE设大一点,或者用动态内存分配(比如malloc)来避免缓冲区溢出。 - 第一遍扫描的时候,还可以顺便把label存入符号表,记录它的地址,这也是汇编器第一遍的核心工作之一。
内容的提问来源于stack exchange,提问作者Tivon Mohammed
相关产品推荐
相关产品推荐

