Reputation: 14236
When creating C functions that expecting input, I tend to do it like this:
function(unsigned char *bytes, unsigned int bytelen) { …
Now, I have a function in a project I'm coding where such a function expects a specific bytelen
of exactly 256 unsigned chars.
So, I tried the following which seems to work:
function(unsigned char bytes[256]) { …
Yet, testing that (using GCC), it doesn't fail at compile time when I pass 1024 unsigned chars to the function. Adding a printf
to that function, it even prints those 1024 unsigned chars without a problem.
That's not what I expected or intended, because — in the end — the function behaves as if I would've used function(unsigned char *bytes) { …
.
Of course, I can do the usual sanity checks to see if the expected length is passed and programaticly fail if the input is not exactly 256 chars. But isn't there a way to explicitly predefine that limit within the function's parameters? (Or am I doing it wrong? If, I would appreciate a heads-up on where I'm wrong.)
Upvotes: 1
Views: 178
Reputation: 106126
How are you calling the function? Imagine something like:
void f()
{
const char* p = get_a_line_from_file();
function(p);
}
Assuming get_a_line_from_file()
returns however much data is in the file at run-time, there's clearly no way the compiler can know at compile time whether the string is going to be 256 characters or not.
On the other hand, in...
char local_buffer[256];
populate(local_buffer, sizeof local_buffer);
function(local_buffer);
...it would be possible for the compiler to verify the local buffer size at compile time. If you want that, you need to do so before the function is called though, as in:
#define FUNCTION(X) do { STATIC_ASSERT(sizeof local_buffer == 256); function_impl(x); } while (false)
This assumes a supporting macro STATIC_ASSERT that generates an error if the enclosed expression is not statically determined to be true - you can undoubtedly find lots of good implementations online. The do
-while
idiom is commonly used by macros to ensure they work properly as single-line statements in if
-else
clauses.
An issue with this is that if the buffer is not local, you need to fall back on calling the implementation directly, as in:
void g(const char* p)
{
function_impl(p);
}
void h()
{
char local_buffer[256];
g(local_buffer);
}
All in all, it's unlikely to be worth the effort of compile-time verification.
If the content is ASCIIZ / NUL-delimited, then you probably want a run-time strlen()
verification anyway.
(In C++ you can verify this using template <size_t N> void function(const char (¶m)[N]) { ... }
)
Upvotes: 0
Reputation: 22821
Arrays decay into pointers in functions, pass the array size as well like this:
function(unsigned char bytes[], unsigned int bytelen)
An lvalue of type array-of-T which appears in an expression decays (with three exceptions) into a pointer to its first element; the type of the resultant pointer is pointer-to-T.
(The exceptions are when the array is the operand of a sizeof or & operator, or is a literal string initializer for a character array.)
Upvotes: 2
Reputation: 241791
In C, an array of N things is a contiguous chunk of storage with N things. It's not any more sophisticated than that, by design. So if s
points to the beginning of an array of 1024 characters then it also points to the beginning of an array of 1023 things, or 256 things, or 3 things. And s+1
, s+400
and s+768
(or &s[1]
, &s[400]
and &s[768]
, which are precisely equivalent) also point to the beginning of an array of 256 things.
In any event, it's pretty unlikely that the compiler will check this stuff for you, although it might.
If you want to discuss an object which has exactly 256 characters, not more and not less, wrap it into a struct:
struct TwoFiveSix {
char s[256];
};
If your function's prototype says it takes the address of a struct TwoFiveSix
, the compiler will definitely complain if you try to pass it something else. Like a string.
Upvotes: 1
Reputation: 2743
That's not what I expected or intended, because — in the end — the function behaves as if I would've used function(unsigned char *bytes) {...
Your remark is absolutely correct. Writing unsigned char bytes[1024]
is exactly the same in C as unsigned char *bytes
in function parameter lists. It works and behaves exactly the same way.
But isn't there a way to explicitly predefine that limit within the function's parameters?
Not in C. What you could do is define a struct having the fixed sized array in it:
typedef struct {
unsigned char buffer[1024];
} arraytype;
and then you can use arraytype *
as your function parameter type so your compiler will then make it sure the actual function invocation uses a properly typed arraytype *
pointer. Of course, you then cannot pass a bare unsigned char
array, you have to use arraytype
.
Upvotes: 3