在创建期望输入的C函数时,我倾向于这样做:
function(unsigned char *bytes, unsigned int bytelen) { …
现在,我在一个正在编码的项目中有一个函数,其中这样的函数需要特定的bytelen
正好256个无符号字符。
所以,我尝试了以下似乎有效的方法:
function(unsigned char bytes[256]) { …
然而,测试(使用GCC),当我将1024个无符号字符传递给函数时,它在编译时不会失败。在该函数中添加printf
,它甚至可以打印出那些1024个无符号字符而没有问题。
这不是我的预期或意图,因为 - 最后 - 该功能的行为就好像我会使用function(unsigned char *bytes) { …
。
当然,我可以进行通常的健全性检查,看看预期的长度是否通过,如果输入不是256个字符,则编程失败。但是,有没有办法在函数的参数中明确预定义该限制? (或者我做错了吗?如果,我会很高兴看到我错了。)
答案 0 :(得分:3)
这不是我的预期或意图,因为 - 最后 - 函数的行为就好像我会使用函数(unsigned char * bytes){...
你的评论绝对正确。在函数参数列表中,unsigned char bytes[1024]
与C unsigned char *bytes
中的完全相同。它的工作方式和行为完全相同。
但是,有没有办法在函数的参数中明确预定义该限制?
不在C.您可以做的是定义一个具有固定大小数组的结构:
typedef struct {
unsigned char buffer[1024];
} arraytype;
然后您可以使用arraytype *
作为函数参数类型,这样您的编译器就会确保实际的函数调用使用正确类型的arraytype *
指针。当然,您无法传递裸unsigned char
数组,您必须使用arraytype
。
答案 1 :(得分:2)
数组衰减成函数中的指针,传递数组大小如下:
function(unsigned char bytes[], unsigned int bytelen)
表达式中出现的类型为array-of-T的左值衰减(带有 三个例外)成为指向其第一个元素的指针;的类型 结果指针是指向T的指针。
(例外情况是数组是sizeof或&运算符的操作数,或者是字符数组的文字字符串初始值设定项。)
答案 2 :(得分:1)
在C中,N个事物的数组是N个事物的连续存储块。根据设计,它并不比那更复杂。因此,如果s
指向1024个字符数组的开头,那么它也指向1023个数组或256个事物或3个事物的数组的开头。而s+1
,s+400
和s+768
(或&s[1]
,&s[400]
和&s[768]
等等也是指向的开头256件事的数组。
无论如何,编译器不太可能为你检查这些东西,尽管它可能会。
如果你想讨论一个只有256个字符的对象,而不是更多而不是更少,请将它包装成一个结构:
struct TwoFiveSix {
char s[256];
};
如果你的函数的原型说它需要struct TwoFiveSix
的地址,那么如果你尝试将其他东西传递给它,编译器肯定会抱怨。像一个字符串。
答案 3 :(得分:0)
你是怎么称呼这个功能的?想象一下:
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 (¶m)[N]) { ... }
)