limits.h and int len

  • Thread starter Massimiliano Alberti
  • Start date
M

Massimiliano Alberti

Now... in limits.h there are many costants with the max and min integer,
long, char... Is there a costant with the max length in characters of
int/long/char/...
(for example.. INT_MAX = 2147483647. If we consider the sign, it's 11
characters (plus the '\0'))

--- bye
 
M

Martin Dickopp

Massimiliano Alberti said:
Now... in limits.h there are many costants with the max and min integer,
long, char... Is there a costant with the max length in characters of
int/long/char/...
(for example.. INT_MAX = 2147483647. If we consider the sign, it's 11
characters (plus the '\0'))

No, but if you want to define a buffer which is guaranteed to be large
enough to hold a text representation of any integer, you can use:

char buffer [sizeof (int) * CHAR_BIT * 8 / 25 + 3];

For some values of `sizeof (int)' and `CHAR_BIT', is may be slighly[*]
too large, but it is never too small.

`sizeof (int) * CHAR_BIT' is an upper bound on the number of value bits
in an `int'; `8 / 25' (i.e. 0.32 (mathematically; the C expression
`8 / 25' has of course integer type)) is an approximation for the
base 10 logarithm of 2 (0.301029995664...).

Of the 3 added, 1 accounts for rounding due to integer arithmetic,
1 accounts for the possible sign character, and 1 accounts for the
terminating '\0' character.


[*] On the DeathStation9000, where I presume `int' has 31 value bits
and 0x6d616e792c206d616e79 padding bits, it may be more than
just /slightly/ too large... ;)
 
M

Massimiliano Alberti

char buffer [sizeof (int) * CHAR_BIT * 8 / 25 + 3];
Wow... It this formula seems to be from a Science Fiction book :) Thanks a
lot!

--- bye!
 
D

Dan Pop

In said:
char buffer [sizeof (int) * CHAR_BIT * 8 / 25 + 3];
Wow... It this formula seems to be from a Science Fiction book :) Thanks a
lot!

If you want one that is easier to remember, you can use:

sizeof(int) * CHAR_BIT / 3 + 3

it actually evaluates the number of octal digits (hence the division by 3)
needed to represent the value and also takes into account the sign
character and the null character that is supposed to terminate the string.

Dan
 
C

caroundw5h

Massimiliano Alberti said:
Now... in limits.h there are many costants with the max and min integer,
long, char... Is there a costant with the max length in characters of
int/long/char/...
(for example.. INT_MAX = 2147483647. If we consider the sign, it's 11
}characters (plus the '\0'))

--- bye

Why are You over complicating each data type for?!!??
INT_MAX is the max unsigned numbers. What does the character length
have to do with it? its not a string. To see it your way, you'd have
to use the strlen() funciton:

code:

#include <stdio.h>
int main(){
char INT_MAX[40] = "2147483647";
printf("%d", strlen(INT_MAX));
getchar();
return 0;
}

endcode: Why in god's name would you want to return the string length
of the int types? And if you did return the strlen(INT_MAX), it would
be 10, not '11'. The null character is NOT counted as part of the
string. It is simply there to terminate the string constant. so your
quote:
"If we consider the sign, it's 11
}characters (plus the '\0'))
"

is wrong. because it is not counted.
 
D

Darrell Grainger

Now... in limits.h there are many costants with the max and min integer,
long, char... Is there a costant with the max length in characters of
int/long/char/...
(for example.. INT_MAX = 2147483647. If we consider the sign, it's 11
characters (plus the '\0'))

Things like strtol lets you do everything from base 2 to base 36. That
would be 35 macros for each data type. We could limit it to say 2, 8, 10
and 16. Really, it is not that hard to come up with some formula to figure
out worse case scenario.

Let's take base 10. We can use sizeof(int)*CHAR_BIT to figure out how many
bits. Now we just need to figure out how many bits to represent a single
digit. If you look at octal (base 8) then each digit requires 3 bits. If
you look at hexidecimal (base 16) then each digit requires 4 bits. So
decimal (base 10) will require between 3 and 4 bits per digit. If the size
of sizeof(int)*CHAR_BIT is say 32 we know that in octal it will be 11
digits (32 / 3 and round up). In hexidecimal it will 8 digits (32 / 4).

