Array initialisation warning - c

I'm trying to initialise a structure which ends with an array[0] (here, char iedata[0]) for the actual packet payload. If I try to initialise it inline, like this:
struct some_packet pkt = {
.elem1 = blah, .elem2 = bleh,
.iedata = {
1, 2, 3, 4
}
};
I get a warning from gcc:
warning: (near initialization for ‘pkt.iedata’)
Is there any good way to mark that this is a proper initialisation?

If you're able to compile in C99 mode, you could try using standard flexible length arrays rather than the zero-length hack: http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
Note that in GCC 3.0 and newer, extra entries in an array initialiser will be discarded (per the documentation linked above).

As you are using C99 initialization, why not make the member a proper FAM, i.e. char data[];
The only way to create valid struct's with a FAM (or struct hack member) is by dynamically allocating the correct amount of excess storage for the last member so, as the warning suggests, your local initialization isn't valid.

Related

C: Reading 8 bytes from a region of size 0 [-Wstringop-overread] [duplicate]

Just curious, what actually happens if I define a zero-length array int array[0]; in code? GCC doesn't complain at all.
Sample Program
#include <stdio.h>
int main() {
int arr[0];
return 0;
}
Clarification
I'm actually trying to figure out if zero-length arrays initialised this way, instead of being pointed at like the variable length in Darhazer's comments, are optimised out or not.
This is because I have to release some code out into the wild, so I'm trying to figure out if I have to handle cases where the SIZE is defined as 0, which happens in some code with a statically defined int array[SIZE];
I was actually surprised that GCC does not complain, which led to my question. From the answers I've received, I believe the lack of a warning is largely due to supporting old code which has not been updated with the new [] syntax.
Because I was mainly wondering about the error, I am tagging Lundin's answer as correct (Nawaz's was first, but it wasn't as complete) -- the others were pointing out its actual use for tail-padded structures, while relevant, isn't exactly what I was looking for.
An array cannot have zero size.
ISO 9899:2011 6.7.6.2:
If the expression is a constant expression, it shall have a value greater than zero.
The above text is true both for a plain array (paragraph 1). For a VLA (variable length array), the behavior is undefined if the expression's value is less than or equal to zero (paragraph 5). This is normative text in the C standard. A compiler is not allowed to implement it differently.
gcc -std=c99 -pedantic gives a warning for the non-VLA case.
As per the standard, it is not allowed.
However it's been current practice in C compilers to treat those declarations as a flexible array member (FAM) declaration:
C99 6.7.2.1, §16: As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member.
The standard syntax of a FAM is:
struct Array {
size_t size;
int content[];
};
The idea is that you would then allocate it so:
void foo(size_t x) {
Array* array = malloc(sizeof(size_t) + x * sizeof(int));
array->size = x;
for (size_t i = 0; i != x; ++i) {
array->content[i] = 0;
}
}
You might also use it statically (gcc extension):
Array a = { 3, { 1, 2, 3 } };
This is also known as tail-padded structures (this term predates the publication of the C99 Standard) or struct hack (thanks to Joe Wreschnig for pointing it out).
However this syntax was standardized (and the effects guaranteed) only lately in C99. Before a constant size was necessary.
1 was the portable way to go, though it was rather strange.
0 was better at indicating intent, but not legal as far as the Standard was concerned and supported as an extension by some compilers (including gcc).
The tail padding practice, however, relies on the fact that storage is available (careful malloc) so is not suited to stack usage in general.
In Standard C and C++, zero-size array is not allowed..
If you're using GCC, compile it with -pedantic option. It will give warning, saying:
zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]
In case of C++, it gives similar warning.
It's totally illegal, and always has been, but a lot of compilers
neglect to signal the error. I'm not sure why you want to do this.
The one use I know of is to trigger a compile time error from a boolean:
char someCondition[ condition ];
If condition is a false, then I get a compile time error. Because
compilers do allow this, however, I've taken to using:
char someCondition[ 2 * condition - 1 ];
This gives a size of either 1 or -1, and I've never found a compiler
which would accept a size of -1.
Another use of zero-length arrays is for making variable-length object (pre-C99). Zero-length arrays are different from flexible arrays which have [] without 0.
Quoted from gcc doc:
Zero-length arrays are allowed in GNU C. They are very useful as the last element of a structure that is really a header for a variable-length object:
struct line {
int length;
char contents[0];
};
struct line *thisline = (struct line *)
malloc (sizeof (struct line) + this_length);
thisline->length = this_length;
In ISO C99, you would use a flexible array member, which is slightly different in syntax and semantics:
Flexible array members are written as contents[] without the 0.
Flexible array members have incomplete type, and so the sizeof operator may not be applied.
A real-world example is zero-length arrays of struct kdbus_item in kdbus.h (a Linux kernel module).
I'll add that there is a whole page of the online documentation of gcc on this argument.
Some quotes:
Zero-length arrays are allowed in GNU C.
In ISO C90, you would have to give contents a length of 1
and
GCC versions before 3.0 allowed zero-length arrays to be statically initialized, as if they were flexible arrays. In addition to those cases that were useful, it also allowed initializations in situations that would corrupt later data
so you could
int arr[0] = { 1 };
and boom :-)
Zero-size array declarations within structs would be useful if they were allowed, and if the semantics were such that (1) they would force alignment but otherwise not allocate any space, and (2) indexing the array would be considered defined behavior in the case where the resulting pointer would be within the same block of memory as the struct. Such behavior was never permitted by any C standard, but some older compilers allowed it before it became standard for compilers to allow incomplete array declarations with empty brackets.
The struct hack, as commonly implemented using an array of size 1, is dodgy and I don't think there's any requirement that compilers refrain from breaking it. For example, I would expect that if a compiler sees int a[1], it would be within its rights to regard a[i] as a[0]. If someone tries to work around the alignment issues of the struct hack via something like
typedef struct {
uint32_t size;
uint8_t data[4]; // Use four, to avoid having padding throw off the size of the struct
}
a compiler might get clever and assume the array size really is four:
; As written
foo = myStruct->data[i];
; As interpreted (assuming little-endian hardware)
foo = ((*(uint32_t*)myStruct->data) >> (i << 3)) & 0xFF;
Such an optimization might be reasonable, especially if myStruct->data could be loaded into a register in the same operation as myStruct->size. I know nothing in the standard that would forbid such optimization, though of course it would break any code which might expect to access stuff beyond the fourth element.
Definitely you can't have zero sized arrays by standard, but actually every most popular compiler gives you to do that. So I will try to explain why it can be bad
#include <cstdio>
int main() {
struct A {
A() {
printf("A()\n");
}
~A() {
printf("~A()\n");
}
int empty[0];
};
A vals[3];
}
I am like a human would expect such output:
A()
A()
A()
~A()
~A()
~A()
Clang prints this:
A()
~A()
GCC prints this:
A()
A()
A()
It is totally strange, so it is a good reason not to use empty arrays in C++ if you can.
Also there is extension in GNU C, which gives you to create zero length array in C, but as I understand it right, there should be at least one member in structure prior, or you will get very strange examples as above if you use C++.

