If I have an array in which I want to compose a string and I want to make sure I don't have overflows when I write strings, I can do it like this:
void componer( char compuesto[100], const char* cadena1, const char* cadena2 )
{
sprintf( compuesto, "%.40s--%.40s", cadena1, cadena2 );
}
That way I make sure I never overflow the array compuesto
regardless of the length of the strings cadena1
and cadena2
because it %.40s
means writing a string but at most 40 characters.
But for integers width and precision in formatting printf
and similar functions mean the minimum number of characters to print, but more can be printed.
The problem is that this function:
void componeInt( char compuesto[14], int a, int b)
{
sprintf( compuesto, "%d/%d", a, b );
}
it will work fine on architectures where int is 16 bits; because at most an int has 5 decimal places, 6 with the negative sign. And with 14 characters there is always room to store that string.
But on 32-bit architectures I'm going to get an array overflow.
Is there a format parameter to sprintf to restrict the maximum number of characters to print in an integer? If there isn't, how to do it? I don't care if the most significant figures or the least significant figures are written from the partially written number.
The first is the first.
In modern C code it should not be ok to use the classic types
int
,short
, etc, since their range of values depends on the machine we are working on. Instead it would be more convenient to use the types given by the librarystdint.h
. The types defined in this library have the advantage that their range of values is fixed for any machine, which avoids unpleasant surprises when designing applications to be compiled for multiple devices.That said, a first approximation could be made using types with fixed range:
Or leave the signature with the classic types and cast within the function, although in this case we may end up truncating the value and losing digits:
A third option is to not use
sprintf
, a function that is by the way, classified as insecure because it is not capable of controlling buffer overflow. Instead it is safer to usesnprintf
. This function receives, in addition to a buffer to store the output, the maximum storage size. This prevents writing out of the buffer at the cost of sacrificing the end of the string:The bad thing about this last solution is that we can lose the last one
int
completely. If it is a requirement to show both values, even partially, the best solution is to combine the solutions we have seen into one. Well, really one or two, depending on the requirements:Truncating the values inside the function
Limiting the size of variables directly in the signature
The nice thing about both of these solutions is that they make use of a safe function
snprintf
instead of an unsafe one,sprintf
. Perhaps between these two possible solutions, the most elegant is the second, since whoever is going to use the function is clear directly in the signature that the function is only going to work with 16-bit values.EDIT:
If the types defined in are used
stdin.h
and we don't want to be surprised when printing these variables, we have to use the macros declared ininttypes.h
. These macros ensure that the value is printed according to its specified range.It is true that these macros can complicate the reading of the code, but it is only a little and one quickly gets used to the new syntax:
You can try using int snprintf ( char * s, size_t n, const char * format, ... ); In a part of the documentation it says something like this:
n Parameter:
The maximum number of
bytes
that is used in the buffer.The generated string has a length of like
maximo n-1
, leaving room for the additional terminating null character. size_t is an unsigned integer type.Notes: Snprint is part of the C99 standard.