To play it safe, I'd allocate 11 digits for a decimal number converted to
a string (plus 1 more for the null character).
 
N

nrk

Massimiliano said:
Now... in limits.h there are many costants with the max and min integer,
long, char... Is there a costant with the max length in characters of
int/long/char/...
(for example.. INT_MAX = 2147483647. If we consider the sign, it's 11
characters (plus the '\0'))

--- bye

Others have shown you a compile-time way of determining what you want. You
could also use the C99 compliant snprintf to determine the length:

int n = snprintf(NULL, 0, "%ld", LONG_MIN);
assert(n > 0);
++n; /* Now, n has the length of our string */

-nrk.
 
R

Richard Bos

Why are You over complicating each data type for?!!??

That sentence does not seem to be in English.
endcode: Why in god's name would you want to return the string length
of the int types?

For starters, because you might want to sprintf() some integers and need
to know how large the destination memory needs to be.

Richard
 
D

Dan Pop

In said:
Why are You over complicating each data type for?!!??
INT_MAX is the max unsigned numbers.

Make it signed int numbers.
What does the character length
have to do with it? its not a string. To see it your way, you'd have
to use the strlen() funciton:

code:

#include <stdio.h>
int main(){
char INT_MAX[40] = "2147483647";

What if INT_MAX happens to have another value? The whole purpose of the
exercise is to come up with a *portable* solution.
printf("%d", strlen(INT_MAX));

Have you declared strlen() anywhere? I must have missed it...
getchar();
return 0;
}

endcode: Why in god's name would you want to return the string length
of the int types?

Imagine that you want to use sprintf and need to evaluate the size of the
buffer containing the output string. In a portable way, of course.
And using a constant expression, if possible.

Dan
 
D

Dan Pop

In said:
Others have shown you a compile-time way of determining what you want. You
could also use the C99 compliant snprintf to determine the length:

int n = snprintf(NULL, 0, "%ld", LONG_MIN);
assert(n > 0);
++n; /* Now, n has the length of our string */

But beware! Many of the snprintf implementations floating around are not
C99 compliant and they behave differently when invoked as shown above!

From the UNIX98 specification:

If the value of n is zero on a call to snprintf(), an unspecified
value less than 1 is returned.

Pick your standard ;-)

Dan
 
M

Massimiliano Alberti

As someone has pointed out, I wanted a compiler time solution... And no,
using (?)printf is not a solution... Instead of using it, at least for
integers, it's probably better to write something like this:

size_t iLen;


// to change it to uint:
// unsigned int iMax = UINT_MAX
int iMax = INT_MAX;

// Now we overflow iMax. If it returns to 0 then it's unsigned, otherwhise
it's signed
if ((iMax + 1) == 0)
iLen = 1; // we are including the \0
else
iLen = 2; // we are including the sign and the \0

// Now we count the digits
for (; iMax > 0; iMax /= 10) // we want to write it in base10
iLen++;

--- bye
 
E

Eric Sosman

Massimiliano said:
As someone has pointed out, I wanted a compiler time solution...

Compile-time solutions have been posted, all based on
various approximations to log10(2). For extraordinarily
wide integers they may allocate a couple more bytes than is
strictly necessary -- but if you've got a machine with
256-bit integers, I suspect a few bytes' worth of space is
not of great concern ...
And no,
using (?)printf is not a solution... Instead of using it, at least for
integers, it's probably better to write something like this:

I dispute the "better." First, it is not a compile-time
solution and so does not meet your requirement. Second, it
invokes undefined behavior. Third, the undefined behavior is
entirely unnecessary.
size_t iLen;

// to change it to uint:
// unsigned int iMax = UINT_MAX
int iMax = INT_MAX;

// Now we overflow iMax. If it returns to 0 then it's unsigned, otherwhise
it's signed
if ((iMax + 1) == 0)

In the signed case, this is where the undefined behavior
occurs. The mathematically correct value of `iMax + 1' is
outside the range of the `int' type, so anything at all can
happen (section 6.5 paragraph 5). You cannot predict what
value might be delivered; you cannot even be sure *any* value
will be delivered (e.g., the program might crash). It is
even possible that the delivered value will be zero, leading
the code to make the wrong conclusion.

