以下两种在for循环中使用C结构体的方式,哪一种更可取?

huangapple go评论70阅读模式
英文:

Which of the following two ways of using C structs in a for loop is preferred?

问题

以下是您要翻译的内容:

让我们假设我们想在一个包含结构体的for循环中执行一些操作。这个结构体将包含一个指向字符的指针元素。因此,其形式如下:

struct foo
{
    char *s;    
}foo;

struct foo *new_foo(){
    struct foo *myfoo = malloc(sizeof(foo));
    myfoo->s = NULL;
    
    return myfoo;
}

void free_foo(struct foo *myfoo){
    free(myfoo->s);
}

for循环的基本结构是,给定一些变量(这些变量依赖于循环索引),它计算某些内容并返回有关foo的信息(然后这些信息可以用于某些输出或用于其他计算,但我不会详细说明)。实现这种效果有两种方法,一种是通过返回struct foo的函数,另一种是struct foo *。为了演示一个示例(尽管是愚蠢的示例),可以是:

struct foo foo_fun1(int size)
{
    int i;
    struct foo myfoo;
    myfoo.s = calloc(size+1, sizeof(char));
    
    for (i=0; i<size; i++){
        myfoo.s[i] = 'A';
    }
    
    return myfoo;
    
}

struct foo *foo_fun2(int size)
{
    int i;
    struct foo *myfoo = new_foo();
    myfoo->s = calloc(size+1, sizeof(char));
    
    for (i=0; i<size; i++){
        myfoo->s[i] = 'A';
    }
    
    return myfoo;
}

它们以相同的方式实现效果,如果按以下方式使用,则不会有任何泄漏:

int main() {
    int i;
    
    struct foo myfoo1;
    struct foo *myfoo2 = NULL;
    
    for (i=0; i<10; i++){
        
        myfoo1 = foo_fun1(i);
        myfoo2 = foo_fun2(i);
        
        free_foo(&myfoo1);
        free_foo(myfoo2);
        free(myfoo2);
        
    }
    
    
    return 0;
}

在更现实的情况下,这个for循环可能会运行几百万次(虽然不使用这些函数!),创建的结构体可能包含多个不同长度的字符串,长度可能高达数百个字符。我的问题是,从风格或效率方面考虑,人们会优先考虑其中一种方法吗?
英文:

Let's say that we want do some operations in a for loop involving a struct. This struct will contain an element which is a pointer to char. So something of the following form:

struct foo
{
    char *s;    
}foo;

struct foo *new_foo(){
    struct foo *myfoo = malloc(sizeof(foo));
    myfoo-&gt;s = NULL;
    
    return myfoo;
}

void free_foo(struct foo *myfoo){
    free(myfoo-&gt;s);
}

The basic structure of the for loop is that given some variables (which depend on the loop index) it computes something and returns the information about foo (then this may be directed to some output or used for other calculations but I wont get into details of that). There are two ways of achieving such an effect, one is via a function that returns struct foo and the other struct foo *. To demonstrate an example (though a stupid one), these could be:

struct foo foo_fun1(int size)
{
    int i;
    struct foo myfoo;
    myfoo.s = calloc(size+1, sizeof(char));
    
    for (i=0; i&lt;size; i++){
        myfoo.s[i] = &#39;A&#39;;
    }
    
    return myfoo;
    
}

struct foo *foo_fun2(int size)
{
    int i;
    struct foo *myfoo = new_foo();
    myfoo-&gt;s = calloc(size+1, sizeof(char));
    
    for (i=0; i&lt;size; i++){
        myfoo-&gt;s[i] = &#39;A&#39;;
    }
    
    return myfoo;
    
}

They achieve the same effect without any leaks if used as such:

int main() {
    int i;
    
    struct foo myfoo1;
    struct foo *myfoo2 = NULL;
    
    for (i=0; i&lt;10; i++){
        
        myfoo1 = foo_fun1(i);
        myfoo2 = foo_fun2(i);
        
        free_foo(&amp;myfoo1);
        free_foo(myfoo2);
        free(myfoo2);
        
    }
    
    
    return 0;
}

In the more realistic scenario this for loop would be going for possibly couple million times (not with these functions though!) and the created structs could contain multiple different strings of lengths that could be as much as couple hundred chars. My question is style or efficiency wise would one consider one method over the other?

答案1

得分: 1

以下是您要翻译的部分:

Which of the following two ways of using C structs in a for loop is preferred? [sic]

显然,有两种截然不同的选择,您需要考虑:

一种是通过返回结构体 foo,另一种是结构体 foo *。

返回 struct foo 的重要特点包括:

  • 调用函数不会为结构体分配空间的成本。

  • 结构体从调用函数复制到调用者。 如果结构体很大,复制可能很昂贵,但如果结构体很小,复制就很便宜。

  • 调用者不需要负责释放结构体本身(尽管它可能需要负责释放其内容,如示例中所示)。

  • 调用者存储结构体数据的对象(如果有的话)的生命周期由调用者控制。

返回 struct foo * 的重要特点包括:

  • 调用函数(可能)需要为结构体分配空间的成本。

  • 仅将指向结构体的指针复制回给调用者。 无论结构体的大小如何,这都很便宜。

  • 假设指向的结构体是动态分配的,就像示例中一样,调用者需要负责释放结构体本身,而不仅仅是释放其内容。

  • 存储结构体的对象的生命周期由调用函数控制(并且可能是“分配”的持续时间,尽管这并非必需)。