Why does splint suggest that { 0 } doesn't really initialize all elements to zero in C: Initializer does not define all elements of a declared array

In this piece of code (the whole file contains only one line):
char buffer[256] = { 0 };
Checked with Splint, I got the following hint:
foo.c(1,20): Initializer block for buffer has 1 element, but declared as char
[256]: 0
Initializer does not define all elements of a declared array. (Use
-initallelements to inhibit warning)
Initializer does not define all elements of a declared array. This is puzzling: I read some SO answers, but all of them claims that { 0 } does initialize all elements to zero.
Splint version:
Splint 3.1.1 --- 12 April 2003
Maintainer: splint-bug#splint.org
Compiled using Microsoft Visual C++ 6.0
Downloaded from Splint.org.
Yes, it does zero all the elements. The general rule is that if you provide less initializers than there are elements, the remaining elements are zeroed. (So e.g. char buffer[256] = {1}; will set only the first element to 1, and the rest to 0.)
The warning doesn't say that remaining elements are uninitialized, it says you didn't provide initializers for them. This warning doesn't make sense in this specific case (= {0} is a common pattern to zero an array), but in general it could be useful (e.g. it would warn about int arr[4] = {1,2,3};).
For char array like that, the most common (and safe) way seems to be:
char buffer[256] = "";
It may be initialised using empty {} just like other struct types:
char buffer[256] = {}; // not working in C, only work for C++
Using {0} seems to be more C++ way to initialise struct type.

