【问题标题】:C macro - Returning a pointer or assigning a value to a pointer passed to the macroC 宏 - 返回一个指针或为传递给宏的指针赋值
【发布时间】:2019-08-06 17:51:01
【问题描述】:

我正在尝试编写“对象”风格的 C 代码,因为我需要使用不同的参数多次声明同一个对象。除了返回或分配指向已创建对象的指针外,一切正常。我搜索并找到了几个示例,但没有一个有效。

这是有效的。

头文件:

#ifndef _OBJECT_H_
#define _OBJECT_H_

typedef struct
{
    uint8_t* Buffer_PU8;
    uint8_t Index_U8;
}TParameters_S;

typedef struct
{
    uint8_t ID_U8;
    TParameters_S* Parameters_PS;
}TObject_S;

#define NEW_OBJECT(id, size) \
    static uint8_t Buffer_AU8##id[size]; \
    static TParameters_S Parameters_S##id = { \
        0, Buffer_AU8##id \
    }; \
    static const TObject_S __attribute__((used, section("my_objects"))) Object_S##id = { \
        id, &Parameters_S##id \
    }; \

#endif  // _OBJECT_H_

源文件:

#incldue "object.h"

typedef enum
{
    OBJECT1_E,
    OBJECT2_E,
}TObjects_E;

NEW_OBJECT(OBJECT1_E, 10);
NEW_OBJECT(OBJECT2_E, 20);

void function(void)
{
}

这可以正常工作并在“my_objects”部分中创建两个“对象”。每个对象都有不同大小的缓冲区。

但我想获得一个指向该对象的指针,以便可以对其进行操作。我可以在“my_objects”部分中搜索对象并指向它,但是如果我可以在编译时为该对象分配一个指针,那就太好了。这样我就不必浪费时间或代码空间来搜索对象了。

所以我已经尝试过了,但它不起作用(希望它将一个对象指针分配给“句柄”):

头文件:

#define NEW_OBJECT(id, size, handle) \
    static uint8_t Buffer_AU8##id[size]; \
    static TParameters_S Parameters_S##id = { \
        0, Buffer_AU8##id \
    }; \
    static const TObject_S __attribute__((used, section("my_objects"))) Object_S##id = { \
        id, &Parameters_S##id \
    }; \
    handle = &Object_S##id; \ <----- This does not work

源文件:

TObject_S* object1_PS;
TObject_S* object1_PS;

// therefore does not work
NEW_OBJECT(OBJECT1_E, 10, object1_PS);
NEW_OBJECT(OBJECT2_E, 20, object2_PS);

void function(void)
{
    // Then operate on the object (and therefore this does not work)
    object1_PS->Parameters_PS->Buffer_PS[object1_PS->Parameters_PS->Index_U8];
}

我也试过这个,希望它会返回一个指向对象的指针(我想我在这里抓住机会):

头文件:

#define NEW_OBJECT(id, size) \
    static uint8_t Buffer_AU8##id[size]; \
    static TParameters_S Parameters_S##id = { \
        0, Buffer_AU8##id \
    }; \
    static const TObject_S __attribute__((used, section("my_objects"))) Object_S##id = { \
        id, &Parameters_S##id \
    }; \
    &Object_S##id; \ <----- This does not work

源文件:

// therefore this does not work
TObject_S* object1_PS = NEW_OBJECT(OBJECT1_E, 10, object1_PS);
TObject_S* object1_PS = NEW_OBJECT(OBJECT2_E, 20, object2_PS);

void function(void)
{
    // Then operate on the object (and therefore this does not work)
    object1_PS->Parameters_PS->Buffer_PS[object1_PS->Parameters_PS->Index_U8];
}

这可行,但我认为它为每个对象创建了两个指针。虽然我只能在地图文件中看到每个对象的一个​​指针:

头文件:

#define NEW_OBJECT(id, size, handle) \
    static uint8_t Buffer_AU8##id[size]; \
    static TParameters_S Parameters_S##id = { \
        0, Buffer_AU8##id \
    }; \
    static const TObject_S __attribute__((used, section("my_objects"))) Object_S##id = { \
        id, &Parameters_S##id \
    }; \
    static const TObject_S* __attribute__((used, section("my_objects_pointers"))) handle = &Object_S##id; \ <----- This works

源文件:

TObject_S* object1_PS;
TObject_S* object1_PS;

// therefore this works
NEW_OBJECT(OBJECT1_E, 10, object1_PS);
NEW_OBJECT(OBJECT2_E, 20, object2_PS);

void function(void)
{
    // Then operate on the object (and therefore this works)
    object1_PS->Parameters_PS->Buffer_PS[object1_PS->Parameters_PS->Index_U8];
}

【问题讨论】:

  • "如果我可以在编译时为对象分配一个指针,那就太好了" - 我认为您真的不想这样做。您对环境的内存管理有这种类型的控制吗?
  • 为什么不呢?每次我调用 NEW_OBJECT 时,编译器都会创建 struct TObject 的单个实例,并且链接器会将它放在“my_objects”部分中。现在我只想要一个指向该对象的变量。它就像指向结构的任何其他变量一样。没有错。
  • 请注意,#define _OBJECT_H_ 是非法的。以下划线和大写字母开头的标识符在所有上下文中都是保留的。
  • 为什么不直接使用&amp;Object_SOBJECT1_E作为指向对象的指针呢?
  • "编译器将创建结构的单个实例" -- 不,它不会。