我的问题是从风格或效率的角度,是否会考虑一种方法而不是另一种方法?

假设示例驱动代码反映了预期的用法,即不需要让结构体对象在创建它们的循环体执行结束后继续存在,我倾向于节省分配和释放多个 struct foo 对象的成本,因为动态内存分配相对昂贵。 如果 struct foo 很小,那么您的第一种选择将是返回 struct foo。 但如果 struct foo 很大,那么我会考虑使用一个 初始化 函数而不是对象创建函数:

void foo_fun3(struct foo *the_foo, int size) {
    char *s = malloc(size + 1);

    if (s) {
        memset(s, 'A', size);
        s[size] = '
void foo_fun3(struct foo *the_foo, int size) {
char *s = malloc(size + 1);
if (s) {
memset(s, 'A', size);
s[size] = '\0';
}
the_foo->s = s;  
}
// ...
// 用法:
void f(void) {
struct foo a_foo;
struct foo *a_foo_pointer = malloc(sizeof(*a_foo_pointer));
foo_fun3(&a_foo, A_SIZE);
foo_fun3(a_foo_pointer, A_SIZE);
// ...
}
'; } the_foo->s = s; } // ... // 用法: void f(void) { struct foo a_foo; struct foo *a_foo_pointer = malloc(sizeof(*a_foo_pointer)); foo_fun3(&a_foo, A_SIZE); foo_fun3(a_foo_pointer, A_SIZE); // ... }

这种方法的优势在于调用者可以控制是否执行整个结构体的内存分配,而且它避免了从调用函数复制结果到调用者的成本。

就风格而言,这三种方式都可以接受,根据我的经验,结构体返回的方式不如其他两种方式常用。 就效率而言,我倾向于认为尽可能避免动态分配内存在性能上可能对您有利,但您应该始终通过测试来回答性能问题。

说到避免动态分配内存,您还应考虑是否可以避免分配结构体的成员。 如果您可以对内部缓冲区的大小设置上限,那么在示例代码中确实可以这样做,但在实际场景中可能还有其他考虑。

英文:

> Which of the following two ways of using C structs in a for loop is preferred? [sic]

Evidently, there are two contrasting alternatives you want to consider:

> one is via a function that returns struct foo and the other struct foo *.

Important characteristics of returning a struct foo include:

  • The called function does not incur the cost of allocating space for the structure.

  • The structure is copied from called function to the caller. This copying may be costly if the structure is large, but it is pretty cheap if the structure is small.

  • The caller does not acquire a duty to free the structure itself (though it may acquire a duty to free its contents, as in the example)

  • The lifetime of the object, if any, in which the caller stores the structure data is controlled by the caller.

Important characteristics of returning a struct foo * include:

  • The called function (probably) incurs the cost of allocating space for the structure.

  • Only a pointer to the structure is copied back to the caller. This is pretty cheap regardless of the size of the structure.

  • Presuming that the pointed-to structure is dynamically allocated, as in the example, the caller does acquire a duty to free the structure itself, in addition to any duty it may acquire to free the contents.

  • The lifetime of the object in which the structure is stored is controlled by the called function (and presumably it is "allocated" duration, though it does not have to be).

> My question is style or efficiency wise would one consider one method over the other?

Supposing that the example driver code reflects the intended usage, in that the structure objects are not required to live past the end of the loop-body execution in which they are created, I would be inclined to save the cost of allocating and deallocating many struct foo objects, as dynamic memory allocation is relatively expensive. If struct foo is small, then that would mean your first alternative, returning a struct foo. But if struct foo were large, then I would consider using an initialization function instead of an object-creation function:

void foo_fun3(struct foo *the_foo, int size) {
    char *s = malloc(size + 1);

    if (s) {
        memset(s, &#39;A&#39;, size);
        s[size] = &#39;
void foo_fun3(struct foo *the_foo, int size) {
char *s = malloc(size + 1);
if (s) {
memset(s, &#39;A&#39;, size);
s[size] = &#39;\0&#39;;
}
the_foo-&gt;s = s;  
}
// ...
// usage:
void f(void) {
struct foo a_foo;
struct foo *a_foo_pointer = malloc(sizeof(*a_foo_pointer));
foo_fun3(&amp;a_foo, A_SIZE);
foo_fun3(a_foo_pointer, A_SIZE);
// ...
}
&#39;; } the_foo-&gt;s = s; } // ... // usage: void f(void) { struct foo a_foo; struct foo *a_foo_pointer = malloc(sizeof(*a_foo_pointer)); foo_fun3(&amp;a_foo, A_SIZE); foo_fun3(a_foo_pointer, A_SIZE); // ... }

This approach has the advantage that the caller controls whether memory allocation for the overall structure is performed, and it avoids the cost of copying a result back from called function to caller.

All three are fine stylistically, according to me, though the structure-return alternative is less used than the other two in my experience. As far as efficiency goes, I'm inclined to think that avoiding dynamic allocation as much as is reasonably possible is likely to be to your advantage performance-wise, but you should always answer performance questions by testing.

And speaking of avoiding dynamic allocation, you should consider whether you can avoid allocating the structure member(s), too. You indeed should be able to do that in the example code if you can place an upper bound on the size of the internal buffer, but there may be additional considerations in a real-world scenario.

huangapple
  • 本文由 发表于 2023年4月10日 21:09:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/75977422.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定