0

I know I shouldn't be using that function, and I don't care. Last time I checked the spec for strcat, it said something along the lines of updating the first value as well as returning the same.

Now, this is a really stupid question, and I want you to explain it like you're talking to a really stupid person.

Why won't this work?

char* foo="foo";
printf(strcat(foo,"bar"));

EDIT: I don't know the difference between char[] and char*. How would I allocate a string of 255 characters?

EDIT 2: OK, OK, so char[number] allocates a string of that many bytes? Makes sense. Thanks.

EDIT 3: Also, how would I use a character array without declaring it? Would I typecast it as char[255]?

EDIT 4: strcat((char[256])"foo","bar") returns an error. I'm about fed up with C.

EDIT 5: So does strcat((char[256])"foo",(char[])"bar").

EDIT 5:

char[256] foo="bar";

Real smooth. "identifier expected"

  • There's nothing *wrong* with `strcat`. It's just not safe. Hopefully, whoever told you not to use it at least had the courtesy to mention `strncat`, `strcat`'s memory-safe cousin, which ensures the buffer doesn't overflow. – Braden Best Jan 03 '16 at 22:01
  • Edit 3: you can't. You either use a read-only `const char *` (includes string literals), or you declare a variable and allocate it in advance, dynamically (`char *foo`, `malloc`, etc) or statically (`char foo[256] = "bar";`). – Braden Best Jan 03 '16 at 22:17
  • Edit 4: you can't do that typecast. You should have gotten an error to the effect of "cast specifies array type". `(char *)`, however, is allowed. Not that it'll do anything. It you typecast the string literal "foo", no matter what you cast it to, the data stays the same, and it's still read-only. – Braden Best Jan 03 '16 at 22:23
  • 1
    Edit 5 (you mean 6): that's not how you declare an array in C. The compiler's even telling you that. It goes ` {*} {[size]};`, where `` are mandatory and `{fields}` are optional, e.g. `char *array_of_strings[42];` would define and alloc a 42-long array of pointers to char. Or `int ***what;`, which is a pointer to a pointer to a pointer to an int. This could either be the most indirect reference to a variable in the world, or a three-dimensional array of integers. You can do some *crazy* stuff. With great power, comes a great many ways to shoot yourself in the foot. – Braden Best Jan 03 '16 at 22:30
  • 1
    `pointer` is itself a concrete type, so it belongs on the "type" side of the declaration. But the whole array thing can be anything, and there's no such type as "42-long array". The result of `char foo[42]` *is* a `char *`, but its memory is freed after the caller exits (just like `alloca(3)`). So it's kind of like syntax sugar. Think of pointers as an I.O.U. Variables are like gold. They are tangible and can be worked with directly. Pointers are like a piece of paper that tells you *where the gold is*. It's just as valid as gold, but it's a little more tricky to get to the actual gold. – Braden Best Jan 03 '16 at 23:42

4 Answers4

3

A few problems...

1. foo is constant here. You cannot modify string literals.

2. The contract for strcat is that the first parameter is large enough to fit the concatenated string. So more realistically you'd do something this...

char foo[8] = "foo";  /* Note that buffer size is larger than the string... */

strcat(foo, "bar");

3. As you might guess, it's not obvious to a newcomer how this is supposed to work. I say there's a reason for this: in general strcat is considered quite bad. A good C interface for interacting with buffers of potentially arbitrary length will make this a lot more explicit. You might want to look at strncat or strlcat which track sizes.

I would say in general though if you're using the strcat family you're doing something wrong. Each call to strcat will have to traverse the string to find where the end is. Imagine you are doing a lot of these operations -- it's very easy to imagine something that can easily be done in O(n) steps suddenly turning into O(n2) because of the repeated traversal of the string. If you need to concatenate to a string repeatedly, you should be maintaining a pointer to the current end of the string and doing the copy from there.

Update: Example of how you might do this last suggestion follows...

struct string
{
   char *current_end;
   size_t bytes_remaining;
};

int
smart_concat(struct string *str, const char *tail)
{
   size_t new_length = strlen(tail);

   /* Do we have enough space?  (include NUL character) */
   if (new_length + 1 < str->bytes_remaining)
   {
      /* Evidently not... */
      return -1;
   }

   /* Copy the string...  (including NUL character) */
   memcpy(str->current_end, tail, new_length + 1);

   /* Update the pointer to the end of our string, and bytes remaining.. */
   str->current_end += new_length;
   str->bytes_remaining -= new_length;

   return 0;
}

Then you might use this as follows:

struct string str;
char buffer[some_size];

/* Initialize the structure to point at our buffer... */
str.current_end = buffer;
str.bytes_remaining = sizeof(buffer);

