views:

768

answers:

9

Recently I attended an interview where they asked me to write a C program to concatenate two strings without using strcat(), strlen() and strcmp() and that function should not exceed two (2) lines.

I know how to concatenate two strings without using strcat(). But my method has nearly 15 lines. I dont know how to write it in two lines.

+18  A: 

I expect they wanted something like this:

void mystrcat(char * dest, const char * src)
{
    //advance dest until we find the terminating null
    while (*dest) ++dest;

    //copy src to dest including terminating null, until we hit the end of src
    //Edit: originally this: 
    //for (; *dest = *src, *src; ++dest, ++src);
    //...which is the same as this
    for (; *dest = *src; ++dest, ++src);
}

It doesn't return the end of the concatenated string like the real strcat, but that doesn't seem to be required.

I don't necessarily know if this sort of thing is a good interview question - it shows that you can code tersely, and that you know what strcat does, but that's about it.

Edit: as aib writes, the statement

while (*dest++ = *src++);

...is perhaps a more conventional way of writing the second loop (instead of using for).

Doug
just curious, is the lone *src part of the condition of the for loop necessary? should *dest = *src pretty much evaluate to the same thing?
jay.lee
@jay.lee: yes, you're right. I first wrote the termination condition, and then realized that I needed to copy the final null, so I moved `*dest = *src` from the increment part of the expression to the termination condition.
Doug
well it does show if the interviewee knows what pointers are.
Anders K.
I consider the ability to recognize the triviality of a task and how to express it succinctly an important job skill. I've seen plenty of fools write 20-50 line functions to perform tasks equally trivial to `strcat`.
R..
I'd add checks for `*dest != '\0'`, for pedantic folks like me who insist that character expressions are not boolean. I'd also use `continue` to make it clear that the copying loop has an empty body.
Loadmaster
+2  A: 

Any function can be made to fit in a single line by simply removing all the \n.

However, I think you're looking for this answer:

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

int main(void)
{
    char string1[32] = "Hello";
    char string2[] = ", World!";

    char *dst = string1 + strlen(string1);
    char *src = string2;

    while (*dst++ = *src++); //single statement

    printf("\"%s\"\n", string1);

    return EXIT_SUCCESS;
}

The explanation is rather simple:

src++ returns a pointer to the current character being copied before incrementing to point to the next one. * dereferences this pointer, and a similar expression on the LHS copies it to dst. Result of the whole = expression is the character that was copied, hence a simple while loops it until a \0 is encountered and copied.

However:

