【C】 25_#和##操作符分析

#运算符

  • # 运算符用于在预处理期将宏参数转换为字符串
  • # 的转换作用是在预处理期完成的,因此只在宏定义中有效
  • 编译器不知道 # 的转换作用

用法

#define STRING(x) #x
printf("%s\n", STRING(Hello word));

实例分析: #运算符的基本用法

#include <stdio.h>

#define STRING(x) #x

int main()
{
    printf("%s\n", STRING(Hello word));
    printf("%s\n", STRING(100));
    printf("%s\n", STRING(while));
    printf("%s\n", STRING(return));
}
输出:
Hello word
100
while
return

Test_2.i

printf("%s\n", "Hello word");
printf("%s\n", "100");
printf("%s\n", "while");
printf("%s\n", "return");

实例分析: # 运算符的妙用

Test_2.c

#include <stdio.h>

#define CALL(f, p) (printf("Call funtion %s\n", #f), f(p))

int square(int n)
{
    return n * n;
}

int func(int x)
{
    return x;
}

int main()
{
    int result = 0;
    
    result = CALL(square, 4);
    
    printf("result = %d\n", result);
    
    result = CALL(func, 10);
    
    printf("result = %d\n", result);
    
    return 0;
}
输出:
Call funtion square
result = 16
Call funtion func
result = 10

Test_2.i

 result = (printf("Call funtion %s\n", "square"), square(4));

 result = (printf("Call funtion %s\n", "func"), func(10));

##运算符

  • ## 运算符用于在预处理期粘结两个标识符
  • ## 的连接作用是在预处理期完成的,因此只在宏定义中有效
  • 编译器不知道 ## 的连接作用

用法

#define CONNECT(a, b) a##b
int CONNECT(a, 1);    // int 1
a1 = 2;

实例分析: ## 运算符的基本用法

Test_3.c

#include <stdio.h>

#define NAME(n) name##n

int main()
{
    int NAME(1);
    int NAME(2);
    
    NAME(1) = 1;
    NAME(2) = 2;
    
    printf("%d\n", NAME(1));
    printf("%d\n", NAME(2));
}
输出:
1
2

Test_3.i

int main()
{
    int name1;
    int name2;

    name1 = 1;
    name2 = 2;

    printf("%d\n", name1);
    printf("%d\n", name2);
}

实例分析:运算符的工程应用

Test_3.c

#include <stdio.h>

#define STRUCT(type) typedef struct _tag_##type type;\
                     struct _tag_##type
                     
STRUCT(Student)
{
    char* name;
    int id;
};

int main()
{
    Student s1;
    Student s2;
    
    s1.name = "s1";
    s1.id = 1;
    
    s2.name = "s2";
    s2.id = 2;

    printf("s1.name = %s\n", s1.name);
    printf("s1.id = %d\n", s1.id);
    printf("s2.name = %s\n", s2.name);
    printf("s2.id = %d\n", s2.id);
    
    return 0;
}
输出:
s1.name = s1
s1.id = 1
s2.name = s2
s2.id = 2

Test_4.i

typedef struct _tag_Student Student; struct _tag_Student
{
 char* name;
 int id;
};

小结

  • # 运算符用于在预处理期将宏参数转换为字符串
  • ## 运算符用于在预处理期粘连两个标识符
  • 编译器不知道 # 和 ## 运算符的存在
  • # 和 ## 运算符只在宏定义中有效

