2

我会避免使用 malloc 来初始化结构,并且我正在寻找使用 oo 样式(如果可能)设计 C 软件的最佳实践。

只有 C99,不是 C++

第一个问题,当使用像对象这样的结构时,什么是可取的?typedef 它的指针与否?

这些是我的测试(所有工作都使用 gcc 编译器):

情况1

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

typedef struct sItem{
    int n;
    char* text;
} oItem, *Item;

int main(int argc, char** argv) {
    Item i1=(&(oItem){.n=1, .text="A"});
    Item i2=(&(oItem){.n=100, .text="ABC"});
    printf("%d, %s, %d\n", i1->n, i1->text, sizeof(*i1)); // 1, "A", 8
    printf("%d, %s, %d\n", i2->n, i2->text, sizeof(*i2)); // 1, "ABC", 8
    return (EXIT_SUCCESS);
}

这有效,但我认为它不应该因为文本未初始化为包含字符串。这是一段无效的代码吗?

案例2

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

typedef struct sItem{
    int n;
    char text[5];
} oItem, *Item;

int main(int argc, char** argv) {
    Item i1=(&(oItem){.n=1, .text="A"});
    Item i2=(&(oItem){.n=100, .text="ABC"});
    printf("%d, %s, %d\n", i1->n, i1->text, sizeof(*i1)); // 1, "A", 12
    printf("%d, %s, %d\n", i2->n, i2->text, sizeof(*i2)); // 1, "ABC", 12
    return (EXIT_SUCCESS);
}

这行得通,我认为它是正确的,是吗?

案例3

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

#define Item_new(i, n, s) (&(oItem){0});Item_ctor(i, n, s);
#define Item_neww(i, x, s) (&(oItem){\
        .n=x,\
        .text=s\
})

typedef struct sItem{
    int n;
    char text[5];
} oItem, *Item;


void Item_ctor(Item i, int n, char* text){
    i->n=n;
    strcpy(i->text, text);
}

int main(int argc, char** argv) {
    Item i1=Item_new(i1, 10, "ABC");
    Item i2=Item_neww(i2, 10, "ABC");
    printf("%d, %s, %d\n", i1->n, i1->text, sizeof(*i1)); // 10, "ABC", 12
    printf("%d, %s, %d\n", i2->n, i2->text, sizeof(*i2)); // 10, "ABC", 12
    return (EXIT_SUCCESS);
}

我认为这很好,但是隐藏了代码,并且可能有害,您怎么看?I case 3,最好的选择是什么:宏还是构造函数?

4

2 回答 2

2

不要做 3,包含 unprotected 的宏;让我非常紧张。

相反,我会用以下内容替换您的“新”和“ctor”

#define Item_new(i, n, s) Item_ctor(&(oItem){0}, n, s)

Item Item_ctor(Item i, int n, char* text){
    if (i) {
      i->n=n;
      strncpy(i->text, text, 4);
    }
    return i;
}

这并没有打破用户的期望Item_new:一个真正的函数,比如返回一个值的宏。

并且 ctor 应该进行必要的检查并且永远不会覆盖内存,i->text[4]将永远是0. (更好的办法是使用一个符号常量,而不是5将它也用于strncpy调用。)

于 2012-08-19T23:54:26.560 回答
1

案例 3 是我最常看到的,并且会推荐。将代码包装在构造函数中非常好——为什么隐藏代码会成为问题?事实上,这是一个特性——从实现中隐藏接口。此外,不要为此使用宏 - 这对于宏来说是一项过于复杂的任务,然而,宏通常是邪恶的。

方法 1 和 2 非常丑陋(事实证明,它们也是 UB)并且在我看来不可读。此外,案例 1 不是 const 正确的,要么使用案例 2(好吧,也不要使用它,因为它会调用未定义的行为)或将“文本”声明为const char *.

于 2012-08-19T23:03:47.133 回答