4 回答
这不是我的预期或意图,因为 - 最后 - 该函数的行为就像我会使用 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
.
数组衰减为函数中的指针,传递数组大小,如下所示:
function(unsigned char bytes[], unsigned int bytelen)
出现在表达式中的 T 数组类型的左值衰减(除了三个例外)成指向其第一个元素的指针;结果指针的类型是指向 T 的指针。
(例外情况是数组是 sizeof 或 & 运算符的操作数,或者是字符数组的文字字符串初始值设定项。)
在 C 中,包含 N 个事物的数组是包含 N 个事物的连续存储块。按照设计,它没有比这更复杂的了。因此,如果s
指向 1024 个字符的数组的开头,那么它也指向 1023 个、256 个或 3 个对象的数组的开头。And s+1
, s+400
and s+768
(或&s[1]
, &s[400]
and &s[768]
,它们完全等价)也指向一个包含 256 个事物的数组的开头。
无论如何,编译器不太可能为您检查这些内容,尽管它可能会。
如果你想讨论一个正好有 256 个字符的对象,不多不少,把它包装成一个结构:
struct TwoFiveSix {
char s[256];
};
如果您的函数原型说它需要 a 的地址,struct TwoFiveSix
那么如果您尝试传递其他东西,编译器肯定会抱怨。像一根弦。
你是如何调用函数的?想象一下:
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]) { ... }
)