以上内容参考狄泰软件学院系列课程,请大家保护原创!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一个关于使用LEX和YACC实现C语言词法和语法分析的基本步骤: 1. 定义词法规则:使用LEX定义C语言的词法规则,包括关键字、操作符、标识符、常量等。 2. 编写语法规则:使用YACC编写C语言的语法规则,包括程序结构、数据类型、表达式、语句等。 3. 链接LEX和YACC:将LEX和YACC生成的词法和语法分析程序进行链接,生成最终的C语言解析器。 4. 测试解析器:编写一些C语言代码,用生成的解析器进行测试,检查是否能正确地解析出代码中的词法和语法错误。 下面是一个简单的示例: 首先,我们需要创建一个名为“cparser”的文件夹,然后在该文件夹中创建三个文件:lex.l、yacc.y和main.c。 在lex.l文件中,我们定义C语言的词法规则,可以包含以下内容: ```c %{ #include "yacc.tab.h" %} %% "auto" { return AUTO; } "break" { return BREAK; } "case" { return CASE; } "char" { return CHAR; } "const" { return CONST; } "continue" { return CONTINUE; } "default" { return DEFAULT; } "do" { return DO; } "double" { return DOUBLE; } "else" { return ELSE; } "enum" { return ENUM; } "extern" { return EXTERN; } "float" { return FLOAT; } "for" { return FOR; } "goto" { return GOTO; } "if" { return IF; } "int" { return INT; } "long" { return LONG; } "register" { return REGISTER; } "return" { return RETURN; } "short" { return SHORT; } "signed" { return SIGNED; } "sizeof" { return SIZEOF; } "static" { return STATIC; } "struct" { return STRUCT; } "switch" { return SWITCH; } "typedef" { return TYPEDEF; } "union" { return UNION; } "unsigned" { return UNSIGNED; } "void" { return VOID; } "volatile" { return VOLATILE; } "while" { return WHILE; } "+" { return PLUS; } "-" { return MINUS; } "*" { return TIMES; } "/" { return DIVIDE; } "=" { return ASSIGN; } "==" { return EQ; } "!=" { return NEQ; } ">" { return GT; } ">=" { return GE; } "<" { return LT; } "<=" { return LE; } "(" { return LPAREN; } ")" { return RPAREN; } "{" { return LBRACE; } "}" { return RBRACE; } ";" { return SEMICOLON; } "," { return COMMA; } %% ``` 在yacc.y文件中,我们定义C语言的语法规则,可以包含以下内容: ```c %{ #include <stdio.h> #include <stdlib.h> void yyerror(const char *msg); int yylex(void); %} %token AUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM EXTERN FLOAT FOR GOTO IF INT LONG REGISTER RETURN SHORT SIGNED SIZEOF STATIC STRUCT SWITCH TYPEDEF UNION UNSIGNED VOID VOLATILE WHILE %token PLUS MINUS TIMES DIVIDE ASSIGN EQ NEQ GT GE LT LE LPAREN RPAREN LBRACE RBRACE SEMICOLON COMMA %token ID NUM %start program %% program: /* empty */ | program external_declaration ; external_declaration: function_definition ; function_definition: type_specifier declarator compound_statement ; type_specifier: INT | VOID ; declarator: ID | LPAREN declarator RPAREN | declarator LBRACE RBRACE | declarator LPAREN parameter_list RPAREN | declarator LPAREN RPAREN ; parameter_list: parameter_declaration | parameter_list COMMA parameter_declaration ; parameter_declaration: type_specifier declarator ; compound_statement: LBRACE RBRACE | LBRACE statement_list RBRACE ; statement_list: statement | statement_list statement ; statement: expression_statement ; expression_statement: expression SEMICOLON ; expression: ID ASSIGN expression | ID | NUM ; %% void yyerror(const char *msg) { fprintf(stderr, "error: %s\n", msg); } int main(int argc, char **argv) { yyparse(); return 0; } ``` 在main.c文件中,我们将使用解析器来读取C语言代码: ```c #include <stdio.h> #include <stdlib.h> void yyerror(const char *msg); int yyparse(void); int main(int argc, char **argv) { yyparse(); return 0; } void yyerror(const char *msg) { fprintf(stderr, "error: %s\n", msg); } ``` 现在,我们可以使用以下命令来编译和运行解析器: ```sh $ lex lex.l $ yacc -d yacc.y $ gcc lex.yy.c yacc.tab.c main.c -o cparser $ ./cparser ``` 输入一些C语言代码,例如: ```c int main() { return 0; } ``` 解析器应该能够正确地解析该代码,不会提示任何错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值