问题
Currently, I have the following block of code to make safe string copying (it works):
#define STRCPY(dst, src) do { assert(((void*)(dst)) == ((void*) & (dst))); \
strlcpy(dst, src, sizeof(dst)); } while (0)
So it accepts the construction like:
const char *src = "hello";
char dest[5];
STRCPY(dest, src); //hell
And denies the following:
void (char *dst) {
STRCPY(dst, "heaven"); //unknown size of dst
}
The problem is that the block of code creates an assertion. Is there a way to perform this check on compilation time?
So I want to have the error on compilation (like creating an array with negative size) instead of crashing code if it possible.
回答1:
For compile-time assertion on whether or not dst
is an array,
I would use @Lundin solution (or _Static_assert
) in case C11 is available.
Else, I would use the following:
#define BUILD_BUG_ON_NON_ARRAY_TYPE(e) (sizeof(struct { int:-!!(((void*)(e)) != ((void*) & (e))); }))
Which basically takes your compile-time evaluated expression ((void*)(dst)) == ((void*) & (dst))
but instead of using it in run time assert
just use it in a compile-time assertion manner.
So, overall I would change your macro into:
#define STRCPY(dst, src) do { BUILD_BUG_ON_NON_ARRAY_TYPE(dst); \
strlcpy(dst, src, sizeof(dst)); } while (0)
回答2:
If standard C is available, then you can do this:
#define STRCPY(dst, src) \
_Generic(&(dst), \
char(*)[sizeof(dst)]: strlcpy(dst,src,sizeof(dst)) )
Explanation:
You can't use a _Generic
expression on an array type, because it is not one of the special cases that is exempt from the "array decay" rule (C17 6.3.2.1 §3). So by simply using _Generic((dst), ...
in my example, dst
would end up as a char*
when the expression is evaluated, and then we would lose the information of its original type.
But if we take the address of the array using &
, we do utilize one of those special cases and the array decay doesn't happen. Instead we end up with an array pointer, meaning that _Generic
will have to check for an array pointer of the expected type and size: char(*)[sizeof(dst)]
.
As a side-note/safety concern, I never use do-while(0)
macros and discourage them, but that's another story.
回答3:
I've found that my post was closed as duplicated for a while and followed the link mentioned. The solution is only for GCC, but it's fine for me, because I have night builds on GCC too. So the pre-draft of the code is:
#if defined(__GNUC__)
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
#define STRCPY(dst,src) do{__must_be_array(dst);strlcpy(dst, src, sizeof(dst));}while(0)
#else
#define STRCPY(dst,src) do{strlcpy(dst, src, sizeof(dst));}while(0)
#endif
回答4:
If you want to check it the only way I can think of is:
assert((sizeof(dst)) != (sizeof(void*)));
but it only work if the size of the array is different than the size of the pointer on OPs system
来源:https://stackoverflow.com/questions/53831489/compile-time-assertion-to-determine-if-pointer-is-an-array