【问题标题】:Returning error with unsigned values?返回无符号值的错误?
【发布时间】:2017-11-14 15:51:04
【问题描述】:

我正在制作一个数据结构库。我允许用户调用的一项功能是:

unsigned int index(struct myDataStructure, void* value);

它搜索我的数据结构并返回该值所在的index 位置。

例如

{ 'A', 'D', 'C' }

char val1 = 'A';
unsigned int location = index(s, &val1); // location = 0
char val2 = 'C';
location = index(s, &val2); // location = 2

如果列表中不存在该元素,那么我不知道要返回什么。以下是我目前排除的选项:

  • 使用Assert 或系统退出或异常来结束运行时。我认为这不会有太大用处,因为用户必须调用contains 以确保该元素在调用index() 之前在结构中
  • 返回 UINT_MAX 或 0
  • 将返回类型更改为long,这样我就可以返回-1。我不想切换数据类型。
  • 让用户传入**unsigned int,这样我就可以将它指向NULL(如果不存在)或一个真正的索引值。这对于阅读我的 API 的人来说并不友好。

我的最佳解决方案是:

// Change return type to pointer.
unsigned int* index(struct myDataStructure, void* value)
{
     static int val;

     if (value exists...)
     {
          val = correct index value
          return &val;
     }
     else
     {
          return NULL;
     }
}

但是我还是觉得这个解决方案很差。

【问题讨论】:

  • 您可以将指针传递给存储错误值的变量:unsigned int index(struct myDataStructure, void* value, int *status);
  • 您也可以考虑使用 POSIX ssize_t 类型而不是 unsignedssize_tsize_t 的宽度相同,但保证能够保持-1 的值。

标签: c function error-handling


【解决方案1】:

你是对的,返回一个指向静态的指针在很多方面都是不好的。它不是线程安全的,而且 - 更糟糕的是 - 它会邀请用户做类似的事情

int *aIndexLoc = index(data, &a);
int *bIndexLoc = index(data, &b);
if (aIndexLoc && bIndexLoc) 
  printf ("a's loc is %u; b's loc is %u\n", *aIndexLoc, *bIndexLoc);

当然会得到错误的答案。

首先...如果您希望您的库是面向未来的,那么不要为数组索引返回无符号数。返回size_t

那么... 有几种处理错误返回的习语。最常见的是返回一个int 或枚举错误代码作为函数值和带有指针arg 的实际返回值。按照惯例,0 表示“好的”,非零值是各种错误代码。此外,如果您的数据结构超过几个字节,请不要传递它的完整副本。传递指针

typedef int ERROR;
ERROR index(size_t *result, struct myDataStructure *myStruct, void *valueToFind);

然后是这样的:

size_t loc[1];
struct myDataStructure someData[1];
int aValue[1];

initialize(someData);
get(aValue);

ERROR error = index(loc, someData, aValue);
if (error) {
  fprintf(stderr, "Couldn't find the value. Error code: %d\n", error);
  return;
}

单元素数组是一种技巧,无论对象是在堆栈还是堆上分配,您都可以使用相同的方式编写代码。您可以将数组的名称视为指针。例如。 someData->fieldName*loc 工作得很好。

【讨论】:

  • 一个批评是我真的想避免污染全局命名空间。使用特殊的错误代码会使新用户对 API 感到困惑。
  • @Hatefiend 好吧,您的示例仅允许两种情况:非空和空。通过扩展, ERROR 有 2 个值:0 和一些非零值,比如 1。 typedef 只是为返回的内容命名的一种方式。它用作一个布尔值,表示是否存在错误。除非您选择这样做,否则这不会污染命名空间。我看不到用户在这一点上感到非常困惑。事实上,任何做过重要 C 编程的人都会觉得它非常熟悉。
【解决方案2】:

几种可能性:

  • 错误返回 (unsigned int) -1 使用

    unsigned int index(struct myDataStructure, void* value)
    
  • 错误返回 -1 使用

    ssize_t index(struct myDataStructure, void* value)
    

    ssize_t 是 POSIX)

  • 传入unsigned int 的地址以指向结果,并在错误时返回-1,在成功时返回0,使用

    int index(struct myDataStructure, void* value, unsigned int * result)
    

使用

  • 我觉得这里不合适的断言,因为它结束了您的程序。
  • 静态缓冲区是上个千年的编码风格。它会使您的库在多线程上下文中无法使用。

【讨论】:

  • 不幸的是,第一个选项可能会导致不小心的图书馆用户越界。第二个选项使用我不喜欢的外部库。它损害了便携性。第三个可能是最好的选择,但我无法决定是否应该将unsigned int 作为返回值或参数。我想也许是安全参数?将返回类型设为bool,以免做myArr[index(myArr, "F")]
  • @Hatefiend:“可能会导致不小心的图书馆用户越界”和“不要这样做”在任何情况下,您都应该期望您的用户阅读文档并对每个可能失败的函数调用进行适当的错误检查。
  • 我的意思是我希望 API 调用使用起来非常简单。
  • 如果index() 可能失败,它可能不会myArr[index(myArr, "F")] 这样。 没有办法
【解决方案3】:

您可以传递一个指向bool 的指针,相应地设置为truefalse

unsigned int index(struct myDataStructure, void *value, bool *ok);

【讨论】:

    猜你喜欢
    • 2013-03-27
    • 2019-05-26
    • 2017-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多