精华内容
下载资源
问答
  • 在 dotnet 运行时中,给引用对象进行赋值替换的时候...给结构体对象赋值,如果此结构体是某个类的成员字段,那么此赋值不一定是线程安全的。是否线程安全,取决于结构体的大小,取决于此结构体能否在一次原子赋值内完成

    在 dotnet 运行时中,给引用对象进行赋值替换的时候,是线程安全的。给结构体对象赋值,如果此结构体是某个类的成员字段,那么此赋值不一定是线程安全的。是否线程安全,取决于结构体的大小,取决于此结构体能否在一次原子赋值内完成

    大家都知道,某个执行逻辑如果是原子逻辑,那么此逻辑是线程安全的。原子逻辑就是一个非 A 即 B 的状态的变更,绝对不会存在处于 A 和 B 中间的状态。满足于此即可称为线程安全,因为线程不会读取到中间状态。在 dotnet 运行时里面,特别对了引用对象,也就是类对象的赋值过程进行了优化,可以让对象的赋值是原子的

    从运行时的逻辑上,可以了解到的是引用对象的赋值本质上就是将新对象的引用地址赋值,对象引用地址可以认为是指针。在单次 CPU 运算中可以一次性完成,不会存在只写入某几位而还有某几位没有写入的情况

    大概可以认为在 x86 上,单次的原子赋值长度就是 32 位。这也就是为什么 dotnet 里面的对象地址设计为 32 位的原因

    但是对于结构体来说,需要分为两个情况,定义在栈上的结构体,如某个方法的局部变量或参数是结构体,此时的结构体是存放在栈上的,而在 dotnet 里面,每个线程都有自己独立的栈,因此放在栈上的结构体在线程上是独立的,相互之间没有影响,也就是线程安全的

    如果是放在堆上面的结构体,如作为某个类对象的字段,此时的结构体将会占用此类对象的内存空间,如对以下代码的内存示意图

        class Foo
        {
            public int X; // 没有任何项目或理由可以公开字段,本文这里不规范的写法仅仅只是为了做演示而已 (Unity除外)
            public FooStruct FooStruct;
            public int Y;
        }
    
        struct FooStruct
        {
            public int A { set; get; }
            public int B { set; get; }
            public int C { set; get; }
            public int D { set; get; }
        }

    此时的 Foo 对象在内存上的布局示意图大概如下

    如上面示意图,在内存布局上,将会在类内存布局上将结构体展开,占用类的一段内存空间。也就是说本质上结构体如命名,就是多个基础类型的组合,实际上是运行的概念。也就是说在给类对象的字段是结构体进行赋值的时候,每次赋值的内容仅仅是取决于原子长度,如 x86 下使用 32 位进行赋值,相当于先给 FooStruct 的 A 进行赋值,再给 FooStruct 的 B 进行赋值等等。此时如果有某个线程在进行赋值,某个线程在进行读取 Foo 对象的 FooStruct 字段,那么也许读取的线程会读取到正在赋值到一半的 FooStruct 结构体

    如以下的测试代码

        class Program
        {
            static void Main(string[] args)
            {
                var taskList = new List<Task>();
    
                for (int i = 0; i < 100; i++)
                {
                    var n = i;
                    taskList.Add(Task.Run(() =>
                    {
                        while (Foo != null)
                        {
                            var fooStruct = new FooStruct()
                            {
                                A = n,
                                B = n,
                                C = n,
                                D = n
                            };
    
                            Foo.FooStruct = fooStruct;
    
                            fooStruct = Foo.FooStruct;
                            var value = fooStruct.A;
                            if (fooStruct.B != value)
                            {
                                throw new Exception();
                            }
    
                            if (fooStruct.C != value)
                            {
                                throw new Exception();
                            }
    
                            if (fooStruct.D != value)
                            {
                                throw new Exception();
                            }
                        }
                    }));
                }
    
                Task.WaitAll(taskList.ToArray());
            }
    
            private static Foo Foo { get; } = new Foo();
        }

    以上代码开启了很多线程,每个线程都在尝试读写此结构体。每次写入的赋值都是在 A B C D 给定相同的一个数值,在读取的时候判断是否读取到的每一个属性是否都是相同的数值,如果存在不同的,那么证明给结构体赋值是线程不安全的

    运行以上代码,可以看到,在结构体中,存在属性的数值是不相同的。通过以上代码可以看到,放在类对象的字段的结构体,进行赋值是线程不安全的

    本文所有代码放在githubgitee 欢迎访问

    可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

    git init
    git remote add origin https://gitee.com/lindexi/lindexi_gd.git
    git pull origin 01a988dd6efdd0550ce0302ecbb93755f1720e85
    

    以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

    git remote remove origin
    git remote add origin https://github.com/lindexi/lindexi_gd.git
    

    获取代码之后,进入 YanibeyeNelahallfaihair 文件夹

    我搭建了自己的博客 https://blog.lindexi.com/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新

    如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入

    如有不方便在博客评论的问题,可以加我 QQ 2844808902 交流

    知识共享许可协议
    本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

    展开全文
  • 程序中用DATA定义的局部变量,也可以使用types 语法: DATA: BEGIN OF <name> <field1>... <field2>... END OF<name>. 例子: DATA:BEGIN OF USERINF, SID(10) TYPE C, NAME(20) TYPE ...

    结构体

    • 有结构的变量
    • 程序中用DATA定义的局部变量,也可以使用types

    语法:

    DATA: BEGIN OF <name>
    	<field1>...
    	<field2>...
    END OF<name>.
    

    例子:

    DATA:BEGIN OF USERINF,
    	SID(10) TYPE C,
    	NAME(20) TYPE C,
    	TEL(20) TYPE C,
    END OF USERINF.
    
    DATA USER2 LIKE USERINF.
    
    结构体赋值
    • 可对结构体直接赋值
    • 相同结构体之间可以使用等号来实现数据的赋值
    • WRITE Structure Name 可输出所有字段,或使用-输出指定字段
    • 相同结构体之间使用Move…To…进行赋值;有差异的可使用MOVE-CORRESSPONDING匹配及赋值相同的字段

    例子:

    USERINF-SID = 'A001'.  "USERINF已定义好的
    USERINF-NAME = 'JE'.
    USERINF-TEL = '10-123'.
    USER2 - USERINF.
    WRITE:USER2,/USERINF-SID.
    
    DATA:BEGIN OF USERDETAIL,
    	SID(10) TYPE C,
    	NAME(20) TYPE C,
    	BIRDATE TYPE D,
    	ADD(50) TYPE C,
    END OF USERDETAIL.
    
    MOVE-CORRESSPONDING USERINF TO USERDETAIL.
    
    结构体的继承
    • 参考已存在的结构体创建一个属于相同的新结构体
    • 可在新结构体中增加字段
    • 定义语句:INCLUDE STRUCTURE

    例子:

    DATA:BEGIN OF EMPLOYEE.
    	INCLUDE STRUCTURE USERINF.
    DATA:BIRTHDATE TYPE D,
    	ADD(50) TYPE C,
    END OF EMPLOYEE.
    
    MOVE-CORRESSPONDING USERINF TO USERDETAIL.
    
    展开全文
  • 话说,今天遇到这么一个问题。...所以,我想请教大虾们一下,结构体指针赋值的时候赋值的什么,是地址吗?是不是像int* 赋给 int* 一样? 还有,再temp = mru后,能够delete temp吗?delete的是哪个空间?
  • C++ 结构体初始化与赋值

    万次阅读 多人点赞 2016-05-30 13:28:57
    1.CC++结构体的初始化今天看到项目中对自定义结构体初始化方式有点陌生,特在此罗列一下可用的结构体初始化的方式。对结构体struct A { int b; int c; }有几种初始化方式:第一种:struct A a = { .b = 1, ...

    在这里插入图片描述

    1.结构体初始化

    结构体是常用的自定义构造类型,是一种很常见的数据打包方法。结构体对象的初始化有多种方式,分为顺序初始化、指定初始化、构造函数初始化。假如有如下结构体。

    struct A {
    	int b;
    	int c;
    };
    

    (1)顺序初始化因为书写起来较为简约,是我们最常用的初始化方式,但相对于指定初始化,无法变更数据成员初始化顺序,灵活性较差,而且表现形式不直观,不能一眼看出 struct 各个数据成员的值。

    A a = {1, 2};
    

    (2)指定初始化(Designated Initializer)实现上有两种方式,一种是通过点号加赋值符号实现,即“.fieldname=value”,另外一种是通过冒号实现,即“fieldname:value”,其中 fieldname 为结构体成员名称 。前者是 C99 标准引入的初始化方式,后者是 GCC 的扩展。遗憾的是有些编译器并不支持指定初始化,比如 Visual C++。

    // 点号+赋值符号
    A a = {.b = 1, .c = 2};
    
    //冒号
    A a = {b:1, c:2};
    

    Linux 内核喜欢用 .fieldname=value 的方式进行初始化,使用指定初始化,一个明显的优点是成员初始化顺序和个数可变,并且扩展性好,比如在结构体非末尾处增加字段时,避免了传统顺序初始化带来的大量修改。

    (3)构造函数初始化常见于 C++ 代码中,因为 C++ 中的 struct 可以看作 class,结构体也可以拥有构造函数,所以我们可以通过结构体的构造函数来初始化结构体对象。给定带有构造函数的结构体:

    struct A {
    	A(int b,int c) {
    		this->b=b;
    		this->c=c;
    	};
    	int b;
    	int c;
    }
    

    那么结构体对象的初始化可以像类对象初始化那样:

    A a(1,2);
    

    注意: struct 如果定义了构造函数的话,就不能用大括号进行初始化了,即不能再使用指定初始化与顺序初始化了。

    2.结构体赋值

    变量的赋值和初始化是不一样的,初始化是在变量定义的时候完成的,是属于变量定义的一部分,赋值是在变量定义完成之后想改变变量值的时候所采取的操作。还是给定结构体 A:

    struct A {
    	int b;
    	int c;
    };
    

    注意: 结构体变量的赋值是不能采用大括号的方式进行赋值的,例如下面的赋值是不允许的。

    A a;
    a={1,2};	// 错误赋值
    

    下面列出常见结构体变量赋值的方法。

    (1)使用 memset 对结构体变量进行置空操作:

    // 按照编译器默认的方式进行初始化(如果 a 是全局静态存储区的变量,默认初始化为0,如果是栈上的局部变量,默认初始化为随机值)
    A a; 
    memset(&a,0,sizeof(a));
    

    (2)依次给每一个结构体成员变量进行赋值:

    A a; 
    a.b=1;
    a.c=2;
    

    (3)使用已有的结构体变量给另一个结构体变量赋值。也就是说结构体变量之间是可以相互赋值的。

    A a = {1,2};
    struct A a1;
    a1=a; 				// 将已有的结构体变量赋给a1
    

    初始化与赋值有着本质的区别,初始化是变量定义时的第一次赋值,赋值则是定义之后的值的变更操作,概念上不同,所以实现上也不一样。


    参考文献

    2016腾讯春季校园实习招聘技术岗初试(一面)问题汇总(CC++后台)
    结构体初始化
    C结构体-designated initializer
    C语言结构体声明中冒号的使用(占位符) & C结构体的乱序初始化

    展开全文
  • 我知道C#里的结构体属于值类型,在直接赋值的时候会进行拷贝,有没有语法像引用传参那样,不引发拷贝呢? 比如: void myaction(ref Size size) { } myaction(ref currentSize); 我想实现这样的效果(有...
  • 定义局部变量,然后用结构体赋值 */ typedef struct { int a; int b; } Stu1; typedef struct { int c; char d; } Stu2; typedef struct { Stu1 st1; Stu2 st2; } Stu3; int main () { // 适用于小端C.
    #include <stdio.h>
    /* 嵌套结构体赋值 
        1.memcpy实现
        2.定义局部变量,然后用结构体赋值
    */
    typedef struct {
        int a;
        int b;
    } Stu1;
    
    typedef struct {
        int c;
        char d;
    } Stu2;
    
    typedef struct {
        Stu1 st1;
        Stu2 st2;
    } Stu3;
    
    int main ()
    {
        // 适用于小端CPU
        char data[16] = {1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0};
        char data1[16] = {5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0};
        printf("struct inner sizeof(int) = %d\n", sizeof(int));
    
        Stu1 *s1 = (Stu1 *)data;
        printf("s1.a = %d s1.b = %d \n", s1->a, s1->b);
        Stu2 *s2 = (Stu2 *)(s1 + 1);
        printf("s2.c = %d s2.d = %d \n", s2->c, s2->d);
        Stu2 *s22 = (Stu2 *)((char *)data1 + sizeof(Stu1));    
        printf("s22.c = %d s22.d = %d \n", s22->c, s22->d);
        
        Stu3 *s3 = (Stu3 *)data;
        printf("s3->st1.a = %d s3->st1.b = %d s3->st2.c = %d s3->st2.d = %d  \n",
            s3->st1.a, s3->st1.b, s3->st2.c, s3->st2.d);
    
        /* 1.malloc 申请内存,然后memcpy拷贝拼接 */
        Stu3 *s4 = (Stu3 *)malloc(sizeof(Stu3));
        memcpy(s4, s1, sizeof(Stu1));
        memcpy((char *)s4 + sizeof(Stu1), s22, sizeof(Stu2));
        printf("s4->st1.a = %d s4>st1.b = %d s4->st2.c = %d s4->st2.d = %d  \n",
            s4->st1.a, s4->st1.b, s4->st2.c, s4->st2.d);
        
        /* 2.局部结构体栈内存 直接赋值拼接 */
        Stu3 s5;
        s5.st1 = *s1;
        s5.st2 = *s22;
        /* memcpy等效实现 */
        //memcpy(&s5, s1, sizeof(Stu1));
        //memcpy((char *)&s5 + sizeof(Stu1), s22, sizeof(Stu2));
        printf("s5.st1.a = %d s5.st1.b = %d s5.st2.c = %d s5.st2.d = %d  \n",
            s5.st1.a, s5.st1.b, s5.st2.c, s5.st2.d);
        return 0;
    }

    展开全文
  • struct s { int a; }; ...为什么全局结构体变量成员只能在函数内调用?...因为g.a=1是赋值语句,不是初始化语句,赋值语句只能运行的时候可以执行。 转载于:https://www.cnblogs.com/any91/p/6...
  • 1.结构体的初始化 结构体是常用的自定义构造类型,是一种很常见的数据打包方法。结构体对象的初始化有多种方式,分为指定初始化、顺序初始化、构造函数初始化。假如有如下结构体。 struct A { int b; int c; } ...
  • CC++结构体初始化与赋值

    万次阅读 2018-03-01 09:38:31
    1.CC++结构体的初始化今天看到项目中对自定义结构体初始化方式有点陌生,特在此罗列一下可用的结构体初始化的方式。对结构体struct A { int b; int c; }1234有几种初始化方式:第一种:struct A a = { .b = 1, ....
  • 多字段更新?并发编程中,原子更新多个字段是常见的需求。举个例子,有一个 struct Person 的结构体,里面有两个字段。我们先更新 Person.name,再更新 Person.ag...
  • 结构体

    千次阅读 多人点赞 2019-01-09 09:40:12
    结构体与数组的区别: 数组:只能由多个相同类型数据构成。 结构体:可以由多个不同类型的数据构成。 结构体定义 定义结构体类型 struct Person { //里面的3个变量,可以称为是结构体的成员或者属性 int age; ...
  • 局部变量与全局变量  按照作用域不同将变量分为 局部变量和全局变量  局部变量:定义在函数内部变量  作用域:从定义的那一行开始直到所在代码结束  生命周期:从定义的那一行开始只直到其所在代码结束  ...
  • 结构体传参: # include struct st { char a; short b; int c; int d; int e; }; void Function(st s) { } int main(int argc, char* argv[]) { st s; s.a = 1; s.b = 2; s.c = 3; s.d = 4; s.e = 5; ...
  • 1.结构体的初始化 结构体是常用的自定义构造类型,是一种很常见的数据打包方法。结构体对象的初始化有多种方式,分为...实现上有两种方式,一种是通过点号加赋值符号实现,即“.fieldname=value”,另外一种是通过冒...
  • 首先介绍一下初始化、赋值、定义、声明这四个概念。 初始化:创建变量并给它赋初值。初始化和赋初值不同。初始化=带指定初始值的定义。一个程序中,一个变量只能初始化一次。如:int i=0; 赋值:擦除对象的当前值...
  • 结构体给函数当形参时,由于主函数里也没分配结构体空间,只有结构体的地址,局部函数里面只能给局部结构体地址,给结构体的地址放在结构体地址的空间中,要有存放结构体地址空间的地址,也就是下面例子中的cJSON** ...
  • //这样是错误的,这是全局变量的赋值,是错误的。 int main() { return 0; } 例2: int a; a =1; //这样是错误的,这是全局变量的赋值,是错误的。 int main() { } 例3: int a ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,949
精华内容 9,979
关键字:

结构体局部赋值