Why do I first have to strcpy() before strcat()?

strcat will look for the null-terminator, interpret that as the end of the string, and append the new text there, overwriting the null-terminator in the process, and writing a new null-terminator at the end of the concatenation.

char stuff[100];  // 'stuff' is uninitialized

Where is the null terminator? stuff is uninitialized, so it might start with NUL, or it might not have NUL anywhere within it.

In C++, you can do this:

char stuff[100] = {};  // 'stuff' is initialized to all zeroes

Now you can do strcat, because the first character of 'stuff' is the null-terminator, so it will append to the right place.

In C, you still need to initialize 'stuff', which can be done a couple of ways:

char stuff[100]; // not initialized
stuff[0] = '\0'; // first character is now the null terminator,
                 // so 'stuff' is effectively ""
strcpy(stuff, "hi ");  // this initializes 'stuff' if it's not already.

In the first case, stuff contains garbage. strcat requires both the destination and the source to contain proper null-terminated strings.

strcat(stuff, "hi ");

will scan stuff for a terminating '\0' character, where it will start copying "hi ". If it doesn't find it, it will run off the end of the array, and arbitrarily bad things can happen (i.e., the behavior is undefined).

One way to avoid the problem is like this:

char stuff[100];
stuff[0] = '\0';      /* ensures stuff contains a valid string */
strcat(stuff, "hi ");
strcat(stuff, "there");

Or you can initialize stuff to an empty string:

char stuff[100] = "";

which will fill all 100 bytes of stuff with zeros (the increased clarity is probably worth any minor performance issue).