【问题标题】:Is there a way to declare a complex number without including <complex.h>?有没有办法在不包含 <complex.h> 的情况下声明一个复数?
【发布时间】:2019-10-05 00:24:51
【问题描述】:

我想使用&lt;complex.h&gt; 库来加快一些计算,其中可能包括多达 20 个复杂元素和操作 (+ - * /)。但是,我的同事已经声明了一个结构:

struct complex {
    double a;
    double b;
};

因此来自库的complex宏与上述结构冲突,无法使用。

问题是,由于对当前项目的巨大影响,我无法更改结构的名称。我尝试改用_Complex 宏,并删除#include &lt;complex.h&gt; 标头,它可以工作,但_Imaginary_I_Complex_II 不起作用。

#include <stdio.h>      /* Standard Library of Input and Output */
// #include <complex.h>    /* I want to remove this */    

int main() {
    struct complex
    {
        double a;
        double b;
    };
    double _Complex z1 = 1.0;// + 3.0 * _Complex_I;
    double _Complex z2 = 1.0;// - 4.0 * _Complex_I;    

    // none of these work
    double _Complex z = CMPLX(0.0, -0.0);
    double _Complex z3 = 1.0 + 1.0 * _Imaginary_I;
    double _Complex z4 = 1.0 + 1.0 * _Complex_I;
    double _Complex z5 = 1.0 + 1.0 * I;    

    printf("Starting values: Z1 = %.2f + %.2fi\n", creal(z1), cimag(z1));    

}

我希望有一种方法可以区分来自库的复杂宏和定义的复杂结构。遗憾的是我无法改变结构,所以事情变得出乎意料地复杂了。

我的__STDC_VERSION__201112L,而__VERSION__ 是“4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.46.4)”。

【问题讨论】:

  • _Complex 不是宏;它是§6.4.1 Keyword_Imaginary 也是。以_I 为后缀的名称不是关键字;它们是由&lt;complex.h&gt; 定义的宏。
  • 但是,我的同事已经声明了 struct complex 而不是跳过箍来回避这个基本问题,FIX真的很难> 误用a standard C identifier的实际问题?因为那是 REAL 问题,不修复它会奖励错误代码的创建 - 并留下一个地雷,在未来引发各种问题。当 New Coder 开始使用该程序并且不知道他必须#undef complex 才能掩盖过去的错误决定时会发生什么?

标签: c header-files c99 complex-numbers standard-library


【解决方案1】:

C 标准预见到了这个问题,并有一个专门针对它的子句,7.3.1p7

尽管有 7.1.3 的规定,程序可能会取消定义并可能重新定义宏 compleximaginaryI

这意味着,你可以这样做:

#include <stdio.h>
#include <complex.h>
#undef complex     /* after including complex.h, before declaring struct complex */

//#include <header_that_defines_struct_complex.h>
struct complex
{
    double a;
    double b;
};

现在您的代码可以毫无问题地同时使用struct complexdouble _Complex,并且可以访问&lt;complex.h&gt; 中声明的复杂数学函数。

int main(void)
{
    // this should all be fine now
    double _Complex z = CMPLX(0.0, -0.0);
    double _Complex z1 = 1.0 + 3.0 * _Complex_I;
    double _Complex z2 = 1.0 - 4.0 * _Complex_I;    
    double _Complex z3 = 1.0 + 1.0 * _Imaginary_I;
    double _Complex z4 = 1.0 + 1.0 * _Complex_I;
    double _Complex z5 = 1.0 + 1.0 * I;    
    printf("Starting values: Z1 = %.2f + %.2fi\n", creal(z1), cimag(z1));    

    return 0;
}

但是请注意,无法使struct complex 自动转换为double _Complex,也无法“兼容”double _Complex,即使它们在内存中可能具有相同的布局。您必须手动来回复制。

【讨论】:

  • ...呵呵。不敢相信我这么接近那个明显的解决方案就摔倒了。
  • 虽然这是目前规避标准定义的有效方式,但这并不能为未来的软件开发奠定坚实的基础。一般来说,重用 C 标准为不同目的定义的任何标识符都不是一个好主意。仅供参考,C 委员会目前正在讨论将这些丑陋的关键字(如 _Complex)更改为它们的“真实”小写名称。
  • @JensGustedt 呃,我希望他们意识到这是多么糟糕的想法。与执行此类操作的古老第三方库的向后兼容性,您对此无能为力,这是 C 仍然要做的唯一事情之一。
  • 是的,我忘记了只有complex宏被复制了,其他组件如crealcimagI仍然可以正常工作。
【解决方案2】:

或者,您可以重新定义在the_other_colleagues_header 中定义的结构复合体

#define complex my_complex
#include <the_other_colleagues_header>
#undef complex

#include <complex.h>

现在在您的 .c 文件中,您可以同时使用 complexmy_complex,其他人仍然可以正常使用 struct complex。或者为complex.h 编写一个包装器并从您的应用程序中调用它

但更好的解决方案是让其他同事改名。使用标准名称通常是个坏主意

【讨论】:

  • 您的解决方案确实有效,它也帮助我理解了undef 指令的另一种用法。我认为它的目的只是删除define 指令。另外,感谢您编辑我的问题并使它变得更好!
【解决方案3】:

你的实际问题是

my colleague has declared a struct:

    struct complex {
        double a;
        double b;
    };

解决这个问题。

解决此问题的所有其他方法都会创建事实上撤消标准 C 功能的未记录依赖 - 宏 complex 的定义.您的组织将要做的最好的事情可能是“在我们的编码标准的第 4 页[你有编码标准吗?],它声明你必须在#include &lt;complex.h&gt; 之后#undef complex,然后你必须@987654325 @ 在那之后”。你的组织实际上做那么多的几率是多少?然后确保节目中的每个人都知道这一点?

很可能,他们不会为此付出努力,甚至不会接近。

你最终会得到一些无证的部落知识。

同样,您的组织是否甚至有书面编码标准并实际执行?如果您不能对此回答是肯定的,那么您确实需要现在真正解决问题的根本原因,而不是将其埋没,等着在未来引发更严重的问题。

struct complex 不仅创建了对撤消标准 C 功能的依赖,而且还创建了对 #include 指令和 #undef 语句的排序依赖。如果不严格遵循这些依赖关系,没有真正的方法可以知道会发生什么 - 不能排除未来几年奇怪的结果和无法解释的故障。

是的,使用#undef complex 取消定义宏是合法的,而且并不难。但是如果有人不这样做,或者以错误的顺序执行,会发生什么?

如果你做得对,编写和维护软件就够难了。不要故意做一些有问题的事情,从而使程序的整个生命周期变得更加困难。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多