【问题标题】:Can a C struct contain a pointer to an array of other structs (with a varying length)C 结构是否可以包含指向其他结构数组的指针(长度可变)
【发布时间】:2021-07-22 19:17:12
【问题描述】:

我会尽我所能解释这一点。 我已经在 C 领域工作了一段时间,但从来不需要超越我的舒适区。我真的很想尝试让它发挥作用,这将有助于维护我一直在努力工作的程序结构。

虽然我没有接受过正式的 C 培训,所以我会尽可能多地自学。

我有一个 typedef 结构,我们称之为“struct_A_t”,另一个 typedef 结构“struct_B_t”。在“struct_A_t”中,我想要(除其他外)一个指向“struct_B_t”数组的成员。我有多个“struct_A_t”实例,它们的“struct_B_t”项的数组长度并不相同。

我知道将数组传递给 fn 会将地址传递给第一个元素,但我也注意到指向数组的指针和指向第一个元素的指针不一定相同。不过,一些指针的东西仍然让我着迷。

理想的情况是我能够将“struct_A_t”项的所有实例放入一个数组中,并在我的程序中传递这个数组,根据需要更改 struct_B 和 struct_A 成员的值。

参见下面的示例代码:

我的 main.h

#include <stdio.h>
#include "stdlib.h"
#include "stdbool.h"
#include "stdint.h"


//Defines fo the array lengths of struct_B_t

#define MyGroup0_Length     10
#define MyGroup1_Length     20
#define MyGroup2_Length     30

#define GroupCount 3

//Struct B definition
typedef struct
{
    bool        ItemSelected;
    uint8_t     ItemID;
    uint8_t     ItemState;
} struct_B_t;

//Struct A Definition
typedef struct 
{
    bool        GroupEnabled;
    uint8_t     GroupID;
    uint8_t     GroupState;
    uint8_t     ItemCount;
    struct_B_t  (*ItemList)[];
} struct_A_t;

//These are the item lists that must be contained within each GroupState
struct_B_t Group0Items[MyGroup0_Length];
struct_B_t Group1Items[MyGroup1_Length];
struct_B_t Group2Items[MyGroup2_Length];


//Function prototypes
void main(void);
void BuildGroups(struct_A_t* _groups);
void ChangeGroupItems(struct_A_t* _groups, uint8_t _groupCount);

我的 main.c

#include <stdio.h>
#include "stdlib.h"
#include "stdint.h"
#include "stdbool.h"
#include "main.h"

//A array of type "struct_A_t" with length = 3, to accomodate my three groups
struct_A_t MyGroups[GroupCount];

void main(void)
{
    BuildGroups(&MyGroups);
    
    ChangeGroupItems(&MyGroups, GroupCount);
}

void BuildGroups(struct_A_t* _groups)
{
    //Set the group lists
    
    //Group 1
    _groups[0].ItemList = &Group0Items;
    _groups[0].ItemCount = MyGroup0_Length;
    
    //Group 2 
    _groups[1].ItemList = &Group1Items;
    _groups[1].ItemCount = MyGroup1_Length;
    
    //Group 3
    _groups[2].ItemList = &Group2Items;
    _groups[2].ItemCount = MyGroup2_Length;
}

void ChangeGroupItems(struct_A_t* _groups, uint8_t _groupCount)
{
    for(uint8_t i = 0; i < _groupCount; i++)
    {
        for(uint8_t j = 0; j < _groups[i].ItemCount; j++)
        {
            if(_groups[i].ItemList[j].ItemSelected)
            {
                _groups[i].ItemList[j].ItemState++;
            }
        }
    }
}

您可以猜到,这没有正确编译。当我稍微改变一下并构建它时,我会收到不兼容指针类型的警告。

我不觉得这是一个非常独特的问题,所以我很想听听任何建议。 在这个阶段,我试图将我的应用程序数据限制在一个结构中,并且我想避免让太多的东西全局可访问。

我还应该注意,我的实际程序看起来有点不同,并且事情被分成比这更多的 .c 文件,而我想要实现的目标将很好地适应我的整体程序模式。

提前致谢。

【问题讨论】:

  • struct_B_t *ItemList_groups[0].itemList = Group0Items;
  • struct_B_t *ItemList;替换struct_B_t (*ItemList)[];
  • 您在研究微控制器吗?如果有,是哪一个?不一定与该问题相关,但 void main(void) 不应在托管环境中使用。那和您对uint8_t 的使用让您认为您处于独立环境中?
  • @12431234123412341234123 名字不要以_开头,它们是保留的。仅C标准完全reserves identifiers that start with two underscores or one underscore and a capital letter。单下划线后跟小写字母可以作为局部变量或函数参数。永远不要使用下划线开头的变量确实可以更容易地避免潜在的冲突。
  • 您好,感谢您的回复。我正在 stm32 上开发一个自定义项目。如果我要替换 struct_B_t itemlist 定义,我仍然可以将它作为数组使用和访问吗?感谢您的提示,我正在快速尝试启动并运行一个工作示例,但肯定会注意到这一点以供将来参考。

