今天帮师姐调一个程序的BUG,师姐的程序中有个结构体直接赋值的语句,在我印象中结构体好像是不能直接赋值的,正如数组不能直接赋值那样,我怀疑这个地方有问题,但最后证明并不是这个问题。那么就总结一下C语言中结构体赋值的问题吧:

结构体直接赋值的实现

下面是一个实例:

#include <stdio.h>

struct Foo {
char a;
int b;
double c;
}foo1, foo2; //define two structs with three different fields void struct_assign(void)
{
foo2 = foo1; //structure directly assignment
} int main()
{
foo1.a = 'a';
foo1.b = 1;
foo1.c = 3.14;
struct_assign();
printf("%c %d %lf\n", foo2.a, foo2.b, foo2.c); return 0;
}

我在Ubuntu 13.04下使用gcc 4.7.3 编译运行得到的结果,如下所示:

guohl@guohailin:~/Documents/c$ gcc struct_test1.c -o struct_test1
guohl@guohailin:~/Documents/c$ ./struct_test1
a 1 3.140000

可以从结果上看出,结构体直接赋值在C语言下是可行的,我们看看struct_assign()函数的汇编实现,从而从底层看看C语言是如何实现两个结构体之间的赋值操作的:

struct_assign:
pushl %ebp
movl %esp, %ebp
movl foo1, %eax
movl %eax, foo2 //copy the first 4 bytes from foo1 to foo2
movl foo1+4, %eax
movl %eax, foo2+4 //copy the second 4 bytes from foo1 to foo2
movl foo1+8, %eax
movl %eax, foo2+8 //copy the third 4 bytes from foo1 to foo2
movl foo1+12, %eax
movl %eax, foo2+12 //copy the forth 4 bytes from foo1 to foo2
popl %ebp
ret

这段汇编比较简单,由于结构体的对齐的特性,sizeof(srtruct Foo)=16,通过四次movl操作将foo1的结构体内容拷贝到结构体foo2中。从汇编上看出,结构体赋值,采用的类似于memcpy这种形式,而不是逐个字段的拷贝。

复杂结构体的赋值

如果结构体中含有其它复杂数据类型呢,例如数组、指针、结构体等,从上面的汇编实现可以看出,只要两个结构体类型相同,就可以实现赋值,如下例:

#include <stdio.h>

struct Foo {
int n;
double d[2];
char *p_c;
}foo1, foo2; int main()
{
char *c = (char *) malloc (4*sizeof(char));
c[0] = 'a'; c[1] = 'b'; c[2] = 'c'; c[3] = '\0'; foo1.n = 1;
foo1.d[0] = 2; foo1.d[1] = 3;
foo1.p_c = c; foo2 = foo1; //assign foo1 to foo2 printf("%d %lf %lf %s\n", foo2.n, foo2.d[0], foo2.d[1], foo2.p_c); return 0;
}

运行结果如下:

guohl@guohailin:~/Documents/c$ gcc struct_test2.c -o struct_test2
guohl@guohailin:~/Documents/c$ ./struct_test2
1 2.000000 3.000000 abc

可以看出结果和我们想象的是一样的。再次验证结构体的赋值,是直接结构体的内存的拷贝!但正是这个问题,如上面的实例,foo1 和 foo2 中p_c 指针都是指向我们申请的一块大小为4个字节的内存区域,这里注意的是,结构体的拷贝只是浅拷贝,即指针p_c的赋值并不会导致再申请一块内存区域,让foo2的p_c指向它。那么,如果释放掉foo1中的p_c指向的内存,此时foo2中p_c变成野指针,这是对foo2的p_c操作就会出现一些不可预见的问题!在C++中引入了一种可以允许用户重载结构体赋值操作运算,那么我们就可以根据语义重载赋值操作。

数组是二等公民

二等公民在维基百科上的解释是:

二等公民不是一个正式的术语,用来描述一个社会体系内对一部分人的歧视或对外来人口的政治限制,即使他们作为一个公民或合法居民的地位。 二等公民虽然不一定是奴隶或罪犯,但他们只享有有限的合法权利、公民权利和经济机会,并经常受到虐待或忽视。法律无视二等公民,不向他们提供保护,甚至在制订法律时可能会根本不考虑他们的利益。划分出二等公民的行为,普遍被视为一种侵犯人权的行为。 典型的二等公民所面临的障碍包括但不仅限于(缺乏或丧失表决权):权利被剥夺,限制民事或军事服务(不包括任何情况下的征兵),以及限制,语言,宗教,教育,行动和结社的自由,武器的所有权,婚姻,性别认同和表达,住房和财产所有权 。

从词条上解释可以看出二等公民与一等公民在权利上是有差别的,这个词很有意思作为计算机专业术语,其含义也有异曲同工之妙!同样我们看看维基百科对计算机的术语”first-class citizen"(一等公民)的定义,一般要满足以下几点,

  • can be stored in variables and data structures
  • can be passed as a parameter to a subroutine
  • can be returned as the result of a subroutine
  • can be constructed at run-time
  • has intrinsic identity (independent of any given name)

对比着上面的定义来看C语言数组,数组作为一个函数的参数传递时,退化成一个指针; 同时,数组无法作为函数的返回值; 也许让数组更不服气的是,数组之间不能直接赋值操作,如下面的操作就是非法的:

int a[10];
int b[10];
a = b;

