如何解决您如何访问结构数组
我对如何访问结构数组感到困惑。
简单情况:
typedef struct node
{
int number;
struct node *left;
struct node *right;
} node;
node *nodeArray = malloc(sizeof(node));
nodeArray->number = 5;
所以,这一切都说得通。但是以下操作无效:
typedef struct node
{
int number;
struct node *left;
struct node *right;
} node;
node *nodeArray = malloc(511 * sizeof(node));
for(int i = 0; i < 511; i++)
{
nodeArray[i]->number = i;
}
但是,nodeArray [i] .number = i确实可以工作,有人可以解释发生了什么, node *nodeArray = malloc(511 * sizeof(node));
和 node (*nodeArray) = malloc(511 * sizeof(node));
之间有什么区别
解决方法
在第一个代码段中,以下各项均等效:
nodeArray->number = 5; // preferred
nodeArray[0].number = 5;
(*nodeArray).number = 5;
在第二个片段中,以下各项均等效:
(nodeArray + i)->number = i;
nodeArray[i].number = i; // preferred
(*(nodeArray + i)).number = i;
因此,如您所见,可以选择三种不同的语法来完成相同的工作。处理指向结构的单个实例的指针时,首选箭头语法(nodeArray->number
)。当处理指向结构数组的指针时,首选使用点符号(nodeArray[i].number
)进行数组索引。明智的程序员避免使用第三种语法(取消引用指针和点符号)。
分配这样的数组时
node* nodeArray = malloc(511*sizeof(node));
nodeArray
是一个指针,您只需添加一个整数即可获得指向单个结构节点的指针:
nodeArray + 1
将给出指向第二个节点的指针
nodeArray + 1
可以写为&nodeArray[1]
所以要取消引用指针
*(nodeArray + 1).number
或输入nodeArray[1].number
可能是对齐问题引起的:
您的节点结构包含一个整数和两个指针,其最小存储大小可以为12个字节(在大多数32位体系结构上)或24个字节(64位体系结构),但是对齐限制为该架构可能会强制使用另一个 maximum 存储大小(带有额外的填充,也需要分配该填充)来对齐每个节点。
sizeof(type)
只会返回一个 minimum 的存储大小(即使在运行时或编译器未检查该额外分配的填充,也不应访问)。
解决方案:使用calloc()
,它还将考虑数组中每个项目的对齐约束!
替换:
node *nodeArray = malloc(511 * sizeof(node));
作者:
node *nodeArray = calloc(511,sizeof(node));
现在您的代码通常是安全的,实际分配的大小将包括基础架构所需的必要附加填充。
否则,您的代码将无法移植。
请注意,某些C / C ++编译器还提供了alignof(type)
来获取数据类型的正确对齐方式(应该在C / C ++库中用于实现void *calloc(size_t nitems,size_t size)
)。
您上面的示例代码可能会遭受缓冲区溢出的困扰,因为在循环中写入项目之前,您没有为数组分配足够的空间。
使用简单类型时,您看不到区别(您不关心它们的对齐方式或它们在何处被单独分配,可能会在堆栈上或使用它们的结构中分配额外的填充,这是无法访问的,即使在物理寄存器中分配其存储空间时也无需填充;但即使使用“自动”或“寄存器”分配,编译器仍可以为其在堆栈上分配空间,作为可用于保存的后备存储在需要其他东西时或在执行外部函数调用或C ++中的方法调用且函数主体未内联之前需要注册的寄存器。
请参阅C ++ 11中alignof
和alignas
声明符的文档。关于它们有很多资源。例如:
https://en.cppreference.com/w/cpp/language/alignas
另请参阅calloc()
(不要被Linux中使用的简化的32位或64位内存模型所迷惑;甚至Linux现在也使用更精确的内存模型,并考虑到对齐问题以及可访问性和性能问题,有时出于良好的安全性考虑,由底层平台强制执行此操作是为了减少针对所有事物的单一/统一“平面”内存模型中存在的攻击面:分段式体系结构重新出现在计算行业中,C / C ++编译器必须适应:C ++ 11回答了这个问题,否则将需要在编译后的代码中使用更昂贵或效率较低的解决方案,从而严重限制了某些优化,例如缓存管理,TLB存储效率,分页和虚拟化内存,针对用户/进程/线程的强制安全范围等等)。
请记住,每个数据类型都有其自己的大小和对齐方式,并且它们是独立的。假设为数组中的数据类型分配单个“大小”是错误的(以及在分配的数组的最后一项之后,额外的填充可能未分配,并且对填充区域的读/写访问可能被编译器或在运行时限制/执行。
现在还要考虑位域的情况(数据类型声明为具有额外精度/大小参数的结构的成员):它们的sizeof()并不是真正的最小值,因为它们可以更紧密地打包(包括布尔数组:sizeof( )一旦将数据类型提升为整数,就返回该数据类型的最小大小,因此当可能通过额外的填充或符号位扩展将其放大时;通常,编译器通过使用位掩码,移位或移位来强制对填充位进行这些无效访问旋转;但是处理器可能会提供更方便的指令来处理内存中甚至寄存器中一个字单元内的位,以使您的位域不会溢出,也不会因为对其值进行算术运算而修改其他周围的位域或填充位)
同样,您的nodeArray[i]
返回对节点对象的引用,而不是指针,因此nodeArray[i]->anything
无效:您需要替换->
中的.
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。