C 中&(((TYPE *)0)->member) 含义

文章探讨了C语言中利用空指针和成员变量地址来获取结构体地址的方法,特别是`offsetof`宏的实现。解释了`((TYPE*)0)->member`虽然看似访问空指针但实际被编译器优化为直接取地址,因此在计算结构体成员相对于首地址的偏移时不会出错。这种方法常用于Linuxkernel中的offsetof宏定义,用于计算结构体成员的偏移量。

C 语言中,根据成员变量地址获取结构体的 地址。有一种实现方法:

member_address - &(((TYPE *)0)->member);

这个里面最让人疑惑是&(((TYPE *)0)->member), 很明显是空指针嘛,空指针指向成员变量一定会出错的,但为啥着这里没有出错,而且linux kernel 中也经常使用这个。

原来,一切的奥秘都在编译器对& 的处理上,

在计算机中,要访问一个地址空间,必先知道他的地址, 然后才访问他对应的空间;

编译器在会将&(((TYPE *)0)->member)  优化为直接取地址,因为这种表达是: 先访问空间, 再取空间的地址,这不就相当于直接取地址么。

编译器一定会将其优化成为直接取地址,而不存在访问空间, 所以这种表述不会出现错误。


应用场景:
#define offsetof(type, field) (long)&(((type*)0)->field)
type:表示一个结构体
filed:表示type结构体的成员
(type*)0:应该是表示结构体首地址
(type*)0)->field:表示结构体成员field
&(((type*)0)->field):表示成员field相对于结构体首地址的偏移地址
作用:计算出type结构体成员filed在结构体中的偏移地址。
如:struct str
{
int a;
int b;
int c;
}
则:offsetof(str,a)=0;offsetof(str,b)=4;offsetof(str,c)=8;
 