Visual studio treats incomplete arrays as zero-length arrays

I have a struct that looks sort of like this:
typedef struct foo {
int this;
int that;
int length;
int info[]; // legal for last element of a struct
} Foo;
When I compile it, I get an warning like this:
C4200 nonstandard extension used: zero-sized array in struct/union
Do I just live with the warning, or is there some property I can set to tell Visual Studio to use C-99?
Visual Studio 2015 [almost] fully implements C99, but still treats all C99 features as language extensions (e.g. disabling language extensions disables C99 support as well). Some of these features trigger spurious warnings like the one you observed.
As long as C99 support remains in this semi-official "extension" status, just ignore/disable such warnings.
Note that VS2015 Update 3 no longer issues this warning for such C code.
For those who are curious about the zero-length array or "flexible array" idioms, it's probably worth taking a minute to explain them. This idiom is as old as C itself.
Say you want to pass around a structure that consists of a header and a variable amount of data. It's not known until the structure is allocated how much data will need to be added to it.
The original idiom was to declare the structure like this:
/* Variation 1 */
struct mydata {
int type;
int datalen;
char data[1];
};
Then suppose we wanted to return one of these objects:
struct mydata *
get_some_data()
{
int len;
struct mydata *rval;
len = find_out_how_much_data();
/* Allocate the struct AND enough extra space to hold the data */
rval = malloc(sizeof(*rval) + len - 1);
rval->datalen = len;
read_data(&rval->data[0], len);
return rval;
}
And a caller would access it like this:
void caller()
{
struct mydata *foo = get_some_data();
/* Start accessing foo->datalen bytes of data starting at
* foo->data[0]
*/
free(foo); /* And free it all */
}
The point to this idiom is that the char data[1] declaration is a lie, since the data will surely be longer than that, but the C compiler doesn't do range checking, so everything is cool.
But notice the len - 1 expression in the malloc. This is necessary because declaring data to have a length 1 introduces an off-by-one error into everything, and invites coders to make mistakes.
So both GNU and Microsoft added an extension to the language that allows you to declare an array of length zero:
/* Variation 2 */
struct mydata {
int type;
int datalen;
char data[0];
};
While on the surface, this is nonsensical, it dovetails neatly with the idiom used here. Now we can simply do:
rval = malloc(sizeof(*rval) + len);
and the code is much cleaner.
C99 formalized this idiom by acknowledging that the length of the array is a lie, but the ability to have extra data at the end of the structure comes in very handy. So now you declare:
/* Variation C99 */
struct mydata {
int type;
int datalen;
char data[];
};
and everything is coded up exactly as with the Gnu/Microsoft extension.
Unfortunately, it seems that Microsoft hasn't adopted C99 standards into their compiler, so no matter what you do, variations 2 and C99 will generate a warning. It looks like my only choices are to either live with the warning message or add a pragma to suppress it.
Linux users may amuse themselves by executing gr -r '\[0\]' /usr/include and seeing how many places use zero-length arrays. This is a very commonly used idiom.
As to my own issue: the struct I'm working with is actually part of an ioctl. The driver has already been written and I can't change it. The very most I can do is redefine the array from zero-length to flexible. Unfortunately, neither option makes the MSVC compiler happy.
When you create an array of undefined length (in your case in a struct) it is unknown how much memory needs to be allocated for it. When using variable 'info' it is therefore unknown where in the cache memory of the computer the program is writing or reading. This can make the program crash. Normally when you want to use an array of undefined length a pointer is used. Memory can be allocated for this pointer when it is known how large the array should be.
The compiler warns you about the problem I described above but lets you execute the code (at least it does in Microsoft Visual studio 2013).
Hope this helps you!

Declare Global Struct Variable With Variable Size In C

