简体   繁体   English

Struct条目声明了一个数组类型的字段,但是编译器说这是一个指针

[英]Struct entry declares a field of type array, but compiler says it's a pointer

I'm trying to swap two entries in a struct. 我正在尝试在结构中交换两个条目。

This is the struct: 这是结构:

struct hdr {
    uint8_t ether_dhost[6];
    uint8_t ether_shost[6]; 
}

When I try the save these values in temporary arrays, I get this error on this line: 当我尝试将这些值保存到临时数组中时,在此行出现此错误:

uint8_t original_dhost[6];
original_dhost = ethernet_hdr->ether_dhost;

incompatible types when assigning to type 'uint8_t[6]' from type 'uint8_t *' 从'uint8_t *'类型分配给'uint8_t[6]'类型时不兼容的类型

so instead I try using a pointer rather than an array: 因此,我尝试使用指针而不是数组:

uint8_t *original_dhost;

Then I get no error, but when I try to assign to the ethernet_hdr->ether_dhost , I get this error: 然后我没有收到任何错误,但是当我尝试分配给ethernet_hdr->ether_dhost ,出现了以下错误:

ethernet_hdr->ether_shost = original_dhost;
 incompatible types when assigning to type 'uint8_t[6]' from type 'uint8_t *' 

How can I avoid the first error above? 如何避免上述第一个错误? Specifically, why does the compiler say the field is 'uint8_t *' when I declare it as an array? 具体来说,当我将其声明为数组时,为什么编译器会说该字段为'uint8_t *'

ether_dhost is an array. ether_dhost是一个数组。 You can't copy to or from it using a simple assignment statement. 您不能使用简单的赋值语句向其复制或复制。

Your first error comes because ethernet_hdr->ether_dhost resolves to the address of the first element (a uint8_t pointer), but you can't assign it's value to a new array. 您的第一个错误是因为ethernet_hdr->ether_dhost解析为第一个元素的地址(一个uint8_t指针),但是您无法将其值分配给新数组。

You need to use memcpy (or a loop) to copy all the elements: 您需要使用memcpy(或循环)来复制所有元素:

uint8_t original_dhost[6];
memcpy(original_dhost,ethernet_hdr->ether_dhost,sizeof(original_dhost));

There are several issues at play here. 这里有几个问题。

First of all, an array expression may not be the target of an assignment. 首先,数组表达式可能不是赋值的目标。 You cannot write something like 你不能写这样的东西

uint8_t original_dhost[6];
original_dhost = ethernet_hdr->ether_dhost;

because the expression original_dhost is not a modifiable lvalue. 因为表达式original_dhost不是可修改的左值。 There are reasons for this, which will become apparent below. 这是有原因的,下面将变得很明显。 To copy the contents of one array to another, you will either need to copy each element individually, or use the memcpy library function: 要将一个数组的内容复制到另一个数组,您将需要分别复制每个元素,或者使用memcpy库函数:

memcpy( original_dhost, ethernet_hdr->ether_dhost, sizeof original_dhost );

Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize an array in a declaration, an expression of type "N-element array of T " will be converted ("decay") to an expression of type "pointer to T ", and the value of the expression will be the address of the first element of the array. 除非它是sizeof或一元&运算符的操作数,或者是用于在声明中初始化数组的字符串文字,否则类型为“ T N元素数组”的表达式将被转换(“ decay”)为类型为“指向T指针”的表达式,该表达式的值将是数组第一个元素的地址。 The result is not a modifiable lvalue; 结果不是可修改的左值; that is, it cannot be the target of an assignment. 也就是说,它不能成为任务的目标。

In the statement 在声明中

original_dhost = ethernet_hdr->ether_dhost;

