【问题标题】:How can I return if error - C如果出现错误,我该如何返回 - C
【发布时间】:2014-05-21 11:34:57
【问题描述】:

基本上我有这个数据结构:

typedef struct Data {
    char *str;
    int n;
} TData, *AData;

我想使用 malloc 为 str 分配空间(在该变量中,我放置了一个从文件中读取的字符串,老实说,考虑到我的问题的主题,这并不重要)。因此,我创建了一个函数TData InitData(const char *file_name)

TData InitData(const char *file_name) {
    TData data;

    data.str = malloc(1024);
    if (!data.str) {
        printf("Allocation error!\n");
        // I have no idea what to return here in order to exit the function.
        return (...);
    }

    return data;
}

现在,在我的“main()”函数中,我会这样调用:

int main(int argc, const char * argv[]) {
    TData data;
    if(!(data = ReadExpression(argv[1]))) {
        return 1;
    }
    // I do something with my data and the program ends.
    return 0;
}

如何使InitData函数中的return语句有效?

我知道另一种方式,可能推荐的方式是将TData datamain发送到InitData作为AData类型的参数并将函数的类型更改为int(会导致类似:int InitData(const char *file_name, AData data)) 或简单地使用AData

【问题讨论】:

  • return NULL; 也许?就像 malloc 一样。当然,您需要将 data 设为指向 *Tdata 的指针。
  • 不能工作,因为函数是TData 类型。为了返回“NULL”,它必须是 void * 或指针。
  • 您自己定义结构的语义。那么,sentinel-value 是什么,还是没有?我猜(TData){0, 0}。另外,考虑将您的结构定义为typedef struct data{size_t len;char data[]} data;,并始终将其分配在堆上。无论如何,在 typedef 后面隐藏指针的使用是危险的。
  • @Deduplicator 嗯,我明白你的意思,我认为这将是最好的解决方案。
  • 我的示例结构最后使用了不完整的数组类型,因此字符串直接保存在其他结构成员之后。不知何故,必须保留必要的空间...搜索 struct hack(C99 之前)和不完整的类型以获取以后的标准。

标签: c return


【解决方案1】:
  • 根据经验,始终将结构作为指针参数而不是按值传递。
  • 根据经验,编写复杂函数时,请保留返回值以防出错。

所以在你的情况下,将函数重写为:

bool InitData (TData* data, const char *file_name);

bool 可以用枚举替换以获得更详细的错误代码。现在函数结果只有“ok”或“error”。

【讨论】:

    【解决方案2】:

    C 没有内置的多值返回,所以通常你要做的是

    int InitData(const char *file_name, TData *returnedData) { ... }
    

    InitData 的返回值是错误/成功标志,TData 结构通过指针返回。

    【讨论】:

    • 在我意识到我无法发出错误返回信号之后,我的第一个想法是使用它。在问题中也说了同样的话。
    【解决方案3】:

    我在C做的不多,但是很多功能都是这样的:

    int foo(intargs, outargs);
    

    这背后的事情是您分配对象并将其作为引用传递给函数内部。

    函数的返回值将是enum 或类似的值,用于比较以确定发生了什么。

    【讨论】:

      【解决方案4】:

      我喜欢 MicroVirus 建议的方式。接受数据作为参数也可用于多线程代码安全。此外,您可以返回操作结果以检查调用代码。您的 TData 不是指针,因此您不能将其设置为 NULL,我认为这是一种告诉无效案例的方法。但是,这里还有另一个问题! InitData 中的 TData 数据是一个局部变量,应在函数返回时复制。我认为这不是性能问题的好习惯。我建议,接受对象作为参数(例如作为指针)将函数的结果返回给调用代码并检查数据的有效性。您可以将数据设置为 NULL 或不设置,但是当您返回结果值时,这会使一切都变得清晰,而无需检查数据对象本身。

      【讨论】:

      • 优化编译器应该能够省略复制对象,并在堆栈上就地构造它,因此与指针一样高效。
      • 如果您谈论的是 ROV (en.wikipedia.org/wiki/Return_value_optimization),是的,您是对的,但我不确定 C 是否属于这种情况。
      • 我承认,我认为 C 会有类似的优化,但快速的谷歌搜索什么也没找到;它散布着与 C++ 相关的文章。所以,也许我错了,而 C 没有那样做。
      【解决方案5】:

      正常返回data,但将main()中的代码更改为:

      data = ReadExpression(argv[1]);
      if ( !data.str ) {
          return 1;
      }
      

      您不能返回标量值来代替struct,并且您的问题表明您已经知道可以使用指针但选择不使用,因此这是最简单的替代方法。

      【讨论】:

      • 这是糟糕的程序设计。如果调用者知道结构的成员,为什么调用者不能从一开始就进行分配?您应该努力使用私有​​封装。
      • @Lundin:这不是我的程序。 OP正在询问如何以这种方式进行操作。至于“为什么调用者不能开始分配”,因为除了封装之外,函数还有更多的用途。将分配移动到单独的函数可以使main() 更具可读性,并且如果需要在其他地方执行相同的操作,或者在main() 中执行多次,可以避免重复。
      • 他正在为正确的 OO 设计做一些笨拙的尝试,通过将 malloc 封装在一个知道 TData 是什么的代码模块中。从代码模块中删除详细检查完全破坏了封装,并且无助于他学习 OO 程序设计。
      • 这是 C。有时 struct 只是 struct,一个数据记录,一个松散相关数据的集合,您既不想也不应该“封装”为某些“面向对象设计”。如果一个程序的作者没有使用“正确的 OO 设计”,就声称一个程序一定是“垃圾”是很奇怪的。
      • 我可以根据经验告诉你,世界上很少有没有某种面向对象设计的优秀 C 程序。在这个 C 上下文中,OO 本质上是指具有私有封装和“成员函数”的 ADT。极少数没有这种设计的好程序都很小:通常小于 1000 LOC,接近硬件,一些 8 位嵌入式系统等。在所有其他情况下,没有 OO 方法编写的程序几乎完全是废话。至少这是我在过去 15 年的大部分时间里阅读其他人的 C 代码后得出的结论。
      【解决方案6】:

      根据if (!data.str) 结果,您可以在数据结构集中设置一个标志,并始终返回data。 然后在你想知道发生了什么的时候检查标志。

      只是为了澄清: 你可以写这样的东西,或者类似的东西:

      `typedef struct Data 
      {
          char *str;
          int n;
          char myflag;
          const char *file_name;
      } TData, *AData;`
      
      `void InitData(TData *data) 
      {
          data.str = malloc(1024);
          if (!data.str) 
          {
              printf("Allocation error!\n");
              myflag=0;
          }
          else
          {
              myflag=1;
          }
          return data;
      }`
      

      但也见 cmets。只是想提供另一种观点。

      【讨论】:

      • 内存分配错误与struct的数据没有半点关系,所以不应该存在。
      • 好吧,你自己决定它是否与数据有关——要么数据只是原始数据,要么是原始数据加上某种分配信息。两种选择都有效。
      • 但是,如果您之后不需要分配数据,您可能不应该将其保存在结构中。我不知道它会用来做什么......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-01
      • 1970-01-01
      • 2011-12-29
      • 2022-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多