strcat() is easier to read and possibly much faster. Any other solution is feasible only when strcat() is not available. (Or when you're in an interview, apparently.) And replace strcat() above with strncat() unless you're really really sure the destination string is big enough.

Edit: I missed the part about strlen() being disallowed. Here's the two-statement function:

void my_strcat(char * restrict dst, const char * restrict src)
{
    while (*dst) ++dst;      //move dst to the end of the string
    while (*dst++ = *src++); //copy src to dst
}

Note that the standard strcat() function returns the original value of dst.

aib
The question explicitly disallowed `strlen()`.
Joe White
Oh, right. Editing...
aib
+5  A: 

The proper answer to that question is that the question would demonstrate a skill that it is bad to have. They are wanting you to demonstrate the ability to write hacker code. They are wanting you to invent your own implementation of things provided already by every C compiler, which is waste of time. They are wanting you to write streamlined code which, by definition, is not readable. The 15 line implementation is probably better if it is more readable. Most projects do not fail because the developers wasted 150 clock cycles. Some do fail because someone wrote unmaintainable code. If you did have to write that, it would need a 15 line comment. So my answer to that would be, show me the performance metrics that defend needing to not use the standard libraries and requiring the most optimal solution. Time is much better spent on design and gathering those performance metrics.

Never forget - you are also interviewing them.

 //assuming szA contains "first string" and szB contains "second string"
 //and both are null terminated
 //  iterate over A until you get to null, then iterate over B and add to the end of A
 //  and then add null termination to A
 //  WARNING:  memory corruption likely if either string is not NULL terminated
 //  WARNING:  memory corruption likely if the storage buffer for A was not allocated large
 //            enough for A to store all of B's data
 //  Justification:  Performance metric XXX has shown this optimization is needed
 for(int i=0; szA[i]!='\0'; i++); 
 for(int j=0; (j==0)||(szB[j-1]!='\0'); j++) szA[i+j] = szB[j];

*edit, 9/27/2010

After reading some other solutions to this, I think the following is probably the best code answer:

 //Posted by Doug in answer below this one
 void my_strcat(char * dest, const char * src)
 {    
      while (*dest) ++dest;    
      while (*dest++ = *src++);     
 }

But I would follow that up with a safe version of that:

 void my_safe_strcat(char * dest, const unsigned int max_size, const char * src)
 {
      int characters_used=0;
      while (*dest) { ++dest; characters_used++; }
      while ( (characters_used < (max_size-1) ) && (*dest++ = *src++) ) characters_used++;
      *dest = 0; //ensure we end with a null
 }

And follow that up with (full answer, which compiler will optimize to be the same as above, along with application which was the real question):

void my_readable_safe_strcat(char * dest, const unsigned int max_size, const char * src)
{
    unsigned int characters_used = 0;
    while (*dest != '\0') 
    { 
        ++dest; 
        characters_used++;   
    }
    while ( (characters_used < (max_size-1) ) && (*dest = *src) ) 
    {
        dest++;
        src++;
        characters_used++;
    }
    *dest = 0; //ensure we end with a null
}



int _tmain(int argc, _TCHAR* argv[])
{
    char szTooShort[15] = "First String";
    char szLongEnough[50] = "First String";
    char szClean[] = "Second String";
    char szDirty[5] = {'f','g','h','i','j'};

    my_readable_safe_strcat(szTooShort,15,szClean);
    printf("This string should be cut off:\n%s\n\n",szTooShort);

    my_readable_safe_strcat(szLongEnough,50,szClean);
    printf("This string should be complete:\n%s\n\n",szLongEnough);

    my_readable_safe_strcat(szLongEnough,50,szDirty);
    printf("This string probably has junk data in it, but shouldn't crash the app:\n%s\n\n",szLongEnough);

}
PatrickV
In addition, I wouldn't bet that the terse version that they're after is necessarily faster than a readable version, and it's almost certainly slower than the C library version.
Doug
It's an interview question. They're looking to see how you think creatively under pressure, not show you how to write code. I've had interviewers ask me the Burning Rope question, but I guarantee that lighting a rope on fire wouldn't have been part of my job description.
kubi
it also shows if the candidate knows how to use pointers so not entirely useless.
Anders K.
@kubi Yes, but the write way to think creatively under pressure is not choosing to write this 2 line function as posted in the answer. Responding to pressure by making the code less readable is not the right answer to this problem we all face in our professional lives. I guess in the end it may depend on the job, but I still say the correct answer to this is not technical, or at least it is not *just* the code. Which is why after the tirade, I included the code.
PatrickV
If this were a real-life problem you'd be entirely correct, but it's not, it's an intellectual exercise in an interview. Questions like this are intended to give insight into the candidates creative thought process. Refusing to answer the puzzle as stated demonstrates nothing to the interviewer other than a distinct lack of social skills. When an interviewer asks you to estimate the weight of a 747, the "correct" answer is "Look it up on Wikipedia", but that is also guaranteed to not impress the interviewer.
kubi
+1 for using C99 `for`-scope variables. But unfortunately you failed the interview, since your code is not correct. Hint: `i` is bound to the scope of the loop where it is declared.
Jens Gustedt
Heh, good point. That's what happens when I don't use a compiler!
PatrickV
@Kubi Who refused to answer the question? Like I said, the response should be to give the code, but more importantly to demonstrate why it is a question that shouldn't ever be asked. Who cares if an interviewee can demonstrate a useless skill? Come up with a good real-life example to see what they can do in situations that will come up.
PatrickV
-1, there's no indication that this interview question is looking for "hacker code". Ability to implement a trivial operation in a succinct way that demonstrates its triviality is an important job skill. I would not want someone who could not perform this task working for me, and I've seen way too many people who would implement this with 20-50 lines of useless code.
R..
I think I understand your point R, but disagree. The interview question said to implement something in 2 lines. After having reviewed everything that has been suggested here, I'm left with the same impression. Any such algorithm is not readable code. Readability is more important than squeezing something in 2 lines. When an interview is over, I want the best candidate, and a candidate that knows software engineering is much more valuable than a candidate that knows programming. The only way I like this question is if the next one is "Why should you never do that professionally?"
PatrickV
+14  A: 

Given that the task was to concatenate two strings, not to create a duplicate of strcat, I'd go with the simple option of creating a completely new string that is a combination of the two.

char buffer[REASONABLE_MAX] = {0};
snprintf(buffer, REASONABLE_MAX - 1, "%s%s", string1, string2);
torak
+1 for cleverness and for use of `snprintf`. :-)
R..
+1 for making me smile
pm100
+2  A: 