The galling thing is that this is unnecessary. All you're
doing is trying to decide which of the two declarations of `iMax'
you actually wrote at the beginning of the code -- but since you
wrote the declaration, you already know the answer! Why try to
re-compute it?
 
N

nrk

Dan said:
In <[email protected]> nrk


But beware! Many of the snprintf implementations floating around are not
C99 compliant and they behave differently when invoked as shown above!

From the UNIX98 specification:

If the value of n is zero on a call to snprintf(), an unspecified
value less than 1 is returned.

Pick your standard ;-)

Yes, I was aware of the brain-damaged UNIX98 spec. for snprintf which is why
I was careful to point out "C99 compliant snprintf" (and the assert just in
case it was compiled to use the brain-damaged version of snprintf, although
I am not sure if passing NULL is advisable).

Sometimes, standards make you wonder "what the heck were these people
smoking when they did that?". Like the fact that gets is still part of the
standard library. Seriously, what the heck were they smoking when they
made that decision? I can't justify it even in terms of backward
compatibility.

-nrk.
 
D

Dan Pop

In said:
Yes, I was aware of the brain-damaged UNIX98 spec. for snprintf which is why

The UNIX98 snprintf is, however, prevalent, because the UNIX98 spec. is
far better supported than C99...
I was careful to point out "C99 compliant snprintf" (and the assert just in
case it was compiled to use the brain-damaged version of snprintf, although
I am not sure if passing NULL is advisable).

It is, most likely, undefined behaviour, as the UNIX98 version expects
a character buffer and nothing else.
Sometimes, standards make you wonder "what the heck were these people
smoking when they did that?".

I suppose the UNIX98 snprintf merely codified existing practice, while
C99 enhanced the functionality of the function.
Like the fact that gets is still part of the
standard library. Seriously, what the heck were they smoking when they
made that decision? I can't justify it even in terms of backward
compatibility.

gets() is no different from system(): neither can be used (safely and
meaningfully) in a portable program, but both have their uses in
non-portable code. Many implementations (e.g. Unix and MSDOS) use a
terminal buffer of a fixed size. If the array receiving gets' output
is larger than that size, the code is safe, as long as it checks that
stdin is connected to a terminal (or enforces itself this connection,
via a freopen call).

Dan
 
M

Michael Wojcik

The UNIX98 snprintf is, however, prevalent, because the UNIX98 spec. is
far better supported than C99...

True. Fortunately SUSv3 / IEEE 1003.1-2003 / Open Group Base
Specifications Issue 6 has updated the specification for snprintf to
match C99[1], so UNIX-branded OSes should be catching up.

In my recent experience, current releases of AIX, Solaris, and Linux
have C99-compliant snprintf in the vendor-supplied (for the first two)
or commonly-used (for the last) library. Current releases of HP-UX
and Windows do not. (Disclaimer: that's based on some testing I did
last year, and may not reflect the most recently available libraries
for the last two platforms.)

In any event, it's probably wise to check the behavior of snprintf
in a given implementation before you use it. And, of course, for
this particular problem the snprintf-based solution doesn't seem to
offer any advantage over various other proposals. (In a similar
vein, the stringize operator could probably be used to achieve
what the OP wants, but again that's less elegant than some of the
approaches already proposed.)


1. http://www.opengroup.org/onlinepubs/007904975/functions/snprintf.html
 
B

Ben Pfaff

In any event, it's probably wise to check the behavior of snprintf
in a given implementation before you use it. And, of course, for
this particular problem the snprintf-based solution doesn't seem to
offer any advantage over various other proposals. (In a similar
vein, the stringize operator could probably be used to achieve
what the OP wants, but again that's less elegant than some of the
approaches already proposed.)

The best thing to do may be to write a wrapper for snprintf()
that abstracts away the implementation's actual form of return
value. It is not difficult to support both the `return -1 on
error' and `return required size on error' styles, but you
wouldn't want to do it in inline code. Using a wrapper function
makes your life easier.

For example (I believe this code is correct, please point out any
problems):

/* Formats FMT into a dynamically allocated string as with printf().
Returns the string. */
char *
blp_asprintf (const char *fmt, ...)
{
int size = 64;

for (;;)
{
va_list args;
char *buf;
int n;

buf = xmalloc (size); /* Wrapper for malloc(). */

va_start (args, fmt);
n = vsnprintf (buf, size, fmt, args);
va_end (args);

if (n < 0)
size *= 2;
else if (n < size)
return buf;
else
size = n + 1;

free (buf);
}
}
 