/* Strictly speaking, you should check the return code for overflow... */
smart_concat(&str, "foo");
smart_concat(&str, "bar");

/* Print out the result: */
puts(buffer);
asveikau
  • 39,039
  • 2
  • 53
  • 68
2

You need:

  • writable memory
  • sufficient space

String constants provide only the exact amount of memory in the literal and they aren't supposed to be written.

Instead, do:

char foo[255] = "foo";
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • Does that allocate a string of 254 characters? –  Jan 16 '11 at 20:47
  • 1
    @Anonymous- It allocates an array of 255 characters, indexed 0 through 254. Because C strings must end in a terminating null character, this gives you room for a string of length 254. – templatetypedef Jan 16 '11 at 20:53
  • Alright. What about typecasting? Can I use strcat((char[256])"foo","bar");? –  Jan 16 '11 at 20:57
  • A cast just means you are lying to the compiler. You might get the program to compile but there still won't be enough space and it still won't be in a writable segment. The situation will be worse. – DigitalRoss Jan 16 '11 at 21:14
2

strcat is pretty simple -- it takes pointer to a buffer containing a string and pointer to another string, and copies that second string into the buffer at the end of the string that's already there.

Note the difference between the two arguments. Both are char *, but the first is really a pointer to a buffer and only incidentally a pointer to a string (thats already in the buffer). As a buffer, it needs two things that a simple string does not:

  • it needs to be writable
  • it needs enough free space to hold the second string that is being copied into it

In your example, you try to use char *foo="foo"; as the first argument, but its just a string and is missing both of the requirements for the buffer. Instead you need to do something like:

char foo[16] =  "foo";
printf(strcat(foo, "bar"));

Now we're declaring foo as a writable char array with plenty of space to hold both strings -- an adequate buffer. This gets into the problem that most people have with strcat, which is the second requirement above -- how do you know if the buffer has enough space? In a simple example like this it does, but for more complex examples (where you don't necessarily know how long the strings are) it gets much harder. You end up needing to keep track of how big your buffer is, and use strlen to see how long the strings are to make sure they'll fit BEFORE you call strcat. This is both inefficient (you end up scanning over string strings multiple times to find lengths and then to copy) and error prone.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
1
char* foo="foo";

is a string literal.

It can't be modified. Do char [] foo = "foo"; instead but keep in mind that using strcat like that, will cause problems in this case cause it will write in memory it shouldn't when trying to strcat "bar" so you should try something like char foo[30] = "foo";

Edit: The typecasting you do... sorry I do not have so many brain cells as to try to explain you what you are trying to do. I can only tell you it is wrong. you need to provide a memory location so strcat() can work.

Try that :

int main()
{
    char foo[255]="foo";
    printf("%s",strcat(foo,"bar"));
}
  • That doesn't work either, though. Note he did `strcat(foo, ...);`. If the declaration of foo is `char foo[] = "foo";` then the buffer is not big enough to strcat into. – asveikau Jan 16 '11 at 20:42
  • I don't understand what a string literal is. Is a string literal a string that can only be expanded to the first length it is assigned to? Is char[] a dynamic string? –  Jan 16 '11 at 20:43
  • @asvei, I know. I thought about it after first posting and edited accordingly. –  Jan 16 '11 at 20:43
  • @Anonymous, `char[]` is not a dynamic string, simply speaking when you do `char foo[] = "foo"` foo automatically gets the size required to store that string. trying to `strcat` will be like writing in a place you shouldn't –  Jan 16 '11 at 20:46
  • @Anonymous: no, a string literal is a string that appears as a literal in the code, between quotes. It is generally not legal to modify it in place, even without changing its size. A char[] is a character array, also a string, but it can be modified in place. It still has constant size though, so you shouldn't add to the end of it. In order to use strcat(), you need to make sure that the original string is part of a *buffer* that has enough room for both strings together. – Avi Jan 16 '11 at 20:47
  • 1
    Aha! A character array! So char[255] is what I was looking for. Thanks, guys. –  Jan 16 '11 at 20:50
  • @Anonymous, edited my post with answers to a question. That typecasting doesn't work. –  Jan 16 '11 at 21:17
  • "you need to provide a memory location so strcat() can work" Alright, good enough. –  Jan 16 '11 at 22:37
  • Nevermind, it does work. –  Jan 16 '11 at 23:34
  • 1
    @Anonymous - This answer works, but I would encourage you to also read my answer about why `strcat`'s performance is generally bad. Also, the interface is not very explicit about buffer sizes, meaning it's easy to write security holes and reliability problems when using `strcat`. – asveikau Jan 17 '11 at 02:11