Two lines? Bwah...

void another_strcat(char* str1, const char* str2)
{
    strcpy(strchr(str1, '\0'), str2);
}

EDIT: I'm very upset that people are so against strcpy and strchr. Waah! So, I thought I'd play by the spirit of the rules:

char thing(char* p, const char* s)
{
    return *p ? thing(&p[1], s) : *s ? (*p++ = *s++, thing(p, s)) : *p = '\0';
}

I still can't understand how anyone would take 2 whole lines ;-P.

Tony
Not sure if that violates the constraints of the problem.
Loadmaster
@Loadmaster: possibly, but it's hard to take such a question seriously, and careless to ask it by denying a few specific functions rather than saying "don't use library functions" ;-). Frankly, anyone answering this by writing a treatise and getting philosophical would worry me as much as anyone not answering it at all, though say abelenky's post is nicely succinct.
Tony
Lol I am amazed this post got up votes after using library functions
fahad
@fahad: as per my earlier comment, nothing in the question prohibits all library functions. And, snprintf's currently got 12 votes, and it's not even strcat compatible (as implemented, it writes to a different buffer)...! ;-)
Tony
The question was to catenate without using string.h(He mentioned it indirectly by saying not to use strlen,strcat etc)
fahad
A: 
void StringCatenation(char *str1,char *str2)
        {
          int len1,i=0;
          for(len1=0;*(str1+len1);len1++);
          do{
            str1[len1+i]=str2[i];
            i++;
            }
            while(*(str2+i);
        }
fahad
You made the same mistake I did - len1 is only in scope for the first for loop, but used in the second. Len2 also seems to be unused.
PatrickV
@PatrickV:thanks for mentioning,i fixed the code.
fahad
Why would you write `*(str1+len1)` instead of `str1[len1]`?
Marius Gedminas
Its the same thing just did that for no reason
fahad
@fahad: your code fails to copy or set the NUL terminator. Trivially, your second `for` loop needs the comma to be a semicolon.
Tony
Thanks,fixed both.
fahad
A: 

I have a feeling such questions are meant to be elimination questions rather than selection. It is easier to eliminate candidates for them based on such convoluted questions rather than select candidates by asking them more real world questions. Just a rant from me, since I am also looking for a job and facing such questions and answered quite a few of them thanks to SO!

Shivram
+3  A: 

I tested this bit in VS2008, and it worked fine.

void NewStrCat(char* dest, const char* src)
{
    while (*dest) ++dest;
    while (*dest++ = *src++);
}
abelenky
A: 

One line:

sprintf(string1, "%s%s", string1, string2);

(Note that this might possibly invoke undefined behavior.)

Addendum

The ISO C99 standard states that:
   If copying takes place between objects that overlap, the behavior is undefined.

That being said, the code above will still probably work correctly. It works with MS VC 2010.

Loadmaster
That's a pretty big caveat :-).
Tony
This is valid evem for memmove()?It copies succesfully even if the data overlaps.
fahad
@fahad: That's because `memmove` is specifically designed to work with overlapping operands. `memcpy`, however, is not, and neither are most of the ISO C library functions.
Loadmaster