the expression ethernet_hdr->ether_dhost has type "6-element array of uint8_t "; 表达式ethernet_hdr->ether_dhost类型为“ uint8_t 6元素数组”; since it is not the operand of either the sizeof or unary & operators, it is converted to an expression of type "pointer to uint8_t ", or uint8_t * . 由于它不是sizeof或一元&运算符的操作数,因此将其转换为类型为“指向uint8_t ”或uint8_t *的表达式。 This type is not compatible with uint8_t [6] , hence the first error. 此类型与uint8_t [6]不兼容,因此是第一个错误。 The second error is the same problem, you've just reversed the players involved. 第二个错误是同样的问题,您刚刚扭转了所涉及的参与者。

So why not simply convert the left hand side of the assignment to a pointer as well and let the assignment succeed? 那么,为什么不简单地将赋值的左侧也转换为指针并让赋值成功呢? Time for a short history lesson. 时间短的历史课。

C was derived from an earlier language called B, which was a "typeless" language; C是从一种叫做B的较早语言衍生而来的,B是一种“无类型”语言。 all data were stored in fixed-size words, or "cells", regardless of whether the data were being used to represent integers, real values, text, whatever. 所有数据均以固定大小的单词或“单元格”存储,无论该数据是否用于表示整数,实数值,文本等。 Memory was treated as a linear array of cells. 记忆被视为细胞的线性阵列。 When you declared an array in B, such as 当您在B中声明数组时,例如

auto arr[N];

the compiler would set aside N+1 cells; 编译器将预留N + 1个单元; N cells for the array, and an additional cell that stored the offset to the first element of the array, which would be bound to the symbol arr , like so: 数组的N个单元格,以及将偏移量存储到数组的第一个元素的附加单元格,该元素将绑定到符号arr ,如下所示:


            +---+
  arr:      |   | ---+
            +---+    |
             ...     |
            +---+    |
  arr[0]:   |   | <--+
            +---+
  arr[1]:   |   |
            +---+
  arr[2]:   |   |
            +---+
             ...
            +---+
  arr[N-1]: |   |
            +---+

As in C, subscript operations like arr[i] were computed as *(arr + i) ; 如在C中一样,下标运算(如arr[i] )计算为*(arr + i) you added the value i to the offset value stored in arr , then dereferenced the result. 您将值i添加到存储在arr的偏移值中,然后取消了对结果的引用。

Dennis Ritchie initially kept B's array semantics as he was developing C, but he ran into a problem when he started adding the struct type to the language. 丹尼斯·里奇(Dennis Ritchie)最初在开发C时保留B的数组语义,但是当他开始向语言中添加struct类型时遇到了一个问题。 He wanted the struct contents to map directly onto memory; 他希望将结构内容直接映射到内存中。 an example he gives is of a file system entry, like 他给出的一个示例是文件系统条目,例如

struct {
  int inode;
  char name[14];
};

He wanted the struct to contain a 2-byte integer value immediately followed by a 0-terminated string, but he couldn't figure out what to do with the pointer to the name array: should it be stored as part of the struct, or stored separately? 他希望该结构立即包含一个2字节的整数值,后跟一个0终止的字符串,但他不知道如何处理指向name数组的指针:应该将其存储为结构的一部分,还是分开存放? If separately, where should it be stored? 如果分开存放,应将其存放在哪里

He solved the problem by getting rid of the array pointer altogether; 他彻底摆脱了数组指针,从而解决了这个问题。 instead of setting aside storage for a pointer to the first element of the array, he designed the language so that the pointer value would be computed from the array expression itself. 他没有为指针留出空间来存储指向数组第一个元素的方法,而是设计了语言,以便可以从数组表达式本身计算指针值。 Thus, in C, when you declare an array like 因此,在C中,当您声明类似

T arr[N];

only N elements of type T are allocated: 仅分配了T类型的N个元素:


            +---+    
  arr[0]:   |   | 
            +---+
  arr[1]:   |   |
            +---+
  arr[2]:   |   |
            +---+
             ...
            +---+
  arr[N-1]: |   |
            +---+

