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
在某些情況下,一個資料結構之定義的尾端會包含一個選用區塊。
#include <stdio.h>
#include <stdlib.h>
struct abc {
int age;
char *name[20];
//...
//char *placeholder;
char placeholder[0];
};
int main()
{
char buffer[30];
struct abc *data = malloc(sizeof(struct abc) + 100);
//placeholder會指向後面多的那100
printf("sizeof(struct abc) = %zu\n", sizeof(struct abc));
printf("data = %p, placeholder = %p\n", data, data->placeholder);
return 0;
}
$ ./a.out
sizeof(struct abc) = 168
data = 0x555c65741010, placeholder = 0x555c657410b8
選用區塊從placeholder起算。
placeholder被定義成大小為0的向量。也就是說,abc被分配時若包含這個選用區塊,placeholder就會指向此區塊的開端處。不需要選用區塊時,placeholder就只是一個指向此結構尾端的指標;不會耗用任何空間。
include/net/inet_sock.h
/** struct ip_options - IP Options
*
* @faddr - Saved first hop address
* @nexthop - Saved nexthop address in LSRR and SSRR
* @is_strictroute - Strict source route
* @srr_is_hit - Packet destination addr was our one
* @is_changed - IP checksum more not valid
* @rr_needaddr - Need to record addr of outgoing dev
* @ts_needtime - Need to record timestamp
* @ts_needaddr - Need to record addr of outgoing dev
*/
struct ip_options {
__be32 faddr;
__be32 nexthop;
unsigned char optlen;
unsigned char srr;
unsigned char rr;
unsigned char ts;
unsigned char is_strictroute:1,
srr_is_hit:1,
is_changed:1,
rr_needaddr:1,
ts_needtime:1,
ts_needaddr:1;
unsigned char router_alert;
unsigned char cipso;
unsigned char __pad2;
unsigned char __data[0];
};
net/ipv4/ip_sockglue.c
static int do_ip_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen, unsigned int flags)
{
...
switch (optname) {
case IP_OPTIONS:
{
unsigned char optbuf[sizeof(struct ip_options)+40];
struct ip_options *opt = (struct ip_options *)optbuf;
struct ip_options_rcu *inet_opt;
inet_opt = rcu_dereference_protected(inet->inet_opt,
lockdep_sock_is_held(sk));
opt->optlen = 0;
if (inet_opt)
memcpy(optbuf, &inet_opt->opt,
sizeof(struct ip_options) +
inet_opt->opt.optlen);
release_sock(sk);
if (opt->optlen == 0)
return put_user(0, optlen);
ip_options_undo(opt);
len = min_t(unsigned int, len, opt->optlen);
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, opt->__data, len))
return -EFAULT;
return 0;
}
...
}
net/ipv4/ip_output.c
static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
struct ipcm_cookie *ipc, struct rtable **rtp)
{
...
opt = ipc->opt;
if (opt) {
if (!cork->opt) {
cork->opt = kmalloc(sizeof(struct ip_options) + 40,
sk->sk_allocation);
if (unlikely(!cork->opt))
return -ENOBUFS;
}
memcpy(cork->opt, &opt->opt, sizeof(struct ip_options) + opt->opt.optlen);
cork->flags |= IPCORK_OPT;
cork->addr = ipc->addr;
}
...
}
note:
➠ 用在不確定後面會不會有payload的而且是不一定長度的時候
➠ 要小心資料對齊問題
➠ 主要是減少一次記憶體分配,釋放時也少一次,比較方便
參考資料
https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
沒有留言:
張貼留言