标签: arrays c struct


【解决方案1】:

当您使用数组时,它会衰减为指向第一个元素的指针。例如,当你使用Group1Items[n]++,它等价于(*(Group1Items+n))++Group1Items 将衰减为指向Group1Items[0] 的指针,之后数组的索引完成以访问n'th 元素然后递增。这部分:[n]是对指针的操作,指针指向Group1Items-array。

这两件事是等价的:

struct_B_t myArray[3];
//myArray decays to a pointer,
//which then access the second element in myArray
myArray[1].ItemState=0; 

// .. some code goes here

myArray[1].ItemState++ 

struct_B_t myArray[3];
//here myArray decays to a pointer which has the 
//same type as myPointer.
struct_B_t *myPointer = myArray; 
myPointer[1].ItemState=0;

// .. some code goes here

myPointer[1].ItemState++ 

请注意,当数组是 sizeof 运算符或一元 &amp; 运算符的操作数,或者是用于初始化数组的字符串文字时,数组不会衰减为指针。 (感谢@chux - 恢复莫妮卡)

【讨论】:

  • 详细信息:“不会衰减到指针”在其他情况下:“除非它是 sizeof 运算符的操作数,或一元 & 运算符,或者是用于初始化数组,”
【解决方案2】:

您可以有一个指向未知长度数组的指针。 (注意,未知长度不同于可变长度。int a[] 声明了一个长度未知的数组。int a[n] 声明了一个可变长度的数组,假设 n 是某个变量。)但是,在你描述的情况。通常,我们只是使用指向第一个元素的指针。

如果在struct_A_t 中,ItemList 成员被声明为struct_B_t *ItemList,它将是一个指向struct_B_t 的指针。一旦将它设置为指向struct_B_t 数组的第一个元素,您就可以使用t.ItemList[i] 访问数组的元素i,假设t 是一些struct_A_t(例如@987654333 @)。

这不是一个独特的问题。

不要将main 声明为void main(void)。使用 int main(void)int main(int argc, char *argv[]) 或您的 C 实现定义的其他形式。

不要在main.h 中声明main。在头文件中,仅放置与其相关联的源文件提供给其他源文件使用的东西的声明。但是,main 是一个例外;实际上,C 实现已经有自己的main 声明。只需要定义,不用单独声明。

变化:

#include "stdlib.h"
#include "stdint.h"
#include "stdbool.h"

到:

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

不要使用以下划线开头的名称。这些保留用于(由 C 标准库和编译器以及 C 实现的其他部分)在文件范围内使用。

【讨论】:

  • 谢谢你,没想到会有这么有见地的答案。我会尝试这个实现,但现在对我来说更有意义了!
  • void main(void) 适用于像@JacquesDuminy 这样的独立环境。 main() 函数无论如何都不应该返回。
【解决方案3】:

我个人会使用:

typedef struct 
{
    bool        GroupEnabled;
    uint8_t     GroupID;
    uint8_t     GroupState;
    uint8_t     ItemCount;
    struct_B_t  ItemList[];
} struct_A_t;

只需要一个 malloc 或 realloc 来分配内存。一个免费释放它。

【讨论】:

【解决方案4】:

在大多数情况下,C 中的数组变量/字段等于指向其内存的指针。所以你可以使用下一个结构定义:

typedef struct 
{
    bool        GroupEnabled;
    uint8_t     GroupID;
    uint8_t     GroupState;
    uint8_t     ItemCount;
    struct_B_t  *ItemList;
} struct_A_t;

然后用你的数组初始化它的项目:

 //Group 1
 _groups[0].ItemList = Group0Items;
 _groups[0].ItemCount = MyGroup0_Length;
    
 //Group 2 
 _groups[1].ItemList = Group1Items;
 _groups[1].ItemCount = MyGroup1_Length;

请注意,我将数组直接分配给指针。

使用此类结构保持不变:

_groups[i].ItemList[j].ItemState++;

【讨论】:

  • 非常感谢您的详尽回答。我的职业生涯使我陷入了嵌入式编程的深渊,因此非常感谢您的建议。
  • C 中的数组在大多数情况下等于指向其内存的指针。 这是不正确的。它们可以平等地使用,因为数组变量衰减为指针,但它们是完全不同的东西。
  • @12431234123412341234123 是的,当然。我的意思是使用相等
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-30
相关资源
最近更新 更多