关于C的 struct ,union我的一些想法

中山野鬼 发布于 2012/03/21 17:47
阅读 659
收藏 1

看到,一篇对 C++的 UNION如何被C#使用的问题帖。呵呵。内容很熟悉,包括那写结构体里面的东西,但是没办法回答,因为我曾经一度弱智到,认为C#和C sharp是两个东西。同时这玩意还要.net。我没学习他的原因一则,没时间,二则,花时间学了,究竟能用多久?以微软的作风,我不如好好学点类UNIX上的工具。

回到正题上,无法回答C#的问题。这里就struct ,union上的东西谈下我自己的想法。书本上,和标准上说的东西我就不重复了。所以只是自己的想法。

向新手说union,我几乎不能回避struct。但前提是我只谈论的是C。其实那篇文章所写的,实际是C的union而不是C++的 union。不是我在嚼舌头,有迷糊的朋友可以google些C++的union的用法,再看看C是否支持。

在C++中,为了能对面向对象进行支持,使得union增加了对类成员方法的union的支持。但这个在C中是不存在的(没有支持的必要和存在性)

需要说明,union和struct在C里面(后面不再强调,此处不是谈C++),只是一个多数据类型的操作工具。给谁用?代码员和编译器。可以这么说。没有union,struct的C仍然可以实现,包含了这两个保留字的所有C代码可实现的功能。只是方便程序员写代码而已。

例如我们存在描述的数据,这个数据存在不同的存储空间的字段。那么我们用struct来包裹,就非常方便。如下给出两个例子

typedef struct {
 int a;
 char b;
}_TEST_S;

_TEST_S test_s;

char return_TEST_S_b(_TEST_S *pt){
   return pt->b;
}

char return_test_ab_b(int *p){
    return *(char*)(p+1);
}
int main(int argc ,char *argv[]){
   char re_;
   ...
   re_ = return_TEST_S_b((_TEST_S *)&test_s);
   re_ = return_test_ab_b((int*)&test_s);
   ...
}

以上两者实际是等同的。但_TEST_S和以下的方式完全不同。

int test_a[10];
char test_b[10];

char return_test_b(char *p){
   return *p;
}

这是因为,对于结构体,编译器看到的,每个存储单元,包含了一个int 和 char 两个类型的存储空间。因此对数组的任何一个下标的访问,他们是针对 具体存储单元锁定后,再找寻,该单元内,对应结构体内的具体类型的存储位置。

上述两者的差别非常像,以下两中构造数据表的操作。

我们把一个 “姓名” “年龄”的人的信息,放在一个表里,每个表的记录,包含两个字段,与我们有两张表,假设,表中对应记录的位置(下标)就是唯一ID号。那么每个表都只有一个字段。其中一个是“姓名”另一个是“年龄”

因此,struct只是用于打包,或规整数据之间存储位置的逻辑关系而使用的。方便于你有效描述你的目标逻辑(任务)。

同时C语言里,struct内,是不存在函数的,因为任何函数都是一个实际存在的代码片,用于执行的,struct的目的和功能我上面说了,因此无法将多个函数进行打包。引申一下,多个函数进行位置打包的需求是客观存在的,但情况极少,通常用在对code cache有严格要求,或者函数位置和系统内部逻辑有关联的情况下。不过由于通过增加辅助信息,可以让编译器和连接器完成上述操作,因此C没有必要去用保留字对函数打包操作。这是C与面向对象的C++不同的地方。C++的 类的概念,本身要求存在方法。而方法本身并不是数据而是一个实现函数的模版,其成为了类的一个必要组成部分。

回到 union,其实和struct很像,之不过,union的作用是多者选一个,而不是多个数据类型的整合描述。

例如下面的例子,我们做数组的全累加

typedef union{
   int i;
   char c;
}_TEST_U;
#define MAX_TEST_U_NUM 10
_TEST_U u[MAX_TEST_U_NUM];

int acc_test_U_i(_TEST_U *pt){
   int i,re = 0;
       
   for (i = 0 ; i < MAX_TEST_U_NUM ; i++){
      re += pt->i; pt++;
   }
   return re;
}
char acc_test_U_c(_TEST_U *pt){
   int i;
   char re = 0;//注意这里是会有溢出的,其实acc_test_U_i一样会有溢出,但此处不讨论这方面问题。
       
   for (i = 0 ; i < MAX_TEST_U_NUM ; i++){
      re += pt->c; pt++;
   }
   return re;
} 

这个和下面两个函数,对比,记住,一个是错的,一个是正确的。

int u[MAX_TEST_U_NUM];

int acc_test_U_i(void *p){//对
   int i,re = 0;
   int *pi = (int*)p;    
   for (i = 0 ; i < MAX_TEST_U_NUM ; i++){
      re += *pi++;
   }
   return re;
}
char acc_test_U_c(void *p){//错
   int i;
   char re = 0;
   char *pc = (char *)p;    
   for (i = 0 ; i < MAX_TEST_U_NUM ; i++){
      re += *pc++;
   }
   return re;
} 

因为使用union,则允许整个程序里面,即可以 p->i ,也可以p->c。这是对同一个空间良种不同数据类型操作的差异,因为要保证所有可能的操作均有效。因此,空间必须按照可选择的数据类型中,最大的一个分布。因此,以下的char的累加是对等 _TEST_U的。

char acc_test_U_c(void *p){//对 
   int i;
   char re = 0;
   int *pi = (int *)p;    
   char *pc;
   for (i = 0 ; i < MAX_TEST_U_NUM ; i++){
      pc = (char *)pi;
      re += *pc;pi++;
   }
   return re;
} 
//或者简单点
char acc_test_U_c(void *p){
   int i;
   char re = 0;
   char *pc = (char *)p;    
   for (i = 0 ; i < MAX_TEST_U_NUM ; i++){

      re += *pc;pc+= sizeof(int)/sizeof(char); //这个地方我不怕除法,
//因为编译器对于这类东西,会修正为常量,而不是每次弱智的去除
   }
   return re;

}

如果上述例子还是不能理解union的用法,那么下面我再做个例子,大家有兴趣可以尝试把结果打印一下。假设低位在前(LSB)的硬件系统

typedef union{
   int i;
   char c;
}_TEST_U;

_TEST_U u;

int main(int argc ,char *argv[]){
   char *pc;
   int i;
   u.i = 0x01020304;
   u.c = 0x5;
   if ( u.i == 0x01020305){
     //printf
   }else{
     //printf
   } 
   pc = &(u.c);
   for (i = 0 ; i < 4 ; i++){
      pc[i] = 4 - i;
   }
   if (u.i == 0x04030201){
//      printf(...
   }else{
    //printf..
   }
   ...
}

和我其他的灌水帖一样,这篇灌水帖,包括代码都是临时写的,没验证,有漏笔之处,还望谅解。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

加载中
返回顶部
顶部