因为在工作中需要大量用到结构体,无论是结构体作函数形参和实参,还是结构体元素的普通赋值操作,都在网络通信编程工作中大量出现,而自己一直对结构体的赋值的相关知识都不清晰,所以特意根据在工作中遇到的结构体问题一一做实验来解决心中的疑问。
1.结构相同的结构体,但是结构体名字不一样,是否可以整体赋值?
可能问题描述的不够清楚,那我们看下面的两个结构体:
typedef struct test1_s
{
char age;
char key;
int num;
}test1_t;
typedef struct test2_s
{
char age;
char key;
int num;
}test2_t;
结构是一样,占的内存大小是一样的,唯一不同的是,它们取了不同的名字,那么,将他们进行整体的赋值,会发生什么情况呢?
int main()
{
test1_t test1;
test1.age=18;
test1.num=21;
test1.key=11;
test2_t test2;
test2 = test1;
printf("test1 structure:\n%d\n%d\n%d\n",test1.age,test1.key,test1.num);
printf("test2 structure:\n%d\n%d\n%d\n",test2.age,test2.key,test2.num);
return 0;
}
很不幸,编译器提示错误:
很显然,即使相同结构但不同名字的结构体,作结构体间的整体赋值是不可以的,编译时会提醒“类型不匹配”。
那我们尝试进行结构体的强制转换test2 = (test2_t)test1;,编译是否能成功呢?
conversion to non-scalar type requested
请求的非标量类型的转换
原来, C标准规定标量只能向标量类型转换,算法类型和指针都是标量类型所以向struct或union的转换是错误的(数组,结构体等属于聚合类型,数组有其特殊性)
那何为标量类型?
在C语言中,枚举类型、字符型和各种整数的表示形式统一叫做标量类型。
当在C表达式中使用标量类型的值时,编译器就会自动将这些标识符转换为整数保存。
这种机制的作用是,在这些标量类型上执行的操作与整型上执行的操作完全一样。
typedef struct test1_s
{
char age;
char key;
int num;
}test1_t;
typedef struct test2_s
{
char age;
char key;
int num;
}test2_t;
int main()
{
test1_t test1;
test1.age=18;
test1.num=21;
test1.key=11;
test2_t test2;
memcpy(&test2,&test1,sizeof(test1));
printf("test1 structure:\n%d\n%d\n%d\n",test1.age,test1.key,test1.num);
printf("test2 structure:\n%d\n%d\n%d\n",test2.age,test2.key,test2.num);
return 0;
}
使用memcpy来进行赋值,结果呢?
好的,没问题
再看下例:
typedef struct test1_s
{
char key;
int num;
char age;
}test1_t;
typedef struct test2_s
{
unsigned e;
char age;
char f;
char key;
int c;
int b;
int num;
int a;
}test2_t;
test2_t结构体中包含多个元素,那进行memcpy时,test1是否可以找到test2对应的元素正确赋值呢?
答案是肯定的。
这种操作存在风险,但是不影响正常输出。这种做法虽说可以达到目的,但是在进行这样的操作时,一定要注意这两个结构体的空间大小,否则会产生难以察觉的巨大崩溃!(内存越界,亲身经历,花了一天才定位到错误)
2.那我们将问题1的结构体改为结构体指针呢?
int main()
{
test1_t *test1;
test1=(test1_t*)malloc(sizeof(test1_t)) ;
test1->age=18;
test1->num=21;
test1->key=11;
test2_t *test2;
test2 = (test2_t*)test1;
printf("test1 structure:\n%d\n%d\n%d\n",test1->age,test1->key,test1->num);
printf("test2 structure:\n%d\n%d\n%d\n",test2->age,test2->key,test2->num);
return 0;
}
编译通过,执行结果如下:
test2被成功“赋值”。
分析其赋值成功的原因:本实验中只为一个结构体创建了空间,也就是test1。其实是指针test2被赋予了指针test1的值,使其指向了test1所指向的结构体。
分析问题1赋值失败的原因:这次实验为两个结构体都开辟了空间,而结构体又不属于标量类型,正是如此,C语言不允许非标量的整体赋值。
typedef struct test1_s
{
char age;
char key;
int num;
}test1_t;
typedef struct test2_s
{
int num;
char age;
char key;
}test2_t;
再一次打乱顺序:
typedef struct test1_s
{
char age;
char key;
int num;
}test1_t;
typedef struct test2_s
{
char age;
int num;
char key;
}test2_t;
如果是结构体内的元素的类型一一对应,但是名字不对应,那是否可以呢?
typedef struct test1_s
{
char age;
char key;
int num;
}test1_t;
typedef struct test2_s
{
char a;
char b;
int c;
}test2_t;
int main()
{
test1_t *test1;
test1=(test1_t*)malloc(sizeof(test1_t)) ;
test1->age=18;
test1->num=21;
test1->key=11;
test2_t *test2;
test2 = (test2_t*)test1;
printf("test1 structure:\n%d\n%d\n%d\n",test1->age,test1->key,test1->num);
printf("test2 structure:\n%d\n%d\n%d\n",test2->a,test2->b,test2->c);
return 0;
}
输出成功!
总结:首先,对于这种结构相同(元素类型和位置一一对应相同)但名字不同的结构体,是可以成功“结构体间赋值”的。
原因很简单,就是他们开辟的内存空间是一模一样的,第一个1字节空间就放char,第二个1字节空间就放char,第三个4字节空间就放int。虽说元素名字不同,但是每个元素的空间是有序且一一对应的,所以无乱码出现。
3.如果一个指向大的结构体的指针指向一个小的结构体,是否可以赋值成功?
typedef struct test1_s
{
char age;
char key;
int num;
}test1_t;
typedef struct test2_s
{
int tel;
int hometown;
char age;
char key;
int num;
}test2_t;
int main()
{
test1_t *test1;
test1=(test1_t*)malloc(sizeof(test1_t)) ;
test1->age=18;
test1->num=21;
test1->key=11;
test2_t *test2;
test2 = (test2_t*)test1;
printf("test1 structure:\n%d\n%d\n%d\n",test1->age,test1->key,test1->num);
printf("test2 structure:\n%d\n%d\n%d\n",test2->age,test2->key,test2->num);
return 0;
}
很明显,所谓的赋值出错了。那会不会跟test2的结构有关系,那将test2_t改动一下:
typedef struct test2_s
{
char age;
char key;
int num;
int tel;
int hometown;
}test2_t;
注意这次改动:让test1_t和test2_t的前三个元素的位置保持一致
结果是:
结果是赋值成功
我继续尝试交换test2_t元素的位置
typedef struct test2_s
{
char age;
char key;
int hometown;
int num;
int tel;
}test2_t;
失败
总结:感觉结构体赋值方面的情况很多,真不好分析其原因,但是可以确定的是,只要是前几个元素可以做到一一对应的话,就能保证不出错。memcpy也是一种常用的方法,但是要时刻警惕它的危险性。所以,为了稳妥起见,还是使用一模一样的结构体才是最稳妥的办法。