int rtdDhcpServerAddTempBind(struct client_id *cid, int htype, char *haddr, int haddr_len, DHCP_SERVER_IP ipAddr, struct in_addr subnet, char *poolName, struct dhcp_binding *outBd,BOOL fromMlag) { struct dhcp_binding *binding = NULL; struct hash_member *head = NULL; //DHCP_SERVER_POOL poolParam={}; if (haddr == NULL) { return ERR_BAD_PARAM; } PFM_ENSURE_RET_VAL(poolName, ERR_NO_ERROR); //APPL_IF_ERR_RET(rtdDhcpsPoolGet(poolName,&poolParam )); DHCP_SERVER_RTD_LOCK(); /* * Read binding information from entries with the following format: * idtype:id:subnet:htype:haddr:"expire_date":resource_name */ DBG_DHCP_VRF("ADD TEMP BIND"); if (nbind < DHCPS_IP_MAX_NUM && availreslist == NULL) { nbind = DHCPS_IP_MAX_NUM; rtdDhcpServerGarbageCollect(); } /* Create linked list element. */ head = availreslist; if (availreslist != NULL) { availreslist = availreslist->next; head->next = bindlist; bindlist = head; binding = (struct dhcp_binding *)(head->data); binding->bindtype = AUTO_BIND_TYPE; //binding->ip_addr.ip.s_addr = ipAddr.s_addr; //strncpy(binding->ip_addr.vrfName,poolParam.vrfName,DHCP_VRF_NAME_LEN); COPY_DHCP_SERVER_IP(binding->ip_addr,ipAddr); /* flag set to 0 means not used */ binding->flag = FREE_ENTRY; binding->fromMlag=fromMlag; /* read client identifier value */ memcpy(&binding->cid, cid, sizeof(binding->cid)); /* read subnet number of client */ binding->cid.subnet = subnet; strncpy(binding->cid.vrfName,ipAddr.vrfName,DHCP_VRF_NAME_LEN); /* read hardware address type (e.g. "1" for 10 MB ethernet). */ binding->haddr.htype = htype; /* read client hardware address */ binding->haddr.hlen = haddr_len; memcpy(binding->haddr.haddr, haddr, haddr_len); /* read expiration time for lease */ binding->expire_epoch = 0; /* read expiration time for lease */ binding->temp_epoch = 0; /* if(dhcpsMsgIn.isRxUpMlag) { binding->fromMlag=TRUE; } else { binding->fromMlag=FALSE; } */ if (poolName) { snprintf(binding->poolName, DHCPS_POOLNAME_LEN + 1, "%s", poolName); } hash_del(&cidhashtable, binding->cid.id, binding->cid.idlen, bindcidcmp_not_manual, &binding->cid, free_bind); if ((hash_ins(&cidhashtable, binding->cid.id, binding->cid.idlen, bindcidcmp_not_manual, &binding->cid, binding) < 0)) { head = bindlist; if (bindlist) { bindlist = bindlist->next; } if (head) { head->next = availreslist; } availreslist = head; DHCP_SERVER_RTD_UNLOCK(); return ERR_BAD_PARAM; } hash_del(&iphashtable, (char *)&binding->ip_addr.ip.s_addr, sizeof(UINT32), resipcmp_not_manual, &binding->ip_addr, free_element); if (hash_ins(&iphashtable, (char *)&binding->ip_addr.ip.s_addr, sizeof(UINT32), resipcmp_not_manual, &binding->ip_addr, binding) < 0) { hash_del(&cidhashtable, binding->cid.id, binding->cid.idlen, resipcmp_not_manual, &binding->cid, free_bind); head = bindlist; if (bindlist) { bindlist = bindlist->next; } if (head) { head->next = availreslist; } availreslist = head; DHCP_SERVER_RTD_UNLOCK(); return ERR_BAD_PARAM; } //add_reslist_node(binding); } DBG_DHCP_MLAG("add a temp entry."); _printBinding(*binding); DBG_DHCP_VRF("add client identifier to hash table ok."); if (binding) { _turnoff_bind(binding, TEMP_ENTRY); if (outBd) { memcpy(outBd, binding, sizeof(struct dhcp_binding)); } } else/* binding 为空,添加临时绑定失败 */ { DHCP_SERVER_RTD_UNLOCK(); return ERR_DHCP_SERVER_ADD_TEMP_BIND; } DHCP_SERVER_RTD_UNLOCK(); return ERR_NO_ERROR; } 分析以上代码
07-03
STATUS rtdDhcpServerAddManualBind(int htype, char *haddr, int haddr_len, struct in_addr ipAddr, struct client_id *cid, char *poolName) { struct dhcp_binding *binding = NULL; struct dhcp_binding *res = NULL; struct dhcp_binding tempBind; struct hash_member *head = NULL; struct hash_member *bindptr = NULL; int result; if (haddr == NULL || NULL == poolName || NULL == cid) { return ERROR; } /* * Read binding information from entries with the following format: * idtype:id:subnet:htype:haddr:"expire_date":resource_name */ DBG_DHCP_VRF("type %d", htype); DBG_DHCP_VRF("ADD MANUAL BIND=%x%x%x%x%x%x", haddr[0], haddr[1], haddr[2], haddr[3], haddr[4], haddr[5]); /* Create linked list element. */ DHCP_SERVER_RTD_LOCK(); head = availreslist; if (availreslist != NULL) { availreslist = availreslist->next; head->next = bindlist; bindlist = head; binding = (struct dhcp_binding *)(head->data); binding->bindtype = MANUAL_BIND_TYPE; DHCP_SERVER_IP sIp={}; sIp.ip.s_addr=ipAddr.s_addr; strncpy(sIp.vrfName,cid->vrfName,DHCP_VRF_NAME_LEN); memcpy(&binding->ip_addr,&sIp,sizeof(sIp)); /* 静态绑定,但是还未被discover请求 */ binding->flag = FREE_ENTRY; /* read client identifier type */ binding->cid.idtype = cid->idtype; /* read client identifier value */ binding->cid.idlen = cid->idlen; memcpy(binding->cid.id, cid->id, cid->idlen); /* read subnet number of client */ binding->cid.subnet.s_addr = cid->subnet.s_addr; /* read hardware address type (e.g. "1" for 10 MB ethernet). */ binding->haddr.htype = htype; /* read client hardware address */ binding->haddr.hlen = haddr_len; memcpy(binding->haddr.haddr, haddr, haddr_len); /* read expiration time for lease */ binding->temp_epoch = binding->expire_epoch = DHCP_SERVER_FORERVER; /* read index of lease descriptor */ snprintf(binding->poolName, DHCPS_POOLNAME_LEN + 1, "%s", poolName); strncpy(binding->cid.vrfName,cid->vrfName,DHCP_VRF_NAME_LEN); /* 判断IP是否已被分配 */ res = (struct dhcp_binding *)hash_find(&iphashtable, (char *)&binding->ip_addr.ip.s_addr, sizeof(UINT32), resipcmp, &binding->ip_addr); /* 如果该manual绑定已经存在,直接返回添加成功 */ if (NULL != res && DHCP_SERVER_FORERVER == res->expire_epoch) { head = bindlist; if (bindlist) { bindlist = bindlist->next; } if (head) { head->next = availreslist; } availreslist = head; DHCP_SERVER_RTD_UNLOCK(); return OK; } if ((binding->haddr.htype == ETHERNET_BIND) || (binding->haddr.htype == IEEE802_BIND)) { DBG_DHCP_VRF("no haddr type"); bindptr = bindlist; while (bindptr != NULL) { res = (struct dhcp_binding *)((bindptr)->data); /* 客户端原先已有绑定记录,先删除 */ if (memcmp(res->haddr.haddr, binding->haddr.haddr, binding->haddr.hlen) == 0 && res->cid.idtype == binding->haddr.htype && hash_find(&iphashtable, (char *)&res->ip_addr.ip.s_addr, sizeof(UINT32), resipcmp, &res->ip_addr) != NULL && (0 == strcmp(res->poolName, binding->poolName))) { memcpy(&tempBind, res, sizeof(struct dhcp_binding)); DBG_DHCP_VRF("res ip %x", (UINT32)res->ip_addr.ip.s_addr); DBG_DHCP_VRF("res %p", res); DBG_DHCP_VRF("res->cid.idlen %d", res->cid.idlen); DBG_DHCP_VRF("res->cid=%x%x%x%x%x%x%x%x", res->cid.id[0], res->cid.id[1], res->cid.id[2], res->cid.id[3], res->cid.id[4], res->cid.id[5], res->cid.id[6], res->cid.id[7]); result = hash_del(&cidhashtable, res->cid.id, res->cid.idlen, bindcidcmp, &res->cid, free_bind); if (OK != result) { DBG_DHCP_VRF("remove manual bind err:hash del err cid"); goto ERR_RET; } result = hash_del(&iphashtable, (char *)&res->ip_addr.ip.s_addr, sizeof(UINT32), resipcmp, &res->ip_addr, free_element); if (OK != result) { DBG_DHCP_VRF("remove manual bind err:hash del err ip"); goto ERR_RET; } /* 若客户端原先为绑定状态,则对应地址池已分配地址-1 */ if (tempBind.flag == BIND_ENTRY) { del_bind(tempBind.poolName); } } bindptr = bindptr->next; } } else { /* 判断客户端cid是否已有绑定 */ res = (struct dhcp_binding *)hash_find(&cidhashtable, binding->cid.id, binding->cid.idlen, bindcidcmp, &binding->cid); if (res != NULL)/* 客户端已有绑定 需要先删除 */ { memcpy(&tempBind, res, sizeof(struct dhcp_binding)); result = hash_del(&cidhashtable, res->cid.id, res->cid.idlen, bindcidcmp, &res->cid, free_bind); if (OK != result) { DBG_DHCP_VRF("remove manual bind err:hash del err cid"); goto ERR_RET; } result = hash_del(&iphashtable, (char *)&res->ip_addr.ip.s_addr, sizeof(UINT32), resipcmp, &res->ip_addr, free_element); if (OK != result) { DBG_DHCP_VRF("remove manual bind err:hash del err ip"); goto ERR_RET; } res->flag = FREE_ENTRY; res->expire_epoch = 0; DBG_DHCP_VRF("remove manual bind success"); /* 若客户端原先为绑定状态,则对应地址池已分配地址-1 */ if (tempBind.flag == BIND_ENTRY) { del_bind(tempBind.poolName); } } } /* 判断IP是否已被分配 */ res = (struct dhcp_binding *)hash_find(&iphashtable, (char *)&binding->ip_addr.ip.s_addr, sizeof(UINT32), resipcmp, &binding->ip_addr); if (res != NULL) { memcpy(&tempBind, res, sizeof(struct dhcp_binding)); result = hash_del(&cidhashtable, res->cid.id, res->cid.idlen, bindcidcmp, &res->cid, free_bind); if (OK != result) { DBG_DHCP_VRF("remove manual bind err:hash del err cid"); goto ERR_RET; } result = hash_del(&iphashtable, (char *)&res->ip_addr.ip.s_addr, sizeof(UINT32), resipcmp, &res->ip_addr, free_element); if (OK != result) { DBG_DHCP_VRF("remove manual bind err:hash del err ip"); goto ERR_RET; } res->flag = FREE_ENTRY; res->expire_epoch = 0; DBG_DHCP_VRF("remove manual bind success"); /* 若客户端原先为绑定状态,则对应地址池已分配地址-1 */ if (tempBind.flag == BIND_ENTRY) { del_bind(tempBind.poolName); } } DBG_DHCP_VRF("cid hash ins len %d-- %x:%x:%x:%x:%x:%x", binding->cid.idlen, binding->cid.id[0], binding->cid.id[1], binding->cid.id[2], binding->cid.id[3], binding->cid.id[4], binding->cid.id[5]); if ((hash_ins(&cidhashtable, binding->cid.id, binding->cid.idlen, bindcidcmp, &binding->cid, binding) < 0)) { DBG("cid hash ins error"); goto ERR_RET; } DBG_DHCP_VRF("cid hash ins"); DBG_DHCP_VRF("cid hash ins %x", (UINT32)binding->ip_addr.ip.s_addr); if (hash_ins(&iphashtable, (char *)&binding->ip_addr.ip.s_addr, sizeof(UINT32), resipcmp, &binding->ip_addr, binding) < 0) { hash_del(&cidhashtable, binding->cid.id, binding->cid.idlen, bindcidcmp, &binding->cid, free_bind); DBG_DHCP_VRF("ip hash ins error"); goto ERR_RET; } DBG_DHCP_VRF("ip hash ins %x", (UINT32)binding->ip_addr.ip.s_addr); DBG("add client identifier to hash table ok."); /*memcpy(&bindingCopy, binding, sizeof(struct dhcp_binding)); add_reslist_node(&bindingCopy);*/ DHCP_SERVER_RTD_UNLOCK(); add_bind(poolName);/* 手动绑定成功,地址池中已分配地址+1 */ rtdDhcpServerChangePoolStatis(poolName, DHCP_SERVER_POOL_STATIS_PARAM_MANUAL_CNT, 1); return OK; } else { DBG("no available resource, fail."); DHCP_SERVER_RTD_UNLOCK(); return ERROR; } DHCP_SERVER_RTD_UNLOCK(); return OK; ERR_RET: head = bindlist; if (bindlist) { bindlist = bindlist->next; } if (head) { head->next = availreslist; } availreslist = head; DHCP_SERVER_RTD_UNLOCK(); return ERROR; } 这是添加静态绑定的函数 type 为free_entry啊
09-26
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值