结构体内结构体指针是,在函数中对结构体指针进行操作值会发生变化?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结构体内结构体指针是,在函数中对结构体指针进行操作值会发生变化?相关的知识,希望对你有一定的参考价值。

```
typedef struct node1

int data;
struct node1 *vul;
intnode;
int main()

intnode *c,*s;
c= (intnode *)malloc(sizeof(intnode));
c->data = 5;
s = (intnode *)malloc(sizeof(intnode));
s->data = 8;
c->vul = s;
s->vul= NULL;
test_pointer(*c);
printf("%d", c->data);
printf("%d", c->vul->data);

```
int test_pointer(intnode a)

intnode *p;
p = &a;
p->data= p->data + 1;
p->vul->data += 2;

//结果是 10 ,5, 为什么c->date 不变化 但是c->vul->data改变,传入函数的不是结构体指针啊

参考技术A

关键在于比较一下&a  和 &c的比较,看看两个是否同一个地址。

如果&a 和&c 不是同一个地址,则c指针指向的地址内容不会发生变化。

在test_pointer函数中,参数是intnode a  而非&a ,说明只是一个形式参数,调用时,是对c对象进行的一次拷贝构造,也就是说,在内存里面重新分配了一个c单元的拷贝。

通过c->vul 指向的指针对s内存单元地址进行分配。

通过比较以下几个地址

    &a  &c  比较一下两者是否同一内存区域

    &s  p->val【注意,是p->val 而非  &p->val】  比较。

    &s->data   &p->val->data   比较。

通过以上三个地址的比较,我想你应该会有一个明确的答案了。

本回答被提问者采纳
参考技术B 1、非引用传递下,改变形参的值不会改变实参的值。这里形参a和*c是两个对象,改变a的成员data不会影响*c的data值。
2、结构体和类可以理解为是一个东西,只是他们的默认访问权限不一样。类具有默认构造,当使用intnode a = b时,会将b的所有成员的值对a复制一遍。所以,在题问中,形参a的指针vul的值和*c的vul的值一致。
3、通过指针可以直接对指定的内存块进行操作。根据2描述,a.vul和(*c).vul值一致,即它们指向了同一块内存,所以改变a.vul->data的值会同步改变(*c).vul(即c->vul->data)的值。

在Golang里如何实现结构体成员指针到结构体自身指针的转换

原文地址:http://goworldgs.com/?p=37

在C语言中有一个经典的宏定义,可以将结构体struct内部的某个成员的指针转化为结构体自身的指针。下面是一个例子,通过FIELD_OFFSET宏计算结构体内一个字段的偏移,函数getT可以从一个F*的指针获得对应的T*对象。

struct F {
    int c;
    int d;
}
 
struct T{
    int a;
    int b;
    struct F f;
}
 
#define FIELD_OFFSET(type, field) ((int)(unsigned char *)(((struct type *)0)->field))
 
struct T* getT(struct F* f) {
    return (T*)((unsigned char *)f - FIELD_OFFSET(T, F))
}


在Golang中能否实现同样的功能?尝试写如下的代码:

type T struct {
	a int
	b int
	f F
}

type F struct {
	c int
	d int
}

func (m *F) T1() *T {
	var dummy *T
	fieldOffset := uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(dummy))
	return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(m)) - fieldOffset))
}

编译通过,运行!panic: runtime error: invalid memory address or nil pointer dereference。这里dummy *T是nil,虽然代码并不访问dummy所指向的内容,但是Golang依然不允许这样使用这个指针。

既然Golang不允许使用nil指针,那么我们可以通过创建一个无用的T对象来绕开这个问题,代码如下:

func (m *F) T2() *T {
	var dummy T
	fieldOffset := uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(&dummy))
	return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(m)) - fieldOffset))
}

测试证明这个代码可以正常工作,并且我们可以使用另外一个函数TBad来进行性能对比:

func (m *F) TBad() *T {
	return (*T)(unsafe.Pointer(uintptr(unsafe.Pointer(m)) - 16))
}
 
func BenchmarkGetPtrByMemberPtr_T2(b *testing.B) {
	var t T
	for i := 0; i < b.N; i++ {
		if &t != t.f.T2() {
			b.Fatal("wrong")
		}
	}
}
 
func BenchmarkGetPtrByMemberPtr_TBad(b *testing.B) {
	var t T
	for i := 0; i < b.N; i++ {
		if &t != t.f.TBad() {
			b.Fatal("wrong")
		}
	}
}

测试结果:T2和TBad的运行开销分别为:1.44 ns/op和0.85 ns/op。

考虑到T2为什么会比TBad有更大的开销,我们怀疑T2里每次都需要在heap上创建一个T对象。如果T对象的大小很大的时候,创建T对象的开销也会增大,我们可以通过增大结构体T的大小来进行验证。我们将T结构体的定义修改为:

type T struct {
	a int
	b int
	f F
	e [1024]byte
}

再次运行发现T2的开销增大到37.8 ns/op。那么如何才能消除T结构体大小对这个函数的影响?Golang不允许我们使用nil指针,是不是我们只需要伪造一个*T的非nil指针即可?尝试写如下代码并进行测试:

func (m *F) T3() *T {
	var x struct{}
	dummy := (*T)(unsafe.Pointer(&x))
	fieldOffset := uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(dummy))
	return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(m)) - fieldOffset))
}


T3的开销降低到1.14 ns/op,接近最快的TBad的0.85 ns/op。更进一步的,我们可以直接使用*F指针作为dummy,代码如下:

func (m *F) T4() *T {
	dummy := (*T)(unsafe.Pointer(m))
	fieldOffset := uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(dummy))
	return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(m)) - fieldOffset))
}


但是测试表明T4和T3的开销完全一样,都是1.14 ns/op。

从目前为止,T3和T4的实现性能非常好,只比TBad里高一点点。推测原因是TBad不需要计算F类型field的偏移,在C语言里FIELD_OFFSET宏也是在编译时进行计算,但是在T3和T4中需要计算一次f *F字段在T结构体中的偏移。我们可以使用一个全局变量来保存字段的偏移,这样就不需要每次都进行计算,代码如下:

var fieldOffset uintptr
 
func init() {
	dummy := (*T)(unsafe.Pointer(&fieldOffset))
	fieldOffset = uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(dummy))
}
func (m *F) T5() *T {
	return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(m)) - fieldOffset))
}

测试表明T5的开销和TBad一样,都是0.85 ns/op,这个应该已经是极限了。

由于Go语言没有提供泛型机制,所以每个需要用到这个功能的类都需要定义自己的转换函数,而不能像C/C++那样使用通用的宏就可以实现。

如果你有更好的方案,欢迎留言告诉我!

以上是关于结构体内结构体指针是,在函数中对结构体指针进行操作值会发生变化?的主要内容,如果未能解决你的问题,请参考以下文章

数组强制转换成结构体指针,结构体内部指针的指向问题

结构体指针与结构体变量用作函数参数时有啥区别,在用法上

在Golang里如何实现结构体成员指针到结构体自身指针的转换

C 语言结构体 ( 结构体中嵌套一级指针 | 分配内存时先 为结构体分配内存 然后再为指针分配内存 | 释放内存时先释放 指针成员内存 然后再释放结构头内存 )

C 语言结构体 ( 指针运算与指针内存操作 | 结构体成员偏移量计算 )

结构体数组怎么做函数参数