pete:
Whether or not you can set an unsigned char to 65000
is implementation defined,
so there's nothing wrong
with an implementation defined way of doing it.
The reason I mentioned concrete figures like 65535 instead of
UCHAR_MAX is that I think people find it easier to understand and
grasp.
The point wasn't whether we could assign 65000 to an int, but rather
whether we could assign (UCHAR_MAX - some_small_numb er) to an int and
have the same results on every implementation conceivable.
For clarity, I'll rewrite my original post taking out the concrete
numbers. Remember again, that the code is being written in the context
of it being FULLY portable (e.g. 97-Bit char's and sign-magnitude):
Let's say we have an array of bytes and we want to set every byte to
(UCHAR_MAX - 4). We CANNOT use:
memset(data, UCHAR_MAX - 4, sizeof data);
because the conversion from unsigned integer types to signed integer
types "is implementation-defined or an implementation-defined signal
is raised" if the number is out of range. (So in the context of fully
portable programming, the resultant int could have pretty much any
value because UCHAR_MAX might be bigger than INT_MAX).
Therefore we need to supply memset with an int value, which, went
converted to unsigned char, will yield the value we want.
The rules for converting from signed to unsigned are as follows:
| If the new type is unsigned, the value is converted
| by repeatedly adding or subtracting one more than
| the maximum value that can be represented in the
| new type until the value is in the range of the new type.
The addition method is easier to understand so we'll go with that
one.
If we start off with a negative number like -1, then here's what will
happen:
char unsigned c = -1;
is equal to:
infinite_range_ int x = -1; /* Let's pretend we have a signed
int type that can hold any number */
while (0 x || UCHAR_MAX < x) x += UCHAR_MAX +
(infinite_range _int)1;
char unsigned c = x;
So here's a few samples of what will happen on different systems:
while (0 x || 255 < x) x += 256;
while (0 x || 65535 < x) x += 65536;
while (0 x || 4294967295 < x) x += 4294967296;
while (0 x || 184467440737095 51615 < x) x +=
184467440737095 51616;
If x = -1, then it only takes one iteration of the loop to
yield UCHAR_MAX on any implementation.
Therefore, if we want UCHAR_MAX-1, then we'd use (int)-2.
For UCHAR_MAX-2, we'd use (int)-3.
The entire set of data looks something like:
int char unsigned
-1 UCHAR_MAX
-2 UCHAR_MAX-1
-3 UCHAR_MAX-2
-4 UCHAR_MAX-3
-5 UCHAR_MAX-4
-6 UCHAR_MAX-5
-7 UCHAR_MAX-6
-8 UCHAR_MAX-7
-9 UCHAR_MAX-8
-10 UCHAR_MAX-9
-11 UCHAR_MAX-10
-12 UCHAR_MAX-11
....
....
Now I've just realised a problem. Imagine a system where unsigned char
has the range 0 through 65535 and where int has -32767 through 32767.
The former has 65536 possible combinations while the latter only has
65535 combinations. We might have to resort to a loop if working with
something other than two's complement, but I'm not sure yet.
Anyway here's the code I have at the moment, I robbed some of it from
old posts of yours pete:
#define SIGNMAG 0
#define ONES 1
#define TWOS 2
#if -1 & 3 == 1
#define NUM_SYS SIGNMAG
#elif -1 & 3 == 2
#define NUM_SYS ONES
#else
#define NUM_SYS TWOS
#endif
#if NUM_SYS != TWOS /* ----------- */
#include <stddef.h>
static void *uc_memset(void *const pv,char unsigned const val,size_t
const len)
{
char *p = pv;
char const *const pover = p + len;
while (pover != p) *p++ = val;
return pv;
}
#define UC_MEMSET(p,uc, len) (uc_memset(p,uc ,len))
#else /* ------------ */
#include <string.h>
#define UC_AS_INT(x) UC_AS_INT_Inter nal( (char unsigned)(x) )
#define UC_AS_INT_Inter nal(x) ( x INT_MAX \
? -(int)(UCHAR_MAX - x) - 1 \
: (int)x )
#define UC_MEMSET(p,uc, len) (memset((p),UC_ AS_INT((uc)),(l en)))
#endif /* ----------- */
#include <limits.h>
int main(void)
{
char unsigned data[24];
UC_MEMSET(data, UCHAR_MAX, sizeof data);
return 0;
}
Feel free to make alterations if you see a better way of doing it!
Martin