I have a struct that looks like this.
struct MyStruct1 {
int (*fn)();
int b;
}
And another struct that looks like this.
struct MyStruct2 {
int a;
struct MyStruct1 b[0];
}
I would like to declare a global variable of type MyStruct2, somewhat like this.
int func1() { return 1; }
int func2() { return 2; }
struct MyStruct2 a = { 1, { func1, 5 }, { func2, 6 } };
However, I get a "Initializer Element is not a compile-time constant".
I would like to know (a) if it is even possible to globally declare a variable sized struct (or at least define a chunk of space, of the correct size, to have the values inserted into later), and (b) if it is, what am I doing wrong?
It is not possible to declare a variable-size struct, neither locally nor globally. Every type has a fixed size determined at compile time.
The error message you report is surprising, however. If all the declarations you gave are at file scope, in the same file, in the order you gave them, then the initializer for variable a is a compile-time constant. However, it is not a proper initializer for a struct MyStruct2,
because it specifies more elements than that struct type has members,
because the initializer element for a.b is an initializer for a struct MyStruct1 instead of for an array of such, and
because even if you converted the last two initializer elements into one array initializer, it has more elements than there are elements in a.b (i.e. more than zero).
If you want a dynamically-sized array, whether as a variable in its own right or as a member of a struct, then you must declare a pointer to it, and allocate memory for the elements dynamically. In that case, the elements are not themselves part of the struct; only the pointer to them is. (That is different, by the way, from a fixed size array whose size is implicit in its initializer; these are possible only for independent types, though, not for types of struct or union members).
EDIT:
C99 flexible arrays are a possible alternative, as ShafikYaghmour commented. These are similar, but not identical, to a struct element that is a pointer to a dynamically-allocated array. In that case, however, you not only cannot statically declare the array elements, you also cannot statically declare instances of the struct itself, so this wouldn't at all get around your initializer issue. There are several other quirks and limitations. Personally I see few advantages to flexible arrays, but they do make it a bit easier to properly free struct instances.
You cannot create arrays of size 0 in C legitimately. In C99 or C11, you can use a 'flexible array member' like this:
struct MyStruct2 {
int a;
struct MyStruct1 b[];
};
but structures that have a flexible array member can only usefully be created with dynamic memory allocation (other forms of allocation give you an unusable flexible array of size 0).
The older 'struct hack' version of a structure with a variable size array uses an array of size 1 in the structure. You can create global versions of such a structure with an array of size 1.
But basically, you are trying to do what the language prohibits you from doing, and not very surprisingly, you are failing.
What you do about this depends on what you need. Global variables are inherently somewhat undesirable, so there's an element of "you should be trying to avoid doing this". That said, the rules apply to file scope (static) variables too, and those have many uses.
You can use an explicit pointer in place of the array, and have separate allocations of the body of the struct MyStruct2 and its array of struct MyStruct1 members. You can forgo the global variable and use dynamically allocated structures with a flexible array member.
struct MyStruct2 *ms2 = malloc(sizeof(*ms2) + N * sizeof(ms2->b[0]));
This creates an struct MyStruct2 (as shown at the top of this answer) with N members in the array. Without any further changes, you can use ms2->b[0] through ms2->b[N-1] (well, apart from error checking that the malloc() succeeded).

error: field has incomplete type