M

Michael Wojcik

The best thing to do may be to write a wrapper for snprintf()
that abstracts away the implementation's actual form of return
value. It is not difficult to support both the `return -1 on
error' and `return required size on error' styles, but you
wouldn't want to do it in inline code.

Except when the implementation overloads the -1 return value to mean
both "buffer is too small" and "a formatting error occurred", which
is what the MSVC6 implementation does, for example. (MSVC6 does not
actually provide snprintf, but they provide a function _snprintf
which is very similar.)
Using a wrapper function makes your life easier.

Often it does, yes.
For example (I believe this code is correct, please point out any
problems):

/* Formats FMT into a dynamically allocated string as with printf().
Returns the string. */
char *
blp_asprintf (const char *fmt, ...)
{
int size = 64;

for (;;)
{
va_list args;
char *buf;
int n;

buf = xmalloc (size); /* Wrapper for malloc(). */

I assume this wrapper handles malloc failure in some manner that
doesn't involve returning to this function, correct?
va_start (args, fmt);
n = vsnprintf (buf, size, fmt, args);
va_end (args);

if (n < 0)
size *= 2;

Here's the problem: with an implementation like Microsoft's, a
formatting error will cause this function to loop unless a side
effect of xmalloc (eg calling exit or longjmp) causes it not to
return.

Also, I'd want to check for overflow on size.
else if (n < size)
return buf;
else
size = n + 1;

free (buf);

Personally, I'd realloc the buffer rather than freeing the old one
and malloc'ing a new one, though there isn't necessarily any
advantage to doing it that way.
 
B

Ben Pfaff

Except when the implementation overloads the -1 return value to mean
both "buffer is too small" and "a formatting error occurred", which
is what the MSVC6 implementation does, for example. (MSVC6 does not
actually provide snprintf, but they provide a function _snprintf
which is very similar.)

Argh. How frustrating.

But the only formatting errors that can occur, as far as I can
tell from the standard, are "encoding errors", which cannot occur
in the C locale as far as I know. So this is unlikely to be a
problem in practice for many programs.
Personally, I'd realloc the buffer rather than freeing the old one
and malloc'ing a new one, though there isn't necessarily any
advantage to doing it that way.

Calling realloc() forces the implementation to copy data, which
is wasteful because the data isn't going to be used. Calling
free() avoids the copy.
 
M

Michael Wojcik

Argh. How frustrating.

But the only formatting errors that can occur, as far as I can
tell from the standard, are "encoding errors", which cannot occur
in the C locale as far as I know. So this is unlikely to be a
problem in practice for many programs.

That appears to be true for N869. Of course, some programs must
deal with multibyte characters and so are potentially susceptible
to negative return values from snprintf.

SUSv3 et al. also allow a negative return if the number of bytes
needed to hold the fully-formatted string is larger than INT_MAX,
or if the "n" parameter (the size of the buffer) is greater than
INT_MAX. N869 doesn't explicitly cover those cases. It also
allows a negative return if "there are insufficient arguments"
(assuming, of course, that the implementation can detect this!).

And there are cases for the printf family that produce UB (such
as a precision specifier for a conversion that doesn't support
such a specifier), and a polite implementation might return a
negative value for them.

But in general, yes, it's unlikely to be an issue for many
programs in practice. I just find it worrying that these pre-C99
snprintf implementations would fail to distinguish between two
very different error conditions - particularly since one is the
sort of error which the program might well be able to (and try
to) correct, while the other is not.
Calling realloc() forces the implementation to copy data, which
is wasteful because the data isn't going to be used. Calling
free() avoids the copy.

Good point (though there's no guarantee that the implementation
has to copy the data - it might be able to satisfy the realloc
request in place - but that's a good rule of thumb).

--
Michael Wojcik (e-mail address removed)

This is a "rubbering action game," a 2D platformer where you control a
girl equipped with an elastic rope with a fishing hook at the end.
-- review of _Umihara Kawase Shun_ for the Sony Playstation
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,015
Latest member
AmbrosePal

Latest Threads

Top