iC and iC++ said:
I found this code in a very old book about pointers and I started
playing with it and I have a question.
#include <stdio.h>
#include <string.h>
main()
{
char x[5];
strcpy(x, "COMPILER");
printf("%s, %u, %d, \n", x, &x, sizeof(x));
How deos strcpy assign all the characters in the code if x is only
defines as a 5 byte array? In other words, where are the extra bytes
stored and how is memory for them allocated. I looked at it while
debugging and there were no clues there.
You won't find anything because this use of strcpy() just in-
vokes undefined behaviour. As a programmer you have to ensure
that the buffer you pass to strcpy() as the target is large
enough to hold the string you want to copy to it. In this pro-
gram this isn't the case and the consequences could be anything
- from looking as if everything works fine, a crash of your pro-
gram to your hard disk getting reformatted. So this example
program simply is buggy and should be marked with "Don't do
this at home. kids".
strcpy() simply doesn't have any means of checking if the
source string will fit into the target buffer - all it gets
is two pointers with no information about the sizes of the
buffers. So it has to blindly trust you that you have got
it right and copies everything, including the trailing '\0'
char, from the source string to the destination. If the desti-
nation buffer is too short printf() will blissfully ignorant
of what may be the consequences overwrite the memory following
the end of the destination buffer. If there's something im-
portant to your program (or even the operating system) you
just overwrote it and there's no way to get it back. strcpy()
also can only deduce from the trailing '\0' in the source
buffer when to stop, so if you pass it a char array that
doesn't contain a '\0' (so it's not a string) then you're also
screwed since it won't stop at the end of the source buffer
but happily continues until it finds some '\0' in the memory
following the end of the source buffer.
Perhaps that gets even more clear if you consider a possible
implemention of strcpy():
char *strcpy( char *dest, const char *src )
{
char *ret = dest;
while ( *dest++ = *src++ );
return ret;
}
This is (I think) a perfectly legal implementation of strcpy().
So you will be in trouble when
a) 'dest' is a NULL pointer or does not point to memory you own
b) 'src' is a NULL pointer or does not point to memory you own
c) 'src' isn't a pointer to a string, i.e. there's no '\0'
within the elements of the char array pointed to by 'src'
d) the char array 'dest' is pointing to isn't large enough
to hold all the chars from 'src' up to and including
the terminating '\0; char
So YOU have to take care that this can't happen. You pro-
bably will be careless a few times and will have to spend
a god deal of time figuring out where things went wrong.
In the long run it will make you either a much more careful
programmer or you will switch to a language where this can't
happen. It's like with cooking - things are a lot more fun
if you have really sharp knifes but you have to be on your
toes or you will shed some of your blood (or even lose parts
of your fingers;-)
By the way, also this line is problematic:
printf("%s, %u, %d, \n", x, &x, sizeof(x));
since the type of 'sizeof(x) isn't necessarily an int
(as you are promising printf() by using the '%d' format
specifier). The result of 'sizeof' is an unsigned integer
type of 'size_t' (note the 'integer' in contrast to 'int'),
so it would be prudent to use '%lu' as the format specifier
and cast the result of 'sizeof' to 'unsigned long' (or use
whatever is the largest unsigned integer type on your machine
unless you have a C99 compliant compiler where there's the
special format specifier of '%z' that tells printf() that it
has to expect a type of 'size_t' as the corresponding argument).
Regards, Jens