I am attempting to port a library to Mac OS X. The compiler is reporting an incomplete type error. Specifically: field has incomplete type 'header_t []. However, when I look at the source code, header_t is defined just before packet_state_t, where packet_state_t references header_t. Thus, there shouldn't be any forward reference error, since header_t is clearly defined at the point at which it is referenced inside packet_state_t. The line in which the error occurs is marked with ERROR below. How to resolve?
typedef struct header_t {
uint8_t hdr_id; // header ID
uint8_t hdr_prefix; // length of the prefix (preamble) before the header
uint8_t hdr_gap; // length of the gap between header and payload
uint16_t hdr_flags; // flags for this header
uint16_t hdr_postfix; // length of the postfix (trailer) after the payload
uint32_t hdr_offset; // offset into the packet_t->data buffer
uint32_t hdr_length; // length of the header in packet_t->data buffer
uint32_t hdr_payload; // length of the payload
uint8_t hdr_subcount; // number of sub-headers
header_t *hdr_subheader; // Index of the first subheader in packet_t
jobject hdr_analysis; // Java JAnalysis based object if not null
} header_t;
typedef struct packet_state_t {
flow_key_t pkt_flow_key; // Flow key calculated for this packet, must be first
uint8_t pkt_flags; // flags for this packet
jobject pkt_analysis; // Java JAnalysis based object if not null
uint64_t pkt_frame_num; // Packet's frame number assigned by scanner
uint64_t pkt_header_map; // bit map of presence of headers
uint32_t pkt_wirelen; // Original packet size
uint32_t pkt_buflen; // Captured length
int8_t pkt_header_count; // total number of main headers found
header_t pkt_headers[]; // One per header + 1 more for payload ERROR HERE!!!
int8_t pkt_subheader_count; // total number of sub headers found
header_t pkt_subheaders[]; // One per header + 1 more for payload
} packet_state_t;
The type header_t is just fine, but the compiler is actually complaining about the type header_t[], i.e. "array of indeterminate length of header_t", which has an incomplete type because the compiler doesn't know how big it is (it couldn't possibly).
C99 (but not C89) supports what's called a flexible array member in structures, which is exactly this, but only at the end of a structure:
struct X {
// any member declarations
...
AnyType lastMemberArray[]; // This must be the LAST member
};
This is allowed, but it makes the structure you declared also an incomplete type because again, the compiler doesn't know how big it is. The only way to use it is to dynamically allocate memory of the required size or to cast an already allocated block of memory. For example:
// Allocate an X instance with space for 3 members in lastMemberArray:
X *x = malloc(sizeof(X) + 3 * sizeof(AnyType));
// Can now use x->lastMemberArray[0] through x->lastMemberArray[2]
...
// Alternatively:
char buffer[sizeof(X) + 3 * sizeof(AnyType)];
X *x = (X *)buffer;
// Same as above
Why does the flexible array member have to come last in the struct? Imagine if other members came after it. How could the compiler generate code to access those members?
// If this were allowed:
struct X {
AnyType flexibleArray[];
int memberAfter;
};
void doSomething(X *x) {
// How does the compiler generate code for this? It doesn't know what offset
// memberAfter is from the start of the object, because the array doesn't
// have a known size
printf("memberAfter = %d\n", x->memberAfter);
}
Hence, you can't have a structure with more than one flexible array member, because then clearly one of them wouldn't be the last structure member, so your definition is not allowed.
Whatever library code you're writing, it couldn't have used two flexible array members to begin with, or it wouldn't have compiled on any platform. I suggest you investigate the original code to find out what it did; if it's using standard ISO C features without relying on any platform-specific or implementation-specific behavior or extensions, you should have no trouble porting it.
Without being able to see the original code, I'd recommend you switch from using an inline flexible array member to a dynamically allocated array with a pointer, at least for the first array (and probably also the second for consistency):
typedef struct packet_state_t {
flow_key_t pkt_flow_key; // Flow key calculated for this packet, must be first
uint8_t pkt_flags; // flags for this packet
jobject pkt_analysis; // Java JAnalysis based object if not null
uint64_t pkt_frame_num; // Packet's frame number assigned by scanner
uint64_t pkt_header_map; // bit map of presence of headers
uint32_t pkt_wirelen; // Original packet size
uint32_t pkt_buflen; // Captured length
int8_t pkt_header_count; // total number of main headers found
header_t *pkt_headers; // POINTER here, not an array
int8_t pkt_subheader_count; // total number of sub headers found
header_t *pkt_subheaders; // POINTER here, not an array
} packet_state_t;
EDIT
I downloaded the jnetpcap code and compiled it on Linux to see what happened. To my surprise, it compiled. The compiler command invoked was:
gcc -c -fPIC -DLIBPCAP_VERSION=0x1532 -I/tmp/jnetpcap/build/include -I/tmp/jnetpcap/src/c -I/usr/lib/jvm/default-java/include -I/usr/lib/jvm/default-java/include/linux /tmp/jnetpcap/src/c/jnetpcap.cpp /tmp/jnetpcap/src/c/packet_flow.cpp /tmp/jnetpcap/src/c/packet_jheader.cpp /tmp/jnetpcap/src/c/jnetpcap_pcap_header.cpp /tmp/jnetpcap/src/c/nio_jbuffer.cpp /tmp/jnetpcap/src/c/winpcap_stat_ex.cpp /tmp/jnetpcap/src/c/winpcap_send_queue.cpp /tmp/jnetpcap/src/c/winpcap_ext.cpp /tmp/jnetpcap/src/c/util_debug.cpp /tmp/jnetpcap/src/c/util_crc16.c /tmp/jnetpcap/src/c/jnetpcap_ids.cpp /tmp/jnetpcap/src/c/jnetpcap_dumper.cpp /tmp/jnetpcap/src/c/jnetpcap_utils.cpp /tmp/jnetpcap/src/c/util_in_cksum.cpp /tmp/jnetpcap/src/c/jnetpcap_beta.cpp /tmp/jnetpcap/src/c/nio_jmemory.cpp /tmp/jnetpcap/src/c/util_crc32.c /tmp/jnetpcap/src/c/packet_jsmall_scanner.cpp /tmp/jnetpcap/src/c/mac_addr_sys.c /tmp/jnetpcap/src/c/packet_protocol.cpp /tmp/jnetpcap/src/c/nio_jnumber.cpp /tmp/jnetpcap/src/c/packet_jheader_scanner.cpp /tmp/jnetpcap/src/c/library.cpp /tmp/jnetpcap/src/c/packet_jscan.cpp /tmp/jnetpcap/src/c/jnetpcap_pcap100.cpp /tmp/jnetpcap/src/c/mac_addr_dlpi.c /tmp/jnetpcap/src/c/util_checksum.cpp /tmp/jnetpcap/src/c/packet_jpacket.cpp /tmp/jnetpcap/src/c/winpcap_ids.cpp /tmp/jnetpcap/src/c/jnetpcap_bpf.cpp
So the first thing that's going on here is that this is C++, not C. C++ does not support flexible array members at all, although some compilers support them as extensions. C++03 §9.2/8 says:
[...] When an array is used as the type of a nonstatic member all dimensions shall be specified.
And C++11 §9.2/9 says:
9 Non-static (9.4) data members shall not have incomplete types. [...]
When I compile this code in g++ 4.8.2 with a higher warning level (-Wall -Wextra pedantic), it warns as follows:
In file included from /tmp/jnetpcap/src/c/packet_jscan.cpp:28:0:
/tmp/jnetpcap/src/c/packet_jscanner.h:287:23: warning: ISO C++ forbids zero-size array ‘pkt_headers’ [-Wpedantic]
header_t pkt_headers[]; // One per header + 1 more for payload
^
/tmp/jnetpcap/src/c/packet_jscanner.h:290:26: warning: ISO C++ forbids zero-size array ‘pkt_subheaders’ [-Wpedantic]
header_t pkt_subheaders[]; // One per header + 1 more for payload
So what g++ is doing (contrary to the C++ standard) is converting the arrays of unspecified size to arrays of size 0. The first one (pkt_headers) works exactly like the flexible array member in C99, in that if you've allocated the proper amount of memory, you can access the array members of that normally up to the maximum size. But if you ever access any members after that (particularly pkt_subheader_count and pkt_subheaders), the compiler generates code as if pkt_headers had size 0, i.e. if the struct were equivalent to this:
typedef struct packet_state_t {
flow_key_t pkt_flow_key; // Flow key calculated for this packet, must be first
uint8_t pkt_flags; // flags for this packet
jobject pkt_analysis; // Java JAnalysis based object if not null
uint64_t pkt_frame_num; // Packet's frame number assigned by scanner
uint64_t pkt_header_map; // bit map of presence of headers
uint32_t pkt_wirelen; // Original packet size
uint32_t pkt_buflen; // Captured length
int8_t pkt_header_count; // total number of main headers found
// NOTHING HERE (array of size 0)
int8_t pkt_subheader_count; // total number of sub headers found
header_t pkt_subheaders[]; // One per header + 1 more for payload
} packet_state_t;
Which would cause accesses to pkt_subheader_count (and probably also access to pkt_subheaders) to access the exact same memory as as pkt_headers[0].
Why does the happen to work out ok? Because the code in this project never accesses pkt_subheader_count or pkt_subheaders anywhere. If it did, the code wouldn't work for the reasons described above unless it got extraordinarily lucky. And it's not valid C++, it just happens to be accepted by the compiler.
Solution? Just remove pkt_subheader_count and pkt_subheaders from the structure declaration. They're not used anywhere in code, and removing them allows pkt_headers[] to be the last member of the structure, so it's a valid flexible array member, which is valid C99 or a non-standard compiler extension in C++.
In definition of struct header_t
Change
header_t *hdr_subheader; // Index of the first subheader in packet_t
to
struct header_t *hdr_subheader; // Index of the first subheader in packet_t

Resources