【问题标题】:Ignored Semicolon C Syntax忽略的分号 C 语法
【发布时间】:2014-03-31 02:42:23
【问题描述】:
  • 平台:Linux 3.2.0 x86 (Debian Wheezy)
  • 编译器:GCC 4.7.2 (Debian 4.7.2-5)

我正在编写一个返回指向 char 的指针的函数。我仍然想“返回”一个表示发生了什么错误的整数值,因此该函数将一个指向将用作标志的整数的指针作为参数。这是一个声明的例子

char* foo(int *flag, etc.)

如果用户不想知道发生了什么具体错误,我想允许 flag 为 NULL,因为这可能无关紧要(我不知道是否有,这个想法只是理论上的)。所以如果发生错误,我会写以下内容,

... //Symbolic Code
if(error) //Some error has occured
{
    if(flag != NULL) *flag = SOME_ERR_VALUE; //Set the flag variable
    return NULL; //return NULL to show that an error occured
}

我不想一遍又一遍地编写该代码,而是为它编写一个宏。但是代码不止一个语句,这意味着我必须将宏放在它自己的代码块中,或者宏必须已经在它自己的代码块中。

#define RETFLAG(err, ret) if(flag != NULL) *flag = err; return ret;

... //Symbolic Code
if(error) //Some error has occured
    {RETFLAG(1, NULL)} //Notice the {}

另一种方法是,

//Notice the {} in the definition
#define RETFLAG(err, ret) {if(flag != NULL) *flag = err; return ret;}

... //Symbolic Code
if(error) //Some error has occured
    RETFLAG(1, NULL)

这两种方法都可以,但我不喜欢借调的方法,因为我想像这样在宏的末尾放一个分号,

//Notice the {} in the definition
#define RETFLAG(err, ret) {if(flag != NULL) *flag = err; return ret;}

... //Symbolic Code
if(error) //Some error has occured
    RETFLAG(1, NULL); //The only difference is the semicolon

我知道分号是不必要的,但我认为如果您没有实际查看 RETFLAG 的定义,它会更具可读性。我已经尝试使用不必要的分号,并且我的编译器没有抱怨,并且据我所知该函数工作正常。所以我的问题是,是不必要的分号合法语法。如果合法,是否合适?

【问题讨论】:

    标签: c syntax macros


    【解决方案1】:

    你根本不需要宏。

    inline char *fail_with_reason(int *flag, int reason)
    {
        if (flag) *flag = reason;
        return NULL;
    }
    

    然后你的函数会说

    if (error) return fail_with_reason(flag, 1);
    

    【讨论】:

      【解决方案2】:

      你可以使用do { ... } while(0)

      #define RETFLAG(err, ret) do { if(flag != NULL) *flag = (err); return NULL; } while(0)
      
      if (error)
          RETFLAG(1, NULL);
      

      这样就不用担心作用域了,也没有多余的分号。

      【讨论】:

      • 我喜欢创意,但对我来说似乎有点尴尬。不过我也不喜欢不必要的分号。
      • 这很尴尬,但很常见,大多数有经验的 C 程序员都知道这是什么意思
      【解决方案3】:

      没有表达式的; 只是一个空语句,并且是完全合法的,来自 C99 草案标准部分 6.8.3 表达式和空语句

      空语句(仅由分号组成)不执行任何操作。

      但除了您的简单示例之外,这还有一些缺陷,正如 Raymond 在他的评论中指出的那样,do while trick 需要将您的代码包装在 do {} while(0) 块中,有助于克服,正如公认的答案所说:

      do ... while 和 if ... else 是为了让你的宏后面的分号总是意味着同样的事情[...]

      【讨论】:

      • 多余的分号会搞砸if (error) RETFLAG(1, NULL); else something_else();
      • @Raymond 你是对的我刚刚测试过。为什么会发生这种情况?
      • @JohnVulconshinz 我链接的线程解释了为什么在某些情况下需要 do while 技巧,这是一个很好的解释。
      【解决方案4】:

      逗号可能有帮助

      #define RETFLAG(err, ret) if(flag != NULL) return *flag = err, NULL
      

      更正:

      #define RETFLAG(err, ret) return NULL == flag ? NULL : *flag = err, NULL
      

      【讨论】:

      • 这可行,但问题是现在 return 语句是 if 语句的一部分。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-25
      • 2015-11-27
      • 1970-01-01
      • 2015-09-22
      • 2022-01-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多