The seg fault is because you attempt to write to read only memory. The first action of the strcat is to copy of 't' from the first entry of two into the null at the end of "one". So strictly the seg fault is not due to lack of storage - we never get that far. In fact this code will also likely give you a seg fault:
char* one = "one";
char* two = "";
strcat(one, two);
All this tries to do is copy a null over a null, but in read-only memory. I suppose a optimiser might happen to stop this on some platforms.
Oddly enough the following (incorrect) code will (probably) not give you a seg fault, and even give the "right" answer:
char one[] = "one";
char two[] = "two";
strcat(one, two);
printf("%s\n", one);
This successfully writes "onetwo" to stdout on my machine. We get a stack scribble, which we happen to get away with.
On the other hand this does seg fault:
char* one = "one "; // Plenty of storage, but not writable.
char two[] = "two";
strcat(one,two);
Hence the solution:
const unsigned enoughSpace = 32;
char one[enoughSpace] = "one";
char two[] = "two";
strcat(one,two);
printf("%s\n", one);
The issue with this is of course, how large to make enoughSpace in order to store what ever is coming?
Hence the functions strncat, or strcat_s, or more easily std::string.
Moral of the story: in C++, just like C, you really need to know what your memory layout is.