但是如果数组包装在结构体中,那么就能进行赋值了!相比之下,结构体可以作为函数参数和返回值,这就是一等公民的待遇!至于为什么数组必须是二等公民,这是有历史原因的,大家可以参考C 语言的发展史来看,有时间这块内容我再补上!


参考资料:

C语言中结构体赋值问题的讨论的更多相关文章

  1. C语言中结构体赋值问题的讨论(转载)

    今天帮师姐调一个程序的BUG,师姐的程序中有个结构体直接赋值的语句,在我印象中结构体好像是不能直接赋值的,正如数组不能直接赋值那样,我怀疑这个地方有问题,但最后证明并不是这个问题.那么就总结一下C语言 ...

  2. C语言中结构体对齐问题

    C语言中结构体对齐问题 收藏 关于C语言中的结构体对齐问题 1,比如: struct{short a1;short a2;short a3;}A;struct{long a1;short a2;}B; ...

  3. 6. Go 语言中结构体的使用

    1. 结构体的定义格式 在go语言中结果的定义格式如下: 123 type structName struct { filedList} 列子如下: 1234 type Person struct { ...

  4. Go语言中结构体的使用-第2部分OOP

    1 概述 结构体的基本语法请参见:Go语言中结构体的使用-第1部分结构体.结构体除了是一个复合数据之外,还用来做面向对象编程.Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性. ...

  5. Go语言中结构体的使用-第1部分结构体

    1 概述 结构体是由成员构成的复合类型.Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性.结构体成员,也可称之为成员变量,字段,属性.属性要满足唯一性.结构体的概念在软件工程上 ...

  6. C语言中结构体的深拷贝和浅拷贝

    C++ 的浅拷贝和深拷贝(结构体) 拷贝有两种:深拷贝,浅拷贝 浅拷贝:拷贝过程中是按字节复制的,对于指针型成员变量只复制指针本身,而不复制指针所指向的目标 (1)结构体中不存在指针成员变量时 typ ...

  7. C语言中结构体变量之间赋值

    近期,我阅读了某新员工小刘写的C语言代码,发现其对结构体变量之间的赋值不是非常熟悉. 对于两个同样类型的结构体变量,他均採用的是逐个成员变量直接赋值的形式.例如以下的代码演示样例: 如上代码所看到的, ...

  8. C语言中结构体參数变量的传递

    [文章摘要] 在C语言中,结构体參数变量常常作为函数的參数来进行传递.但假设參数设置不当.会出现内存问题. 本文以实际的程序代码为例.具体地介绍怎样正确地使用结构体參数变量.为相关的开发工作提供了參考 ...

  9. c语言中结构体指针

    1.指向结构体的指针变量: C 语言中->是一个总体,它是用于指向结构体,如果我们在程序中定义了一个结构体,然后声明一个指针变量指向这个结构体.那么我们要用指针取出结构体中的数据.就要用到指向运 ...

随机推荐

  1. # ASP.NET Core依赖注入解读&使用Autofac替代实现

    标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...

  2. order by用法

    此文来源于CSDN中zxcvg的博文 1.ORDER BY 中关于NULL的处理 缺省处理,Oracle在Order by 时认为null是最大值,所以如果是ASC升序则排在最后,DESC降序则排在最 ...

  3. Linux查看CPU和内存使用情况(转)

    在系统维护的过程中,随时可能有需要查看 CPU 使用率,并根据相应信息分析系统状况的需要.在 CentOS 中,可以通过 top 命令来查看 CPU 使用状况.运行 top 命令后,CPU 使用状态会 ...

  4. Lind.DDD.Domain领域模型介绍

    回到目录 Lind.DDD.Domain位于Lind.DDD核心项目中,它主要面向领域实体而设计,由一个IEntity的标识接口,EntityBase基类和N个Entity实体类组成,其中IEntit ...

  5. 使用HTML 和CSS 开发商业站点

    第一章HTML 基础1.html 的基本结构?解析:2.HTML 全称Hyper Text Markup Language(超文本标记语言)扩展XML:Extendsible Markup Langu ...

  6. Entity Framework Code First 中使用 Fluent API 笔记。

    在做MVC+EF CodeFirst 的Demo时,碰到的问题, 在组册用户时,要让用户输入确认密码,但是数据库中又不需要保存这个字段,解决方案很多了,这里我列出通过EF Code First的解决方 ...

  7. placeholder不兼容 IE10 以下版本的解决方法

    对于密码输入框placeholder的兼容问题:HTML代码:<input type="password" id="loginPassword" plac ...

  8. [deviceone开发]-一个固定列,可以上下左右滑动的表格示例

    一.简介 一个类型table的例子,第一列和第二列可以固定,剩下的后面的很多列都可以左右滑动,也可以上下滑动,这种需求一般是为了展示多列表格数据.这个例子是通过二大部分: 左边是一个普通的listvi ...

  9. Android动画例子。

    例子一: 补间动画效果,从右进,从左出. ImageSwitcher mImageSwitcher = new ImageSwitcher(this); mImageSwitcher.setFacto ...

  10. 理解 iOS 的内存管理

    远古时代的故事 那些经历过手工管理内存(MRC)时代的人们,一定对 iOS 开发中的内存管理记忆犹新.那个时候大约是 2010 年,国内 iOS 开发刚刚兴起,tinyfool 大叔的大名已经如雷贯耳 ...