C 如何避免多次取消引用同一变量?

发布于 2024-12-11 01:02:58 字数 1794 浏览 0 评论 0原文

我有一个结构数组,并且有一些函数将使用这些结构的几个成员。我想避免每一行中的取消引用。我认为有某种方法可以在某个内存位置声明变量...类似:

someStruct &myStruct = arrayOfStructs[i];
myStruct.x = foo+bar*myStruct.y*myStruct.w;
//Instead of myStruct->x = foo+bar*myStruct->y*myStruct->w;
//It would/should even be possible to access the members in a similar way:
int &x = &myStruct.x;
x = x+4*y+2*z;
//This should avoid overhead of dereferencing the pointer, and offsetting to the member
//by just accessing that particular address of memory as though it was where the variable
//had always been.

这段示例代码可能有助于解释:

#define NUM_BIGSTRUCTS 10000

typedef struct {
  int a,b,c;
  float d,e,f;
} bigStruct;

bigStruct* arrayOfStructs;

void foo() {
  for(int i=0; i<NUM_BIGSTRUCTS; i++) {
    bigStruct* temp = arrayOfStructs[i];
    temp->f = (temp->d+temp->e)*((float)temp->a+temp->e);
    //more similar, with conditionals, etc...
    //actually I've got nested loops, and a very very large array
    //so any gains per inner loop would decrease my number of instructions exponentially

    //So, if I could declare a bigStruct and set its address to the location of a bigStruct in the array
    //then I could avoid a dereference every time I access a member of that bigStruct
    //Leaving just the member access overhead... which could be handled in a similar manner
    //if possible, and when appropriate
  }
}

int main(int argx, char** argv) {
  arrayOfStructs = g_new0(bigStruct,NUM_BIGSTRUCTS); //Allocate and 0 memory for simplicity

  foo();

  return 0;
}

我从来没有在 SO 上取得过巨大的成功,所以希望我解释了我的意思试图做。顺便说一句,我正在使用 C99,鉴于 c 的低级性质,我相信这是可能的。

[编辑] 看起来我正在寻找 C++ 的“引用”,但对于 C。即便如此,它们只允许赋值一次(初始化),这在我的示例中不起作用。我决定依靠编译器来优化对同一内存部分的多次访问。

谢谢, 詹姆斯·纽曼

I have an array of structs, and I have some functions that will be using several of the members of those structs. I would like to avoid the dereference in every line. I would think that there would be some way to declare a variable at a certain memory location... something like:

someStruct &myStruct = arrayOfStructs[i];
myStruct.x = foo+bar*myStruct.y*myStruct.w;
//Instead of myStruct->x = foo+bar*myStruct->y*myStruct->w;
//It would/should even be possible to access the members in a similar way:
int &x = &myStruct.x;
x = x+4*y+2*z;
//This should avoid overhead of dereferencing the pointer, and offsetting to the member
//by just accessing that particular address of memory as though it was where the variable
//had always been.

This bit of example code may help explain:

#define NUM_BIGSTRUCTS 10000

typedef struct {
  int a,b,c;
  float d,e,f;
} bigStruct;

bigStruct* arrayOfStructs;

void foo() {
  for(int i=0; i<NUM_BIGSTRUCTS; i++) {
    bigStruct* temp = arrayOfStructs[i];
    temp->f = (temp->d+temp->e)*((float)temp->a+temp->e);
    //more similar, with conditionals, etc...
    //actually I've got nested loops, and a very very large array
    //so any gains per inner loop would decrease my number of instructions exponentially

    //So, if I could declare a bigStruct and set its address to the location of a bigStruct in the array
    //then I could avoid a dereference every time I access a member of that bigStruct
    //Leaving just the member access overhead... which could be handled in a similar manner
    //if possible, and when appropriate
  }
}

int main(int argx, char** argv) {
  arrayOfStructs = g_new0(bigStruct,NUM_BIGSTRUCTS); //Allocate and 0 memory for simplicity

  foo();

  return 0;
}

I never have had great success on SO, so hopefully I explained what I'm trying to do. I'm using C99 btw, and I would believe it'd be possible given the low level nature of c.

[edit]
Looks like I was looking for 'References' from C++, but for C. Even so, they only allow assignment once(initialization), which wouldn't work in my example. I've decided to rely on the compiler to optimize away multiple accesses to the same section of memory.

Thanks,
James Newman

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

此生挚爱伱 2024-12-18 01:02:58

您正在尝试编译器优化比您手动执行的操作要好得多的操作。另外,C99 并没有您在示例中尝试定义它们的方式(特别是 C++ 解除引用声明)这些引用结构,如果您也变得非常大和深入,我建议您重新考虑您的算法。如果您尝试引入一些临时变量和更多内存来进行引用,您的生活将会变得更加困难。

例如,如果您查看:

struct some_struct {
        int a;
        struct {
                float f;
                double d;
        } s;
};

struct some_struct array[10000];

int process1(struct some_struct *r) {
#define R (*r)
        R.a+= 1;
        R.s.f = R.s.f/2;
        R.s.d = ( R.s.d + R.s.f ) * 2;
}

