0

When studying string manipulation in C, I've come across an effect that's not quite what I would have expected with strcat(). Take the following little program:

#include <stdio.h>
#include <string.h>

int main()
{
    char string[20] = "abcde";
    strcat(string + 1, "fghij");
    printf("%s", string);

    return 0;
}

I would expect this program to print out bcdefghij. My thinking was that in C, strings are arrays of characters, and the name of an array is a pointer to its first element, i.e., the element with index zero. So the variable string is a pointer to a. But if I calculate string + 1 and use that as the destination array for concatenation with strcat(), I get a pointer to a memory address that's one array element (1 * sizeof(char), in this case) away, and hence a pointer to the b. So my thinking was that the target destination is the array starting with b (and ending with the invisible null character), and to that the fghij is concatenated, giving me bcdefghij.

But that's not what I get - the output of the program is abcdefghij. It's the exact same output as I would get with strcat(string, "fghij"); - the addition of the 1 to string is ignored. I also get the same output with an addition of another number, e.g. strcat(string + 4, "fghij");, for that matter.

Can somebody explain to me why this is the case? My best guess is that it has to do with the binding precedence of the + operator, but I'm not sure about this.

Edit: I increased the size of the original array with char string[20] so that it will, in any case, be big enough to hold the concatenated string. Output is still the same, which I think means the array overflow is not key to my question.

Schnitte
  • 1,193
  • 4
  • 16
  • The program is invalid as it has Undefined Behaviour. `string` only has space for the exact string it is initialised with. You are writing past the end of the array. – kaylum May 08 '22 at 20:31
  • 3
    `string + 1` evaluates to a pointer to `string[1]`, i.e. the letter `b`. But the value of `string` itself, as a pointer expression, does not change. So evaluating `string` in the next line to pass to `printf` still returns a pointer to the original first character. – Nate Eldredge May 08 '22 at 20:35
  • Just like writing `int x = 7; print(x+3); print(x);` would print 10 and then 7. The `+` operator does not modify either of its operands. Nothing at all to do with precedence. – Nate Eldredge May 08 '22 at 20:35
  • 1
    To avoid the overrun bug (which I think is beside the point of your actual question), change `char string[] = "abcde";` to something like `char string[11] = "abcde";`. That will help you get more relevant answers. – Nate Eldredge May 08 '22 at 20:39

4 Answers4

2

You will get an output of abcdefghij, because your call to strcat hasn't changed the address of string (and nor can you change that – it's fixed for the duration of the block in which it is declared, just like the address of any other variable). What you are passing to strcat is the address of the second element of the string array: but that is still interpreted as the address of a nul-terminated string, to which the call appends the second (source) argument. Appending that second argument's content to string, string + 1 or string + n will produce the same result in the string array, so long as there is a nul-terminator at or after the n index.

To print the value of the string that you actually pass to the strcat call (i.e., starting from the 'b' character), you can save the return value of the call and print that:

#include <stdio.h>
#include <string.h>

int main()
{
    char string[20] = "abcde";
    char* result = strcat(string + 1, "fghij"); // strcat will return the "string + 1" pointer
    printf("%s", result); // bcdefghij

    return 0;
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
0
    char string[] = "abcde";
    strcat(string + 1, "fghij");

Append five characters to a full string array. Booom. Undefined behavior.

Adding something to a string array is a performance optimization that tells the runtime that the string is known to be at least that many characters long.

You seem to believe that a string is a thing of its own and not an array, and strcat is doing something to its first argument. That's not how that works. Strings are arrays*; and strcat is modifying the array contents.

*Somebody's going to come by and claim that heap allocated strings are not arrays. OP is not dealing with heap yet.

Joshua
  • 40,822
  • 8
  • 72
  • 132
0

Arrays are non-modibfiable lvalues. For example you may not write

char string[20] = "abcde";
char string2[] = ""fghij"";

string = string2;

Used in expressions arrays with rare exceptions are implicitly converted to pointers to their first elements.

If you will write for example string + 1 then the address of the array will not be changed.

In this call

strcat(string + 1, "fghij");

elements of the array string are being overwritten starting from the second element of the array.

In this statement

printf("%s", string);

there is outputted the whole array starting from its first character (again the array designator used as an argument is converted to a pointer to its first element).

You could write for example

printf("%s", string + 1);

In this case the array is outputted starting from its second element.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

These are just two pointers to different parts of the same memory inside the same array. There is nothing in your code which creates a second array. "the name of an array is a pointer to its first element" well, not really, it decays into a pointer to its first element whenever used in an expression. So in case of string + 1, this decay first happens to the string operand and then you get pointer arithmetic afterwards. You can actually never do pointer arithmetic on array types, only on decayed pointers. Details here: Do pointers support "array style indexing"?

As for strcat, it basically does two things: call strlen on the original string to find where it ends, then call strcpy to append the new string at the position where the null terminator was stored. It's the very same thing as typing strcpy(&src[strlen(src)], dst);

Therefore it won't matter if you pass string + 1 or string, because in either case strcat will look for the null terminator and nothing else.

Lundin
  • 195,001
  • 40
  • 254
  • 396