函数调用作为其他函数的参数

时间:2014-03-12 19:08:15

标签: c arduino

(为了进行搜索或给出合适的标题,我不知道正确的术语)

我一直想知道:如果使用函数调用将参数提供给另一个函数,它是否重要(快速或大小编译)?我可以看到这样做有助于代码可读性,但有时使用一堆局部变量会变得乏味。

我的意思是:假设这些被多次调用(如在for..loop中),这之间有任何实际区别:

      byte patternType = mCols[i].getPatternType();
      byte stepIndex = mCols[i].update(m);
      byte patternValue = getPatternValue(patternType, stepIndex);

和此:

      byte patternValue = getPatternValue(mCols[i].getPatternType(), mCols[i].update(m));

3 个答案:

答案 0 :(得分:3)

评估参数的顺序未定义。如果功能是纯粹的,这应该无关紧要,但如果它们有副作用,它可以。

答案 1 :(得分:0)

这并不重要。如果在启用优化的情况下进行编译,则编译器应在两种情况下都发出相同的代码;但前者的优势在于,在关闭优化的情况下调试更容易。

我刚刚在我的覆盆子pi上试过这个,因为我手头没有arduino。

这是我的y1.c:

#include <stdlib.h>
#include <stdio.h>

typedef signed char byte;

struct something {
    byte (*getPatternType)();
    byte (*update)(int i);
};

int main(void) {
    struct something mCols[20];
    int i;
    int m=3;

    for (i=0; i<20; i++) {
            byte patternType=mCols[i].getPatternType();
            byte stepIndex=mCols[i].update(m);
            byte patternValue=getPatternValue(patternType, stepIndex);
            printf("%d\n", patternValue);
    }
    exit(0);
}

这是y2.c:

#include <stdlib.h>
#include <stdio.h>

typedef signed char byte;

struct something {
    byte (*getPatternType)();
    byte (*update)(int i);
};

int main(void) {
    struct something mCols[20];
    int i;
    int m=3;

    for (i=0; i<20; i++) {
            byte patternValue=getPatternValue(mCols[i].getPatternType(), mCols[i].update(m));
            printf("%d\n", patternValue);
    }
    exit(0);
}

然后:

pi@pi$ cc -O4 -S y1.c
pi@pi$ cc -O4 -S y2.c
pi@pi$ diff y1.s y2.s
13c13
<       .file   "y1.c"
---
>       .file   "y2.c"

如您所见,在汇编程序级别上,唯一的区别是嵌入式源文件名。

我添加了一个printf,因此编译器不会优化所有内容。它没有,这是y1.s:

    .arch armv6
    .eabi_attribute 27, 3
    .eabi_attribute 28, 1
    .fpu vfp
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 2
    .eabi_attribute 30, 2
    .eabi_attribute 18, 4
    .file   "y1.c"
    .section        .text.startup,"ax",%progbits
    .align  2
    .global main
    .type   main, %function
main:
    @ args = 0, pretend = 0, frame = 160
    @ frame_needed = 0, uses_anonymous_args = 0
    stmfd   sp!, {r4, r5, r6, lr}
    mov     r4, #0
    sub     sp, sp, #160
.L2:
    add     r5, sp, #0
    ldr     r3, [r5, r4]!
    blx     r3
    add     r4, r4, #8
    ldr     r3, [r5, #4]
    mov     r6, r0
    mov     r0, #3
    blx     r3
    mov     r1, r0
    mov     r0, r6
    bl      getPatternValue
    sxtb    r1, r0
    ldr     r0, .L5
    bl      printf
    cmp     r4, #160
    bne     .L2
    mov     r0, #0
    bl      exit
.L6:
    .align  2
.L5:
    .word   .LC0
    .size   main, .-main
    .section        .rodata.str1.4,"aMS",%progbits,1
    .align  2
.LC0:
    .ascii  "%d\012\000"
    .ident  "GCC: (Debian 4.6.3-14+rpi1) 4.6.3"
    .section        .note.GNU-stack,"",%progbits

注意编译器如何处理循环:控制寄存器r4增加结构(8)的大小而不是1,并在结尾处与160而不是20进行比较。这样可以节省数组索引通常需要的乘法,但是调试器无法在循环中获得i的“实际”值。

答案 2 :(得分:0)

一般建议:

专注于代码清晰度和可读性,让编译器完成其工作并优化掉不必要的东西。不要试图超越你的编译器,它会比程序员在代码优化方面做得更好。

另外,请注意pat建议的评估顺序和序列点。如果你把代码写得足够清楚,那么你通常不需要担心这些事情。