int process2(struct some_struct *r) {
        r->a+= 1;
        r->s.f = r->s.f/2;
        r->s.d = ( r->s.d + r->s.f ) * 2;
}

int doit() {
        int i;
        for (i = 0; i < sizeof(array)/sizeof(struct some_struct); i++ ) {
                struct some_struct *r = &array[i]; /* via reference */
                process1(r);
                process2(r);
        }
}

process1 和 process2 在 x86_64 平台上使用 gcc -O2 生成相同的汇编输出:

        .file   "foo.c"
        .text
        .p2align 4,,15
        .globl  process1
        .type   process1, @function
process1:
.LFB11:
        .cfi_startproc
        movss   .LC0(%rip), %xmm0
        addl    $1, (%rdi)
        mulss   8(%rdi), %xmm0
        movss   %xmm0, 8(%rdi)
        unpcklps        %xmm0, %xmm0
        cvtps2pd        %xmm0, %xmm0
        addsd   16(%rdi), %xmm0
        addsd   %xmm0, %xmm0
        movsd   %xmm0, 16(%rdi)
        ret
        .cfi_endproc
.LFE11:
        .size   process1, .-process1
        .p2align 4,,15
        .globl  process2
        .type   process2, @function
process2:
.LFB12:
        .cfi_startproc
        movss   .LC0(%rip), %xmm0
        addl    $1, (%rdi)
        mulss   8(%rdi), %xmm0
        movss   %xmm0, 8(%rdi)
        unpcklps        %xmm0, %xmm0
        cvtps2pd        %xmm0, %xmm0
        addsd   16(%rdi), %xmm0
        addsd   %xmm0, %xmm0
        movsd   %xmm0, 16(%rdi)
        ret
        .cfi_endproc
.LFE12:
        .size   process2, .-process2
        .p2align 4,,15
        .globl  doit
        .type   doit, @function
doit:
.LFB13:
        .cfi_startproc
        xorl    %edx, %edx

        movss   .LC0(%rip), %xmm2
        .p2align 4,,10
        .p2align 3
.L4:
        leaq    (%rdx,%rdx,2), %rax
        addq    $1, %rdx
        leaq    array(,%rax,8), %rax
        movss   8(%rax), %xmm1
        addl    $2, (%rax)
        mulss   %xmm2, %xmm1
        cmpq    $10000, %rdx
        unpcklps        %xmm1, %xmm1
        cvtps2pd        %xmm1, %xmm0
        mulss   %xmm2, %xmm1
        addsd   16(%rax), %xmm0
        movss   %xmm1, 8(%rax)
        unpcklps        %xmm1, %xmm1
        cvtps2pd        %xmm1, %xmm1
        addsd   %xmm0, %xmm0
        addsd   %xmm1, %xmm0
        addsd   %xmm0, %xmm0
        movsd   %xmm0, 16(%rax)
        jne     .L4
        rep
        ret
        .cfi_endproc
.LFE13:
        .size   doit, .-doit
        .comm   array,240000,32
        .section        .rodata.cst4,"aM",@progbits,4
        .align 4
.LC0:
        .long   1056964608
        .ident  "GCC: (GNU) 4.6.1"
        .section        .note.GNU-stack,"",@progbits

You're attempting something that the compiler optimization does much better than you can do manually. Also, C99, does not have these referencing constructs the way you are attempting to define them in your example—specifically the C++ dereferencing declarations—if you're also getting really big and deep, I suggest that you rethink your algorithm. If you attempt to introduce a number of temporary variables and more memory around to do referencing you are going to make your life harder.

For instance if you look at:

struct some_struct {
        int a;
        struct {
                float f;
                double d;
        } s;
};

struct some_struct array[10000];

int process1(struct some_struct *r) {
#define R (*r)
        R.a+= 1;
        R.s.f = R.s.f/2;
        R.s.d = ( R.s.d + R.s.f ) * 2;
}

int process2(struct some_struct *r) {
        r->a+= 1;
        r->s.f = r->s.f/2;
        r->s.d = ( r->s.d + r->s.f ) * 2;
}

int doit() {
        int i;
        for (i = 0; i < sizeof(array)/sizeof(struct some_struct); i++ ) {
                struct some_struct *r = &array[i]; /* via reference */
                process1(r);
                process2(r);
        }
}

process1 and process2 generate identical assembly outputs using gcc -O2 on x86_64 platform:

        .file   "foo.c"
        .text
        .p2align 4,,15
        .globl  process1
        .type   process1, @function
process1:
.LFB11:
        .cfi_startproc
        movss   .LC0(%rip), %xmm0
        addl    $1, (%rdi)
        mulss   8(%rdi), %xmm0
        movss   %xmm0, 8(%rdi)
        unpcklps        %xmm0, %xmm0
        cvtps2pd        %xmm0, %xmm0
        addsd   16(%rdi), %xmm0
        addsd   %xmm0, %xmm0
        movsd   %xmm0, 16(%rdi)
        ret
        .cfi_endproc
.LFE11:
        .size   process1, .-process1
        .p2align 4,,15
        .globl  process2
        .type   process2, @function
