结构体还是结构体指针?​

结构体还是结构体指针? ​缘由: ​上周某日晚上的一个项目进度跟进会上,项目中有一个涉及到一个比较大的结构体,然后内部嵌套了好些结构体。后来我问了句:“为啥使用的是结构体,而不是结构体指针?” 同事答到:“都一样”。后来,讨论了一些“如果你嵌套的那个结构体非常大的话,而这个结构体在使用过程中没有被初使化用到,这样会很浪费内存,这种情况使用结构体指针会比较合适”。

由于当时会议的重点不在这里,草草过了。后面越想越不对,这应该是一个值得思考的问题,嵌套时:是使用结构体还是结构体指针,应该是个哲学问题~~~。不过可以从一些角度来分析这个问题

不同角度分析: ​从内存使用上抽象的关系上嵌套和继承/组合的关系参考优秀的代码从内存使用上 ​正如前因提到的:

如果这个被嵌套的结构体字段很多;比如200个字段而这个被嵌套的结构体并不是很个场景都会用的上这时候用结构体指针可以省不少的空间,就很有意义;那就为了省内存而全使用结构体指针?

抽象的关系上 ​编程本质上也是给问题建模,抽象问题,代码也用来描述这些抽象之间的关系的,所以就有了下面的区别:

嵌套的是结构体:有种“包含”或“属于”的关系。内部的结构体“属于”这个外部的在大结构体,内部是外部结构体的组成部分,是它的一员。比如,一个人属于这个部门的人;在比如,协议数据报中,嵌套的关系,一个结构体属于一个结构体嵌套的是结构体指针:有种引用的关系。内部的结构体不属于你,但会“用到”你。比如,为了完成一个项目,这时候需要由不同的部门抽调人过来组成一个项目团队(结构体),这个团队会“用到(引用)”其它部门的人,我在某一时期,某个阶段可能会用到你,也可能不会用到你(的功能)结构体嵌套和继承/组合的关系 ​后来突然冒出一个问题:类的继承如何在C语言中来实现?大概就是用嵌套结构体吧

业内有名名言:“组合优于继承”,这时候:

继承就是结构体嵌套结构体。(子类包含父类中的所有成员)而组合就是结构体嵌套结构体指针。(因为里面的对象都要new,而new是在heap上,所以是指针、引用)所以根据这句名言的结论就是:优先使用嵌套结构体指针?

参考优秀的代码 ​有时候不知道怎么做合适,可以去参考下优秀的代码,归纳总结下,看看别人是怎么做的,参考参考(抄一抄);下面linux源码,进程的结构体表示

cstruct task_struct {

/* these are hardcoded - don't touch */

volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */

long counter;

long priority;

unsigned long signal;

unsigned long blocked; /* bitmap of masked signals */

unsigned long flags; /* per process flags, defined below */

int errno;

long debugreg[8]; /* Hardware debugging registers */

struct exec_domain *exec_domain;

/* various fields */

struct linux_binfmt *binfmt;

struct task_struct *next_task, *prev_task;

struct task_struct *next_run, *prev_run;

unsigned long saved_kernel_stack;

unsigned long kernel_stack_page;

int exit_code, exit_signal;

/* ??? */

unsigned long personality;

int dumpable:1;

int did_exec:1;

/* shouldn't this be pid_t? */

int pid;

int pgrp;

int tty_old_pgrp;

int session;

/* boolean value for session group leader */

int leader;

int groups[NGROUPS];

/*

* pointers to (original) parent process, youngest child, younger sibling,

* older sibling, respectively. (p->father can be replaced with

* p->p_pptr->pid)

*/

struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;

struct wait_queue *wait_chldexit; /* for wait4() */

unsigned short uid,euid,suid,fsuid;

unsigned short gid,egid,sgid,fsgid;

unsigned long timeout, policy, rt_priority;

unsigned long it_real_value, it_prof_value, it_virt_value;

unsigned long it_real_incr, it_prof_incr, it_virt_incr;

struct timer_list real_timer;

long utime, stime, cutime, cstime, start_time;

/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */

unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;

int swappable:1;

unsigned long swap_address;

unsigned long old_maj_flt; /* old value of maj_flt */

unsigned long dec_flt; /* page fault count of the last time */

unsigned long swap_cnt; /* number of pages to swap on next pass */

/* limits */

struct rlimit rlim[RLIM_NLIMITS];

unsigned short used_math;

char comm[16];

/* file system info */

int link_count;

struct tty_struct *tty; /* NULL if no tty */

/* ipc stuff */

struct sem_undo *semundo;

struct sem_queue *semsleeping;

/* ldt for this task - used by Wine. If NULL, default_ldt is used */

struct desc_struct *ldt;

/* tss for this task */

struct thread_struct tss;

/* filesystem information */

struct fs_struct *fs;

/* open file information */

struct files_struct *files;

/* memory management info */

struct mm_struct *mm;

/* signal handlers */

struct signal_struct *sig;

#ifdef __SMP__

int processor;

int last_processor;

int lock_depth; /* Lock depth. We can context switch in and out of holding a syscall kernel lock... */

#endif

};其中除了struct thread_struct tss和struct rlimit rlim[RLIM_NLIMITS]用的结构体,一个是线程,一个是进程资源大小的限制,更像是在表达这俩就“属于”进程,必不可少。其它的ipc,fs,memory management,signal handlers,更像是为了组合到一起来完成任务,会“用到”你们(的功能),它们各自有独立的功能。

总结 ​好像结论已经很明显了,更加倾向于从抽象的角度看这个问题,如果是有“包含”或“属于”的关系在里面,就用嵌套结构体;而如果是有“组合”到一起完成某种功能,它承担了一些独特的任务功能,这时可以用嵌套结构体指针。