【问题标题】:Is there a way to predefine the expected length of a C function's parameter?有没有办法预定义 C 函数参数的预期长度?
【发布时间】:2013-10-29 02:02:44
【问题描述】:

在创建需要输入的 C 函数时,我倾向于这样做:

function(unsigned char *bytes, unsigned int bytelen) { …

现在,我正在编写的项目中有一个函数,该函数需要一个特定的 bytelen,正好是 256 个无符号字符。

所以,我尝试了以下似乎可行的方法:

function(unsigned char bytes[256]) { …

然而,测试(使用 GCC),当我将 1024 个无符号字符传递给函数时,它不会在编译时失败。向该函数添加printf,它甚至可以毫无问题地打印那些 1024 个无符号字符。

这不是我的预期或意图,因为 - 最终 - 函数的行为就像我会使用 function(unsigned char *bytes) { …

当然,我可以进行通常的健全性检查,看看是否通过了预期的长度,如果输入不完全是 256 个字符,则以编程方式失败。但是没有办法在函数的参数中显式地预定义该限制吗? (或者我做错了吗?如果,我会很感激我在哪里做错了。)

【问题讨论】:

  • 可以传递具有unsigned char bytes[256] 字段的结构的地址。

标签: c parameters parameter-passing


【解决方案1】:

这不是我的预期或意图,因为 - 最后 - 函数的行为就像我会使用 function(unsigned char *bytes) {...

你的话完全正确。在 C 中编写 unsigned char bytes[1024] 与函数参数列表中的 unsigned char *bytes 完全相同。它的工作方式和行为方式完全相同。

但是没有办法在函数的参数中显式地预定义该限制吗?

不在 C 中。你可以做的是定义一个结构,其中包含固定大小的数组:

typedef struct {
  unsigned char buffer[1024];
} arraytype;

然后您可以使用arraytype * 作为您的函数参数类型,这样您的编译器就会确保实际的函数调用使用正确类型的arraytype * 指针。当然,你不能传递一个裸的unsigned char数组,你必须使用arraytype

【讨论】:

  • "然后您可以使用 arraytype * 作为您的函数参数类型" 如果您要这样做,您最好将参数设置为 unsigned char (*foo)[1024] 而不必费心定义新类型.
【解决方案2】:

数组衰减为函数中的指针,传递数组大小也是这样:

function(unsigned char bytes[], unsigned int bytelen)

出现在表达式中的数组 T 类型的左值衰减(与 三个例外)转化为指向其第一个元素的指针;的类型 结果指针是指向 T 的指针。

(例外情况是数组是 sizeof 或 & 运算符的操作数,或者是字符数组的文字字符串初始值设定项。)

【讨论】:

  • 这与 数组衰减为指针 语义无关,尽管它密切相关。更准确地说,仅在参数列表中,声明 T a[]T a[N]T *a 被定义为对于任何类型 T 和常量正整数 N 都是相同的,也就是说,它们都意思是T *a
  • OP: IOW,当编译器在函数参数列表中看到unsigned char bytes[256] 时(并且仅在那里),它会悄悄地将其视为unsigned char *bytes。这实际上是故意添加到 C 中的,以使其更“直观”:-/
【解决方案3】:

在 C 中,包含 N 个事物的数组是包含 N 个事物的连续存储块。按照设计,它没有比这更复杂的了。因此,如果s 指向一个包含 1024 个字符的数组的开头,那么它也指向一个包含 1023 个事物、或 256 个事物或 3 个事物的数组的开头。而s+1s+400s+768(或&s[1]&s[400]&s[768],它们完全等价)也指向包含256 个事物的数组的开头。

无论如何,编译器不太可能为您检查这些内容,尽管它可能会。

如果你想讨论一个正好有 256 个字符的对象,不多不少,把它包装成一个结构体:

struct TwoFiveSix {
  char s[256];
};

如果你的函数原型说它需要struct TwoFiveSix 的地址,如果你试图传递其他东西,编译器肯定会抱怨。像一个字符串。

【讨论】:

    【解决方案4】:

    你是如何调用函数的?想象一下:

    void f()
    {
        const char* p = get_a_line_from_file();
        function(p);
    }
    

    假设 get_a_line_from_file() 在运行时返回文件中的任何数据,编译器显然无法在编译时知道字符串是否为 256 个字符。

    另一方面,在...

    char local_buffer[256];
    populate(local_buffer, sizeof local_buffer);
    function(local_buffer);
    

    ...编译器可以在编译时验证本地缓冲区大小。如果你想这样做,你需要在调用函数之前这样做,如:

    #define FUNCTION(X) do { STATIC_ASSERT(sizeof local_buffer == 256); function_impl(x); } while (false)
    

    这假设一个支持宏 STATIC_ASSERT 如果封闭的表达式不是静态确定为真,则会生成错误 - 毫无疑问,您可以在网上找到很多好的实现。 do-while 惯用语通常由宏使用,以确保它们在if-else 子句中作为单行语句正常工作。

    这样的问题是,如果缓冲区不是本地的,则需要直接调用实现,如下所示:

    void g(const char* p)
    {
        function_impl(p);
    }
    
    void h()
    {
        char local_buffer[256];
        g(local_buffer);
    }
    

    总而言之,编译时验证不太值得。

    如果内容是 ASCIIZ / NUL 分隔的,那么您可能仍然需要运行时 strlen() 验证。

    (在 C++ 中,您可以使用 template <size_t N> void function(const char (&param)[N]) { ... } 验证这一点)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-15
      • 1970-01-01
      • 2011-03-30
      • 2012-10-21
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      相关资源
      最近更新 更多