process2:
.LFB12:
        .cfi_startproc
        movss   .LC0(%rip), %xmm0
        addl    $1, (%rdi)
        mulss   8(%rdi), %xmm0
        movss   %xmm0, 8(%rdi)
        unpcklps        %xmm0, %xmm0
        cvtps2pd        %xmm0, %xmm0
        addsd   16(%rdi), %xmm0
        addsd   %xmm0, %xmm0
        movsd   %xmm0, 16(%rdi)
        ret
        .cfi_endproc
.LFE12:
        .size   process2, .-process2
        .p2align 4,,15
        .globl  doit
        .type   doit, @function
doit:
.LFB13:
        .cfi_startproc
        xorl    %edx, %edx

        movss   .LC0(%rip), %xmm2
        .p2align 4,,10
        .p2align 3
.L4:
        leaq    (%rdx,%rdx,2), %rax
        addq    $1, %rdx
        leaq    array(,%rax,8), %rax
        movss   8(%rax), %xmm1
        addl    $2, (%rax)
        mulss   %xmm2, %xmm1
        cmpq    $10000, %rdx
        unpcklps        %xmm1, %xmm1
        cvtps2pd        %xmm1, %xmm0
        mulss   %xmm2, %xmm1
        addsd   16(%rax), %xmm0
        movss   %xmm1, 8(%rax)
        unpcklps        %xmm1, %xmm1
        cvtps2pd        %xmm1, %xmm1
        addsd   %xmm0, %xmm0
        addsd   %xmm1, %xmm0
        addsd   %xmm0, %xmm0
        movsd   %xmm0, 16(%rax)
        jne     .L4
        rep
        ret
        .cfi_endproc
.LFE13:
        .size   doit, .-doit
        .comm   array,240000,32
        .section        .rodata.cst4,"aM",@progbits,4
        .align 4
.LC0:
        .long   1056964608
        .ident  "GCC: (GNU) 4.6.1"
        .section        .note.GNU-stack,"",@progbits
韵柒 2024-12-18 01:02:58

但是..没有什么开销可谈!

你想做的实际上是增加开销。

我认为你需要知道你不应该反对一种语言,而应该使用它;否则,就好像您试图用锤子将方钉推入圆孔一样。

But.. there is no overhead to talk about!

What you are trying to do is actually adding overhead.

I think you need to learn that you should not work against a language, you should instead work with it; otherwise it becomes as if you are trying to push a square peg through a round hole with a hammer.

粉红×色少女 2024-12-18 01:02:58

你的想法不会拯救你任何东西。指针使您可以使用非本地内存。根据定义,数组中的结构体距离很远,并且您不能在很远的地方声明局部变量——这将是矛盾的。

当您说 int &x = &myStruct.x; 时,您会混淆两个想法:

  • 局部变量:您可以很容易地

     int x = temp->x
        //与x一起工作...
        温度->x = x
    

    好处是,当您使用它时,您正在处理靠近您的东西。
    缺点是来回复制,但确实可能有一些东西。

  • 指针:另一种方式是

     int *x = &temp->x
       //像使用指针一样使用x
    

    但是,这确实没有太大帮助,因为这与到处使用 temp->x 没有太大区别。 (除非可能很清楚)。考虑一下数组:
    假设您有以下代码:

     int array[25];
      数组[3] = 数组[2] + 数组[3];
      数组[7] = 数组[3]*数组[7] + 数组[3]<<7;
    

    您建议将其转换为:

     int 数组[25]
     int *a = &array[3], *b=&array[2], *c=&array[7];
     *a = *b+*a;
     *c = (*a)*(*c) + *a<<7;
    

    它可能更具可读性,但生成的代码可能相似,重要的是,您对远程内存的混乱次数完全相同

Your idea wouldn't save you anything. Pointers let you work with non-local memory. By definition, the struct in your array is far away, and you can't declare a local variable far away--that would be oxymoronic.

When you say int &x = &myStruct.x;, you're confusing two ideas:

  • The local variable: you could rather easily

        int x = temp->x
        //work with x...
        temp->x = x
    

    the upside is that while you work with it, you're working with something close to you.
    The downside is the copying back and forth, but there really might be something to it.

  • The pointer: The other way is to

       int *x = &temp->x
       //work with x, like you would a pointer
    

    but, this really isn't too helpful, because this isn't much different than using temp->x all over the place. (Except possibly in clarity). Think about arrays:
    Suppose you have the following code:

      int array[25];
      array[3] = array[2] + array[3];
      array[7] = array[3]*array[7] + array[3]<<7;
    

    You propose transforming it into this:

     int array[25]
     int *a = &array[3], *b=&array[2], *c=&array[7];
     *a = *b+*a;
     *c = (*a)*(*c) + *a<<7;
    

    It might be more readable, but the generated code may be similar and importantly, you mess with far-away memory the exact same number of times.

别低头,皇冠会掉 2024-12-18 01:02:58

我认为您正在寻找放置新运算符,但那是 C++,而不是 C。

除此之外,我同意其他人的观点——不要管它。

I think you're looking for the placement new operator, but that's C++, not C.

Other than that, I agree with the others -- leave it alone.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文