c语言时间换算结构体9,C语言 结构体指针强制转换

最近写c程序遇到的结构体指针强转的坑,记录一下.

是一个简单的菜单程序,程序用到链表,表中存储了9个不同命令.每次将用户输入的命令与表中存储的命令名作对比(遍历查询),然后执行相应功能.

总体结构:

linktable.h:定义通用模块化链表数据结构,以及相关操作

linktable.c:实现头文件中定义的链表操作

main.c:主函数

通用链表节点:(抽象定义)

typedef struct LinkTableNode

{

struct LinkTableNode *pNext;//只有后继指针

}tLinkTableNode;

链表:

typedef struct LinkTable

{

tLinkTableNode *pHead;//表头

tLinkTableNode *pTail;//表尾

int SumOfNode;//节点数

pthread_mutex_t mutex;//互斥锁

}tLinkTable;

数据节点:(通用链表的具体化)

typedef struct DataNode

{

char* cmd;//命令名

char* desc;//命令描述

int (*handler)();//函数指针,指向具体功能函数

struct DataNode *pNext;//后继指针

}tDataNode;

main.c片段:

tDataNode * FindCmd(tLinkTable *head, char *cmd)//函数:遍历链表head,查找与cmd符合的命令并返回该数据节点

{

tDataNode *pNode = (tDataNode*)getLinkTableHead(head);

while(pNode != NULL)

{

if(strcmp(pNode->cmd, cmd) == 0)

{

return pNode;

}

pNode = (tDataNode*)getNextLinkTableNode(head, (tLinkTableNode*)pNode);

}

return NULL;

}

//对数据节点赋值,注意每个字段的摆放顺序要与定义的格式相匹配

static tDataNode menu[] =

{

{"version", "menu program v2.5",NULL,(tLinkTableNode*)&menu[1]},

{"help", "this is help cmd!", Help,(tLinkTableNode*)&menu[2]},

{"add", "this is add cmd!", Add, (tLinkTableNode*)&menu[3]},

{"sub", "this is sub cmd!", Sub, (tLinkTableNode*)&menu[4]},

{"mul", "this is multi cmd!", Multi, (tLinkTableNode*)&menu[5]},

{"div", "this is divide cmd!", Divide, (tLinkTableNode*)&menu[6]},

{"pow", "this is power cmd!", Power, (tLinkTableNode*)&menu[7]},

{"time", "this is time cmd!", Time, (tLinkTableNode*)&menu[8]},

{"quit", "this is quit cmd", Quit, (tLinkTableNode*)NULL}

};

//初始化,创建链表体,并把头尾分别指向menu[0]和menu[8]

int InitMenuData(tLinkTable **ppLinkTable)

{

*ppLinkTable = CreateLinkTable();

(*ppLinkTable)->pHead = (tLinkTableNode*)&menu[0];

(*ppLinkTable)->pTail = (tLinkTableNode*)&menu[8];

(*ppLinkTable)->SumOfNode = 9;

return SUCCESS;

}

tLinkTable *head = NULL;

int main()

{

InitMenuData(&head);//将链表体指针head初始化

printf("Welcome!Use 'help' to get how to use this system.\n");

while(1)//无限循环

{

char cmd[CMD_MAX_LEN];

printf("input a cmd >");

scanf("%s", cmd);//读取用户输入命令

tDataNode *p = FindCmd(head, cmd);//查找

if(p == NULL)//若找不到,提示错误

{

printf("Wrong cmd!Use 'help' to get how to use this system.\n");

continue;

}

printf("%s ---- %s\n", p->cmd, p->desc);//输出该命令信息

if(p->handler != NULL)

{

p->handler();//调用该命令的功能函数

}

}

}

看起来没有什么问题,运行,崩溃.进入调试,发现原因在于main片段的第11行

pNode=(tDataNode*)getNextLinkTableNode(head, (tLinkTableNode*)pNode);

getNextLinkTableNode的功能是返回链表head中pNode节点的下一个节点.这里对参数pNode(第4行创建为tDataNode*类型)进行强制转换成tLinkTableNode *类型.

然而我们知道,强制转换是有可能出问题的,这就是问题所在.tLinkTableNode是一个通用的链表节点类型,其中只包含一个后继域 pNext,而tDataNode包含了四个成员:两个char数组指针cmd和desc,还有函数指针handler和后继pNext.如此一来强转必然出问题.

执行第11行前,调试的信息如图所示:

0818b9ca8b590ca3270a3433284dd417.png

pNode的类型为tDataNode*,其cmd字段为0x406070(“version”),pNext字段为0x405030

进入11行后,调试信息发生如下变化:

0818b9ca8b590ca3270a3433284dd417.png

pNext类型为tLinkTableNode*,其中pNext字段为0x406070.是不是很熟悉?没错,就是强制转换之前的cmd字段内容.也就是说,pNext本该指向0x405030,却因为强制转换而变成了0x406070.而这是一个未知的地址,也就是常说的”指针乱指”,如此一来发生错误是必然的!

可以看到,结构体数组的地址都是非常规整的,他们彼此相邻,每个结构体占0x10的地址空间,然而由于指针乱指到未知的地址0x406070,进入该地址后,pNext继续乱指到0x73726576.再进入这个地址,读取其cmd字段,系统报告发生段错误,程序终止.

0818b9ca8b590ca3270a3433284dd417.png

那么正确的做法是什么呢?观察强制转换的内容,可以发现强转成tLinkTableNode*后,pNext的内容为tDataNode *的cmd内容,也就是定义结构体中的第一个字段,要想保持强转后不丢失后继指针,只有在定义结构体时将后继指针调整到第一个字段:

typedef struct DataNode

{

struct DataNode *pNext;

char* cmd;

char* desc;

int (*handler)();

}tDataNode;

同时对结构体数组赋值时也要注意顺序的调整,把后继指针的赋值放在第一位:

static tDataNode menu[] =

{

{(tLinkTableNode*)&menu[1],"version", "menu program v2.5",NULL},

{(tLinkTableNode*)&menu[2],"help", "this is help cmd!", Help},

{(tLinkTableNode*)&menu[3],"add", "this is add cmd!", Add},

{(tLinkTableNode*)&menu[4],"sub", "this is sub cmd!", Sub},

{(tLinkTableNode*)&menu[5],"mul", "this is multi cmd!", Multi},

{(tLinkTableNode*)&menu[6],"div", "this is divide cmd!", Divide},

{(tLinkTableNode*)&menu[7],"pow", "this is power cmd!", Power},

{(tLinkTableNode*)&menu[8],"time", "this is time cmd!", Time},

{(tLinkTableNode*)NULL,"quit", "this is quit cmd", Quit}

};

这样程序才能正常运行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值