There's no separate storage bound to the symbol arr . 符号arr没有绑定单独的存储。 This is why the expressions &arr and arr both yield the same value (the address of the first element in the array), even though the two expressions have different types ( T (*)[N] and T * , respectively). 这就是为什么即使两个表达式具有不同的类型 (分别为T (*)[N]T * ),表达式&arrarr都产生相同的 (数组中第一个元素的地址)的原因。 And this is why an array expression may not be the target of an assignment; 这就是为什么数组表达式可能不是赋值的目标的原因。 there's nothing to assign a value to . 没有什么可以分配一个

For no particular reason (besides some historical accident), in C you cannot assign arrays directly; 出于特殊原因(除了一些历史事故),您无法在C中直接分配数组。 you have to copy elements one by one (or use memcpy). 您必须一个一个地复制元素(或使用memcpy)。

memcpy(ethernet_hdr->ether_shost,original_dhost,sizeof(original_dhost));

An array is a pointer to a fixed-number of contiguous objects. 数组是指向固定数量的连续对象的指针。 So to achieve your desired move, use memcpy . 因此,要实现所需的动作,请使用memcpy

Struct entry declares a field of type array, but compiler says it's a pointer Struct条目声明了一个数组类型的字段,但是编译器说这是一个指针

Because arrays almost always decay (are implicitly converted) into pointers when being referred to. 因为数组在被引用时几乎总是衰减(隐式转换)为指针。

when I try to assign to the ethernet_hdr->ether_dhost, I get this error: 当我尝试分配给ethernet_hdr-> ether_dhost时,出现以下错误:

Sure. 当然。 Arrays are not modifiable lvalues; 数组不是可修改的左值; if you want to "assign" them, you can't do that directly. 如果要“分配”它们,则不能直接执行。 Use memcpy() instead. 使用memcpy()代替。

The compiler is not saying ethernet_hdr->ether_dhostt is a 'uint8_t * . 编译器没有说ethernet_hdr->ether_dhostt'uint8_t *
It is saying that the right-hand-side of the = is a uint8_t * as that is how arrays are converted when used as an R-value. 这就是说=的右侧是uint8_t *因为这是将数组用作R值时如何对其进行转换。

C11 6.3.2.1 3 "Except when it is the operand of the sizeof operator, ..., or the unary & operator, or is a string literal used to initialize an array, an expression that has type ''array of type'' is converted to an expression with type ''pointer to type'' that points to the initial element of the array object and is not an lvalue. ..." C11 6.3.2.1 3“除了它是sizeof运算符,...或一元&运算符的操作数,还是用于初始化数组的字符串文字外,其表达式的类型为” array of type”转换为类型为“指向类型的指针”的表达式,该表达式指向数组对象的初始元素,而不是左值。

Had OP performed the following, the expected result of 6 would occur for ethernet_hdr->ether_dhost is an array and not a pointer. 如果OP执行以下操作,则ethernet_hdr->ether_dhost是数组而不是指针的预期结果为6

printf("%zu\n", sizeof(ethernet_hdr->ether_dhost));

OP can avoid this a number of ways. OP可以通过多种方法来避免这种情况。

  1. Other have mentioned using memcpy() . 其他人提到使用memcpy()

  2. Assignment with arrays does not work, but assignment with structures do. 使用数组进行分配不起作用,但是使用结构进行分配。 By enclosing each array in a structure, the whole structure may be assigned. 通过将每个数组封闭在一个结构中,可以分配整个结构。

  3. There is a pre-C89 method, maybe-non standard way too. 有C89之前的方法,也可能是非标准方法。 Not detailed here. 这里不详细。

.

#include <stdio.h>
#include <stdlib.h>

struct U6 {
  uint8_t u[6];
};

struct hdr {
  struct U6 ether_dhost;
  struct U6 ether_shost;
};

struct hdr *ethernet_hdr;

void foo3(void) {
  struct U6 original_dhost = {{ 1,2,3,4,5,6}};
  ethernet_hdr = malloc(sizeof(*ethernet_hdr));
  ethernet_hdr->ether_dhost.u[1] = 7;
  // Copy structure
  original_dhost = ethernet_hdr->ether_dhost;
  printf("%u\n", original_dhost.u[1]);
}

int main() {
  foo3();
  return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM