通过值或C中的struct的多个参数

Max*_*xux 5 c stack struct pointers micro-optimization

让我们假设这个结构:

typedef struct mytest_t {
    uint8_t field1;
    uint32_t field2;
    uint64_t field3;
    uint64_t field4;
    uint16_t field5;
    uint32_t field6;

} mytest_t;
Run Code Online (Sandbox Code Playgroud)

还有一些想要创建此结构的函数(有点像一个对象):

int something_with(uint8_t field1, uint32_t field2, uint64_t field3, uint16_t field5) {
    mytest_t *object = malloc(sizeof(mytest_t));

    object->field1 = field1;
    object->field2 = field2;
    object->field3 = field3;
    object->field4 = 0x12345678;
    object->field5 = field5;
    object->field6 = 42;

    dosomethingwith(object);
    return 0;
}

void initial() {
    something_with(123, 456, 789, 456);
}
Run Code Online (Sandbox Code Playgroud)

这些功能纯粹是出于我的情况。此函数就像一个帮助程序,在代码中有一个单一的点,在该点上,对象被填充然后转发到其他对象。

注意:这个例子很小,假设参数要长2到3倍。

为了避免将大量参数传递给函数,并使调用变得冗长且难以阅读,我考虑将一个预填充的mytest_t结构作为参数传递(假设需要的字段正确填充)。

将struct作为值或指针传递会更好吗?取消引用所有字段的成本是多少?既然所有内容都在堆栈中,那有什么区别吗?编译器可以某种方式对其进行优化吗?

void initial() {
    mytest_t source = {
        .field1 = 123,
        .field2 = 456,
        .field3 = 789,
        // field4 not needed
        .field5 = 456,
        // field6 not needed
    };

    call_by_value(source);
    call_by_ptr(&source);
}

int call_by_value(mytest_t origin) {
    mytest_t *object = malloc(sizeof(mytest_t));

    object->field1 = origin.field1;
    object->field2 = origin.field2;
    object->field3 = origin.field3;
    object->field4 = 0x12345678;
    object->field5 = origin.field5;
    object->field6 = 42;

    dosomethingwith(object);
    return 0;
}

int call_by_ptr(mytest_t *origin) {
    mytest_t *object = malloc(sizeof(mytest_t));

    object->field1 = origin->field1;
    object->field2 = origin->field2;
    object->field3 = origin->field3;
    object->field4 = 0x12345678;
    object->field5 = origin->field5;
    object->field6 = 42;

    dosomethingwith(object);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我的第一个假设就是传递值,因为值只会复制堆栈中的所有内容,并且没有任何好处,但是为每个字段重新引用对象是否比复制它更昂贵?指针版本是否可能不会以大量的高速缓存未命中而结束而不是堆栈版本是否会结束?

小智 0

这取决于,如果您要始终使用相同的值,那么预填充的 mytest_t 结构将是更好的选择...但是如果您认为会在另一个函数内的某个时刻更改这些值,那么... .将指针传递给预先填充的 mytest_t 更好。

对于编译器(和操作系统)来说,当您传递地址(指针)时会更好,因为他不需要向操作系统请求更多内存,他已经拥有了他需要的东西(指针和值)。

您唯一关心的应该是......不要丢失您的指针,并且在更新任何数据时始终知道您正在更改什么。