标签: c object pointers macros


【解决方案1】:

可执行语句,例如赋值语句,必须出现在函数内部。这就是为什么

    handle = &Object_S##id; \ <----- This does not work

不起作用(在您的特定用途中)。但是您可以在(最多)其中一个声明中初始化变量。例如,那么:

#define NEW_OBJECT(id, size, handle) \
    static uint8_t Buffer_AU8##id[size]; \
    static TParameters_S Parameters_S##id = { \
        Buffer_AU8##id, 0 \
    }; \
    static const TObject_S __attribute__((used, section("my_objects"))) Object_S##id = { \
        id, &Parameters_S##id \
    }; \
    const TObject_S *handle = &Object_S##id;


const TObject_S *object1_PS;  // This is redundant and can be dropped

NEW_OBJECT(OBJECT1_E, 10, object1_PS);

请注意,由于您将结构本身声明为const,指向它的指针需要适应它。另请注意,我已经翻转了 struct TParameters_S 的元素初始值设定项的顺序,以匹配结构定义中似乎显而易见的意图。

附录:

还请注意,除非您真的想要缓冲区和参数的单独变量,否则鉴于您在文件范围内声明所有内容,您可以替换复合文字以实现更清晰的命名空间:

#define NEW_OBJECT(id, size, handle) \
    static const TObject_S __attribute__((used, section("my_objects"))) Object_S##id = { \
        .ID_U8 = id, .Parameters_PS = & (TParameters_S) { \
            .Buffer_PU8 = (uint8_t[]) { [size - 1] = 0 }, .Index_U8 = 0 } \
    }; \
    const TObject_S *handle = &Object_S##id;

您可以更进一步消除Object_S*variables,只保留句柄指针作为声明的变量,但我不知道您可以将属性应用于复合文字。

【讨论】:

  • const TObject_S *object1_PS; 之前的 NEW_OBJECT... 是多余的还是实际上是错误的?不是同一个变量的不同定义吗?
  • 不,@AjayBrahmakshatriya,他们不是。允许同一变量的多个声明,前提是类型兼容,最多一个声明有一个初始化器,并且满足声明的链接的其他几个要求(包括变量 链接)。
  • @JohnBollinger 关于您的附录,那不会将参数及其成员设置为 const 吗?由于对象是 const?我这样明确地声明它们,这样它们就不是 const 了。
  • @user668773,复合文字替代方案不会产生任何 const 尚未成为 const 的东西。复合文字表示可修改的对象(与字符串文字不同),因此无论哪种方式,const 都是对象 id 和 指向参数的指针。参数本身是可修改的——索引、缓冲区指针和缓冲区元素。另请注意,虽然句柄指向 const 结构,但它本身(指针)是可修改的。
  • 也许这只是我的狂妄自大,@AjayBrahmakshatriya,但从上下文和语气来看,我认为它是一种补充。我认为他们的意思是他们认为我在这方面知识渊博,以至于我必须在其他事情上很愚蠢才能平衡。 (但也许我应该被冒犯,因为他们低估了我成为全能天才的可能性?:-))
【解决方案2】:

好的,通过上面的 cmets(Henning 和 Ajay),我设法想出了一个解决方案。

问题是对象需要一个常量初始化元素。所以我不能将变量分配给该范围内的变量。但我可以用宏来克服它。所以我所做的是这样的:

头文件:

#define NEW_OBJECT(id, size) \
    static uint8_t Buffer_AU8##id[size]; \
    static TParameters_S Parameters_S##id = { \
        0, Buffer_AU8##id \
    }; \
    static const TObject_S __attribute__((used, section("my_objects"))) id = { \
        &Parameters_S##id \
    }; \

#define GET_OBJECT(id) (TObject_S*)&id

源文件:

NEW_OBJECT(OBJECT1, 10);

const TObject_S* object1_PS = (TObject_S*)GET_OBJECT(OBJECT1);

void function(void)
{
    object1_PS->Parameters_PS->Buffer_PS[object1_PS->Parameters_PS->Index_U8];
}

所有的强制转换可能不是必需的,但它有效。

【讨论】:

    【解决方案3】:

    问题是您在全局范围内编写作业。当NEW_OBJECT 展开时,它会创建 -

    handle = &amp;Object_S0;

    对于第一个对象。这在 C 中是不允许的。

    您必须在 main 函数中移动此分配或在需要引用它的任何地方使用 &amp;Object_S0

    您可以在函数内创建对象,这很好。该对象将静态创建,分配将在运行时发生。

    【讨论】:

    • 是的,它需要一个常量初始化元素。并且不允许将变量分配给全局范围内的变量。但是我可以用另一个宏来克服它吗?
    • 您也可以在宏中移动变量的声明。基本上你在问题的最后一个方法中做了什么,然后删除了你对指针的额外声明。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-20
    • 1970-01-01
    • 2013-05-16
    • 2011-10-10
    相关资源
    最近更新 更多