Your scanf crashed. Your strcat quietly overwrote memory. They look like two different bugs. They're the same bug.
You wrote this in exercise_concatinate_inputs.c:
char *s1;
char *s2;
printf("Enter a string1: ");
scanf("%s", s1); // β what happens here?
Run it, and it'll probably segfault β a crash where the operating system kills your program for touching memory it doesn't own. Let's see exactly why, because the reason is the single most important idea in C.
This is the whole lesson, in one sentence. Everything else is consequence.
When you write char *p;, you've created a variable that holds an address β that's all. On a 64-bit machine, p is 8 bytes of storage holding a number we interpret as "somewhere in memory." But those 8 bytes are the pointer itself. There are no characters anywhere. It's a signpost pointing at nothing.
Worse: because you didn't initialize it, p contains garbage β whatever bits were sitting in that memory before. It points at a random address.
When you write char buf[100];, you've reserved 100 real bytes of storage (on the stack). buf is a name for that plot of land. In most expressions buf "decays" into a pointer to its first element β but the land is real, and it's yours.
scanf("%s", p) walks along writing characters to the address in p β a random address. You're spray-painting on a stranger's wall. That is your crash.
malloc'd memory, or a passed-in buffer);
(2) there must be enough of it.
Now look at common_str_ops.c:
char a[] = "Hello, "; // β this array is exactly 8 bytes
char b[] = "world!";
strcat(a, b); // overflow!
Here the storage does exist β so it's not bug (1). But strcat appends "world!" (6 chars) onto the end of "Hello, " (7 chars + '\0'). That needs 7 + 6 + 1 = 14 bytes. The array a has 8. strcat writes the extra 6 bytes past the end β into whatever lives next to a in memory.
This is bug (2): not enough storage. Same principle. The program may seem to work β or it may corrupt b, crash later, or pass all your tests and fail in production. That's the curse of undefined behavior: it's not "it crashes," it's "anything can happen".
char buf1[100];
char buf2[100];
printf("Enter a string1: ");
scanf("%99s", buf1); // real storage + width cap
printf("Enter a string2: ");
scanf("%99s", buf2);
strcat(buf1, buf2); // buf1 is 100 bytes β plenty
printf("%s\n", buf1);
Two changes, both load-bearing:
buf1 is now an array β real bytes β not a bare pointer.%99s tells scanf "read at most 99 characters" so it can never write more than 99 + the '\0' = 100 bytes. The rule: width is always buffer_size β 1.scanf("%s", buf)?
Without the width, scanf reads until whitespace β however long the input is. Type 200 characters and you overflow the 100-byte buffer. The width is what makes it safe, not just working.
char a[20] = "Hello, "; // room for 7 + 6 + 1 = 14, plus slack
char b[] = "world!";
strcat(a, b); // safe: a has 20 bytes
The habit to build: before any strcat, count β current length, plus incoming length, plus 1 for the terminator. If the destination can't hold all three, it's a bug.
Predict each one: safe, or undefined behavior? Reason it out, then open the answer.
char *p = "hi"; p[0] = 'H';p is undefined. String literals should always be pointed at with const char * so the compiler catches this for you: const char *p = "hi"; now p[0] = 'H'; is a compile error, not a runtime crash.
char buf[8]; strcpy(buf, "Hello");"Hello" is 5 chars + '\0' = 6 bytes. buf holds 8. Plenty of room. β
char buf[8]; strcpy(buf, "Hello, world!");'\0' = 14 bytes. buf is 8. Overflow β bug (2), not enough storage. strcpy has no idea how big buf is; it copies until it hits '\0' in the source. (For this reason, prefer strncpy or β better β snprintf in real code.)
int n = 5; int *q = &n; *q = 99;n is a real int on the stack. q points at it. Writing through q writes to n. Both conditions met: storage exists, and it's big enough (one int). This is the legitimate use of a pointer β you already do this correctly in recall.c.
Open exercise_concatinate_inputs.c and rewrite it so it:
char s1[100]; char s2[100];).scanf("%99s", ...).s1 (which has room), and prints the result.#include <stdio.h>
#include <string.h>
int main(void) {
char s1[100];
char s2[100];
printf("Enter a string1: ");
scanf("%99s", s1);
printf("Enter a string2: ");
scanf("%99s", s2);
strcat(s1, s2); // s1 is 100 bytes β fits both + '\0'
printf("%s\n", s1);
return 0;
}
Note: s1 can hold up to 99 chars of input (the cap) and s2 another 99 β but strcat could then need 198 bytes total, overflowing s1! For total safety you'd size s1 at 200, or check lengths before concatenating. Spotting that yourself is the real win β that's the principle in action.
scanf, strcpy, or strcat in C and answer two questions on sight: does the destination have real storage? and is there enough of it? If you can answer both "yes" with evidence, the code is memory-safe. If you can't β it's a bug waiting to happen.
fgets beats scanfscanf("%s", ...) stops at the first space, which is usually not what you want for text. The grown-up idiom for "read a line from the user" is:
char buf[100];
fgets(buf, sizeof buf, stdin); // reads a whole line, spaces and all
buf[strcspn(buf, "\n")] = '\0'; // strip the trailing newline if you want
fgets never overflows β it reads at most sizeof buf β 1 chars and always null-terminates. We'll come back to fgets and parsing in a later lesson; for now, just remember it exists.
buf decay to a pointer?", "what is a segfault, exactly?", "why is size_t not int?" β type it and we'll dig in. No question is too small; the small ones are usually where the real understanding hides.
scanf β the exact semantics of the width specifier.strcat β note the overflow is your problem, not the function's.