【问题标题】:C Macro trick to hide assignment?隐藏赋值的C宏技巧?
【发布时间】:2018-05-16 00:39:23
【问题描述】:

我有一个包含几个文件的项目:source1.c source2.c source1.h source2.h。现在 source1.c 声明了一些变量,并且 source1.h 将它们外部化(使用条件构建宏)。 source2.c 然后将通过为外部变量分配一个值来使用它们。

问题是 source1.h 有如下内容:

#ifdef CUST1
extern type var_name;
#else
// Need to extern something or use a clever macro here but i dont like these below
//#define var_name int x (requires top of scope block usage)
//#define var_name // (doesnt compile)
//#define var_name -1 (simply doesnt compile)
//#define var_name 1= (This seems like it would reduce to unused 1==1; but doesnt work)
#endif

我的构建适用于 CUST1,但不适用于 CUST2,因为在 source2.c 中引用 var_name 时从未声明/超出范围。

我不想为 CUST2 使用 var_name,它是 source2.c 中无法访问的代码。这是我的问题,我怎样才能使用宏来#define var_name 以便赋值“消失”或什么都不做?

我可以“#define var_name int x”。这会将 int x 放在 source2.c 函数中的堆栈上,并在 source2.c 中分配值,但如果它在除作用域块顶部之外的任何地方被引用,我的旧(C89?)编译器将出错。

例如,如果 source2.c 曾经有以下内容,它将无法编译:

unsigned short local_var = 0;
local_var = 1; //or someother value
var_name = local_var * 2;

我可以使用相同的 #ifdef CUST1 宏将逻辑包装在 source2.c 中,但这似乎也不太好。

如果 var_name 只是被比较,它不会那么糟糕,因为我可以只使用 #define var_name -1 或者会失败所有比较/切换的东西。问题是 -1 = temp_var;无法编译,因为 -1 不能是左值。

类似地,我不能“#define var_name //”,因为在替换 marcos 之前删除了 cmets:Can you #define a comment in C?

是否有一个巧妙的宏技巧可以隐藏/删除这个赋值,不会将 local_var 放在堆栈上?我觉得三元组有一些可能,但我想不出。

编辑 最小示例代码:

source1.c

int var_name = 0;

source1.h

#ifdef CUST1
extern int var_name;
#else
// clever macro
#endif

source2.c

#include "source1.h"
int main(){
  var_name = 1;
  return 0;
}

【问题讨论】:

  • "Clever" code is bad code.。不要用另一层混淆来隐藏混淆代码。 修复根本问题。
  • 如果 var_name 仅是 CUST1,那么您需要 #ifdef 全部使用它,或者只在大多数地方使用它,而只需 #ifdef 在您不希望它可见的地方。跨度>
  • @AndrewHenle 我同意这一点,根本问题是它是在为 CUSTOMER2 编译项目时分配的。 “基本修复”是包装在#ifdef CUST1 宏中使用的逻辑,但不能很好地扩展。有两个以上的客户,现在 var_name 已被外部化,以后可能会在其他地方使用。我同意你的看法,这使事情过于复杂,但事情就是这样。问题是关于隐藏 CUSTOMER2 分配的宏技术。
  • 请说明您是否希望将赋值转换为 (a) 无操作、(b) 编译错误或 (c) 运行时错误
  • 在 C99 中你可以做 #define var_name (int[]){0}[0]

标签: c macros


【解决方案1】:

如果在CUST1 未定义(如您所说的那样)时访问source2.c 中的var_name 的代码不可访问,那么您可以这样定义:

#include <stdlib.h>

#define var_name (*(abort(), (type *)0))

这将在语法上作为正确类型的左值(或右值)工作,但如果程序实际执行过,则会中止程序。

【讨论】:

    【解决方案2】:

    没有像您建议的那样直接隐藏作业的方法,但有一些替代方法:

    1. 你可以使用另一个宏:

      #ifdef CUST1
      extern type var_name;
      /* Do the real assignment */
      #define SET_VAR_TO(val) do{ var_name = (val); } while(0)
      #else
      /* Just evaluate for the side-effects */
      #define SET_VAR_TO(val) do { val; } while(0)
      #endif
      

      然后在 source2.c 中将所有对 var_name 的分配替换为 SET_VAR_TO(value),例如:

      int foo(void) {
          /* Replace
          var_name = bar();
          * With: */
          SET_VAR_TO(bar());
      }
      
    2. 您可以测试 CUST1 是否也在 source2.c 中定义:

      /* in source2.c */
      int foo(void) {
      #ifdef CUST1
      var_name = bar();
      #endif
      }
      

      在这种情况下,您甚至可以将分配包装到仅在源文件中定义的真实函数:

      /* in source2.c */
      int set_var_to(type value) {
      #ifdef CUST1
      var_name = value;
      #endif
      }
      
      int foo(void) {
          /* Replace
          var_name = bar();
          * With: */
          set_var_to(bar());
      }
      

    由于您不希望通过将每个分配包装在#ifdef CUST1 ... #endif 中来重复代码,因此您可以使用函数或宏。请记住,选项 #1 还会将宏 SET_VAR_TO 公开给任何 #includes source1.h 的文件,而不仅仅是 source2.c。

    【讨论】:

    • 感谢您的回复。如果我可以 "#define var_name 1=" 那么在 main 中它将是 1==1;这不会影响并“吸收” CUST2 案例中的分配。不过,这似乎对我不起作用。您的两个解决方案都有效,如果我想不出更好的解决方案,我可能会实施第一个解决方案并将您的回答作为答案,但我仍然不相信没有什么聪明的东西可以吸收这项任务。
    • set_var_to() 甚至可以是source1.h 中的static inline 函数,因此函数调用在!CUST1 情况下编译为空。
    【解决方案3】:
    extern int varname;
    
    #define varname __attribute__((unused)) int varname
    
    int square(int num) {
        varname = 1;
        return num * num;
    }
    

    它可以在除 -O0 之外的任何优化级别上工作

    https://godbolt.org/g/phssNn

    【讨论】:

    • 感谢彼得的回复。如果 C89 不在作用域块的顶部,这还会引发编译器错误吗?看起来它声明它未使用,但它仍然声明它并且在作用域块中间的声明对于我的一些客户来说编译失败。
    • 你我在任务中别无他法。顺便说一句,我不明白为什么
    猜你喜欢
    • 2011-07-18
    • 1970-01-01
    • 2011-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-02
    • 2021-04-05
    • 1970-01-01
    相关资源
    最近更新 更多