It has been a while since I looked at C (still learning) and I just got back into the K&R book.
I just had a go to Exercise 5-3 (p107).
Write a pointer version of the function strcat that we showed in Chapter 2: strcat(s,t) copies the string t to the end of s.
I came up with this...
void strcat(char *s, char *t);
void strcat(char *s, char *t) {
while (*s++ != '\0');
s--;
while (*t != '\0') {
*s++ = *t++;
}
*--t = '\0';
}
int main() {
char str[] = "Hey, hello";
char str2[] = " are you?";
strcat(str, str2);
printf("%s\n", str);
return 0;
}
It seems to work.
What I am wondering, is that the K&R book often writes exercises with as little lines as possible - I'd expect had they provided their own code sample for above, you'd get things like this...
void strcat(char *s, char *t) {
while (*s++ != '\0');
s--;
while ((*s++ = *t++) != '\0');
*--t = '\0';
}
To me, this is less readable (maybe this example isn't as good, but I often look at their code and think if that was separated into a few lines, I'd understand it much better). The examples provided in the book seem to advocate this sort of assignment in the condition part of a loop, and in fact cramming as much code as possible per line.
Is the book right in trying to do as much possible where you can, even if readability suffers?
Is this just The C Way?
K&R explain the importance of idioms in the book. Yes, brevity of code is valued by C programmers, but it's not deliberately terse to punish beginners. After some time reading and writing C you start to recognize patterns, so when you see them in someone else's code you know what you're looking at.
Go through the iterations of strcpy() given as an example in K&R -- they explain their philosophy of brevity vs. clarity, and talk about idioms.
You should not expect your program to work, since you are invoking undefined behavior.
You define two buffers of a certain size (str is 11 bytes long, str2 is 10 bytes long). Then, during strcat, you try to write to str[11], which doesn't exist. From this point on there is no guarantee whatsoever about the execution of your program. It may crash, it may do what you expected, or it might just print "42" and make you wonder why.
Furthermore, you should not change *t in strcat, since in newer versions of C t has type const char *.
And third, when re-implementing a function that is also provided by your environment, give it another name. Otherwise your compiler might replace it with some builtin code that is equivalent to the function call. For example GCC has __builtin_strlen which sometimes replaces calls to strlen.
The fixed version of the code looks like this:
#include <stdio.h>
/* renamed strcat to str_cat to avoid confusing the compiler */
void str_cat(char *s, const char *t) { /* added the const qualifier to t */
while (*s++ != '\0');
s--;
while (*t != '\0') {
*s++ = *t++;
}
/* removed the needless modification of *t */
*s = '\0'; /* edit: added this line after the comment from Jonathan Leffler */
}
int main() {
char str[80] = "Hey, hello"; /* note the large array size here */
char str2[] = " are you?";
str_cat(str, str2);
printf("%s\n", str);
return 0;
}
Other more readable, more efficient examples can be found by using Google Codesearch.
Look at the source code for Android and BSD in particular as good examples of more modern C implementation of strcat.
Instead of strcat you should be writing an implementation of strlcat and many examples of that source can be found as well.
Related
void trim(char *line)
{
int i = 0;
char new_line[strlen(line)];
char *start_line = line;
while (*line != '\0')
{
if (*line != ' ' && *line != '\t')
{
new_line[i] = *line;
i++;
}
line++;
}
new_line[i] = '\0';
printf("%s\n", start_line);
printf("%s\n", new_line);
strcpy(start_line, new_line);
}
I really cannot find the problem here. My pointers are initialized, and I made a pointer to have the start of the string line. At the end I would like to copy the new line in the old one, so the caller has a changed value of his line.
But strcpy() makes a segmentation fault. What is wrong?
This is the code that calls trim():
char *str = "Irish People Try American Food";
printf("%s\n", str);
trim(str);
printf("%s\n", str);
You need to show the whole program; what calls "trim()"? Paul R's answer is right, you are one character short and it should be at least:
char new_line[strlen(line) + 1];
However, this will not always cause a segfault, and if it did it would probably not be at strcpy().
The likely reason strcpy(start_line, new_line) is faulting is that start_line points to the original value of line. It is likely that you are calling the function like:
int main() {
trim("blah blah\tblah");
return 0;
}
If so, line is a pointer to a constant char array that can't be modified. On many OS's this is stored in a read-only memory area, so it will cause an immediate segmentation fault if a write attempt is made. So strcpy() then faults when trying to write into to this read only location.
As a quick test try this:
int main() {
char test[100] = "blah blah\tblah";
trim(test);
return 0;
}
If it works, that's your specific issue with strcpy() faulting.
EDIT - the question was updated later to include the main() calling function, which confirmed that the trim function was called with a pointer to a string constant. The problem line is:
char *str = "Irish People Try American Food";
This creates a string literal, an array of 31 characters including a null terminator which cannot be modified. The pointer str is then initialized with the address of this constant, array.
The correction is to allocate a regular array of characters and then initialize it with the known string. In this case the assignment and temporary constant string literal may or may not be optimized out, but the end result is always the same - a writable array of characters initialized with the desired text:
char str[100] = "Irish People Try American Food";
/* or */
char str2[] = "American People Like Irish Beer";
/* or */
char *str3[37];
strcpy(str3, "And German Beer"); /* example only, need to check length */
These create normal writable char arrays of lengths 100, 32, and 37, respectively. Each is then initialized with the given strings.
The ANSI/ISO C standard defined the language such that a string literal is a array of char that cannot be modified. This is the case even as it was first standardized in C89. Prior to this string literals had been commonly writable, such as in the pre-standard K&R C of very early UNIX code.
Identical string literals of either form need not be distinct. If
the program attempts to modify a string literal of either form, the
behavior is undefined. - ANSI X3.159-1989
Many C89 and newer compilers have since then placed this array into the .text or .rodata segments where it may even be physically unwritable (ROM, read-only MMU pages, etc.), as discovered here. Compilers may also coalesce duplicate string constants into single one to conserve space - and you wouldn't to write into "one" of those either!
The fact that these semantically unwritable strings were still left as type char *, and that they could be assigned to and passed as such was known to be a compromise, even as the C89 standard was being drafted. That they did not use the then-brand-new type qualifier const was described as a "not-completely-happy result". See Richie's (DMR's) explanation.
And apparently that result still boomerangs around and whacks people upside the head nearly 30 years later.
Your new_line string is one char too small - it does not have room for the final '\0' terminator - change:
char new_line[strlen(line)];
to:
char new_line[strlen(line) + 1];
You should also be aware that string literals can not be modified, so if you try to call your function like this:
trim("Hello world!");
then this will result in undefined behaviour. (You should also get a compiler warning if you try to do this.)
As #PaulR stated, your new line's buffer is too small. But instead of using another buffer that takes up more space, you could use a single-character approach, like this:
void trim(char *s)
{
char *src = s, *dest = s;
while (*src)
{
if ((*src != ' ') && (*src != '\t'))
*dest++ = *src;
++src;
}
*dest = '\0';
}
I'm trying to learn some C and am having a little bit of trouble with manipulating strings. In trying to learn them, I've decided to make a simple spanish verb conjugator, but I'm getting stuck. Right now I'm just trying to drop the last 2 non '\0' of the string and then add a 'o' to it. (For example, for an input of "hablar" I want it to output "hablo"). Here's my code. I've tried to be overly detailed in my comments to hopefully aid in figuring out what I'm missing conceptually.
#include <stdio.h>
#include <string.h>
/* Reimplemented the length function of a string for practice */
int len(char *);
void conjugatePresentAr(char *, char *);
int len(char *arr){
int l = 0;
while (*arr++ != '\0'){
l++;
}
return l;
}
void conjugatePresentAr(char *verb, char *output){
output = verb;
int i = len(verb);
while (output < (verb + i -2)){
*output = *verb;
output++;
verb++;
}
*output = 'o';
output++;
*output = '\0';
}
int main(){
char input[20];
scanf("%s", input);
printf("%s\n",input);
char conjugated[20];
conjugatePresentAr(input, conjugated);
printf("%s\n", conjugated);
return 0;
}
For any input I get Segmentation Fault: 11. I've spent a decent amount of time looking around here and reading through books on pointers but can't quite seem to figure out what I'm messing up. I appreciate your help!
In conjugatePresentAr() you have changed the argument *output, possibly because you thought that copies the string.
output = verb;
so the function doesn't write anything to the string you supplied. Then when you print it, it's still an uninitialised variable.
int i = len(verb);
while (output < (verb + i -2)){
*output = *verb;
output++;
verb++;
}
will keep going forever: you're chasing (verb + i - 2) as it recedes into the distance (you increment verb inside the loop).
Try something like:
char *end = verb + strlen(verb) - 2;
while (output < end) {
...
verb++; /* this doesn't change end */
}
(and also fix the bug Weather Vane spotted which I entirely missed).
Note: in general, string processing is hard to do well in C, because the built-in facilities are so low-level. It's actually much easier to use C++ with its string and stringstream facilities.
If you're sticking to C, explicitly tracking length and allocated capacity alongside the char pointer (as the C++ string does for you) is good practice. Oh, and there's no obvious benefit to re-writing strlen.
You can't copy strings (char *) by assignment, like you did here:
output = verb;
What you do here is just change output to point at the input string, so any changes made to one of the strings will also apply to the other one - since they both point to the same memory.
you need to explicitly a function for copying the memory - such as strcpy (make sure to supply a null terminated string) or memcpy.
And, regarding your logic, since you don't really check the string for 'ar' in the end, and just assume there is, why not use something a little simpler like this:
void conjugatePresentAr(char *verb, char *output)
{
strcpy(output,verb);
int len = strlen(verb);
output[len - 2] = 'o';
output[len - 1] = '\0';
}
In function conjugatePresentAr() you have alterered the argument *output
output = verb;
Is an address affectation, not value.
Should reread pointer definition
I tried to write my own implementation of the strchr() method.
It now looks like this:
char *mystrchr(const char *s, int c) {
while (*s != (char) c) {
if (!*s++) {
return NULL;
}
}
return (char *)s;
}
The last line originally was
return s;
But this didn't work because s is const. I found out that there needs to be this cast (char *), but I honestly don't know what I am doing there :( Can someone explain?
I believe this is actually a flaw in the C Standard's definition of the strchr() function. (I'll be happy to be proven wrong.) (Replying to the comments, it's arguable whether it's really a flaw; IMHO it's still poor design. It can be used safely, but it's too easy to use it unsafely.)
Here's what the C standard says:
char *strchr(const char *s, int c);
The strchr function locates the first occurrence of c
(converted to a char) in the string pointed to by s. The
terminating null character is considered to be part of the string.
Which means that this program:
#include <stdio.h>
#include <string.h>
int main(void) {
const char *s = "hello";
char *p = strchr(s, 'l');
*p = 'L';
return 0;
}
even though it carefully defines the pointer to the string literal as a pointer to const char, has undefined behavior, since it modifies the string literal. gcc, at least, doesn't warn about this, and the program dies with a segmentation fault.
The problem is that strchr() takes a const char* argument, which means it promises not to modify the data that s points to -- but it returns a plain char*, which permits the caller to modify the same data.
Here's another example; it doesn't have undefined behavior, but it quietly modifies a const qualified object without any casts (which, on further thought, I believe has undefined behavior):
#include <stdio.h>
#include <string.h>
int main(void) {
const char s[] = "hello";
char *p = strchr(s, 'l');
*p = 'L';
printf("s = \"%s\"\n", s);
return 0;
}
Which means, I think, (to answer your question) that a C implementation of strchr() has to cast its result to convert it from const char* to char*, or do something equivalent.
This is why C++, in one of the few changes it makes to the C standard library, replaces strchr() with two overloaded functions of the same name:
const char * strchr ( const char * str, int character );
char * strchr ( char * str, int character );
Of course C can't do this.
An alternative would have been to replace strchr by two functions, one taking a const char* and returning a const char*, and another taking a char* and returning a char*. Unlike in C++, the two functions would have to have different names, perhaps strchr and strcchr.
(Historically, const was added to C after strchr() had already been defined. This was probably the only way to keep strchr() without breaking existing code.)
strchr() is not the only C standard library function that has this problem. The list of affected function (I think this list is complete but I don't guarantee it) is:
void *memchr(const void *s, int c, size_t n);
char *strchr(const char *s, int c);
char *strpbrk(const char *s1, const char *s2);
char *strrchr(const char *s, int c);
char *strstr(const char *s1, const char *s2);
(all declared in <string.h>) and:
void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
(declared in <stdlib.h>). All these functions take a pointer to const data that points to the initial element of an array, and return a non-const pointer to an element of that array.
The practice of returning non-const pointers to const data from non-modifying functions is actually an idiom rather widely used in C language. It is not always pretty, but it is rather well established.
The reationale here is simple: strchr by itself is a non-modifying operation. Yet we need strchr functionality for both constant strings and non-constant strings, which would also propagate the constness of the input to the constness of the output. Neither C not C++ provide any elegant support for this concept, meaning that in both languages you will have to write two virtually identical functions in order to avoid taking any risks with const-correctness.
In C++ you wild be able to use function overloading by declaring two functions with the same name
const char *strchr(const char *s, int c);
char *strchr(char *s, int c);
In C you have no function overloading, so in order to fully enforce const-correctness in this case you would have to provide two functions with different names, something like
const char *strchr_c(const char *s, int c);
char *strchr(char *s, int c);
Although in some cases this might be the right thing to do, it is typically (and rightfully) considered too cumbersome and involving by C standards. You can resolve this situation in a more compact (albeit more risky) way by implementing only one function
char *strchr(const char *s, int c);
which returns non-const pointer into the input string (by using a cast at the exit, exactly as you did it). Note, that this approach does not violate any rules of the language, although it provides the caller with the means to violate them. By casting away the constness of the data this approach simply delegates the responsibility to observe const-correctness from the function itself to the caller. As long as the caller is aware of what's going on and remembers to "play nice", i.e. uses a const-qualified pointer to point to const data, any temporary breaches in the wall of const-correctness created by such function are repaired instantly.
I see this trick as a perfectly acceptable approach to reducing unnecessary code duplication (especially in absence of function overloading). The standard library uses it. You have no reason to avoid it either, assuming you understand what you are doing.
Now, as for your implementation of strchr, it looks weird to me from the stylistic point of view. I would use the cycle header to iterate over the full range we are operating on (the full string), and use the inner if to catch the early termination condition
for (; *s != '\0'; ++s)
if (*s == c)
return (char *) s;
return NULL;
But things like that are always a matter of personal preference. Someone might prefer to just
for (; *s != '\0' && *s != c; ++s)
;
return *s == c ? (char *) s : NULL;
Some might say that modifying function parameter (s) inside the function is a bad practice.
The const keyword means that the parameter cannot be modified.
You couldn't return s directly because s is declared as const char *s and the return type of the function is char *. If the compiler allowed you to do that, it would be possible to override the const restriction.
Adding a explicit cast to char* tells the compiler that you know what you're doing (though as Eric explained, it would be better if you didn't do it).
UPDATE: For the sake of context I'm quoting Eric's answer, since he seems to have deleted it:
You should not be modifying s since it is a const char *.
Instead, define a local variable that represents the result of type char * and use that in place of s in the method body.
The Function Return Value should be a Constant Pointer to a Character:
strchr accepts a const char* and should return const char* also. You are returning a non constant which is potentially dangerous since the return value points into the input character array (the caller might be expecting the constant argument to remain constant, but it is modifiable if any part of it is returned as as a char * pointer).
The Function return Value should be NULL if No matching Character is Found:
Also strchr is supposed to return NULL if the sought character is not found. If it returns non-NULL when the character is not found, or s in this case, the caller (if he thinks the behavior is the same as strchr)
might assume that the first character in the result actually matches (without the NULL return value
there is no way to tell whether there was a match or not).
(I'm not sure if that is what you intended to do.)
Here is an Example of a Function that Does This:
I wrote and ran several tests on this function; I added a few really obvious sanity checks to avoid potential crashes:
const char *mystrchr1(const char *s, int c) {
if (s == NULL) {
return NULL;
}
if ((c > 255) || (c < 0)) {
return NULL;
}
int s_len;
int i;
s_len = strlen(s);
for (i = 0; i < s_len; i++) {
if ((char) c == s[i]) {
return (const char*) &s[i];
}
}
return NULL;
}
You're no doubt seeing compiler errors anytime you write code that tries to use the char* result of mystrchr to modify the string literal being passed to mystrchr.
Modifying string literals is a security no-no, because it can lead to abnormal program termination and possibly denial-of-service attacks. Compilers may warn you when you pass a string literal to a function taking char*, but they aren't required to.
How do you use strchr correctly? Let's look at an example.
This is an example of what not to do:
#include <stdio.h>
#include <string.h>
/** Truncate a null-terminated string $str starting at the first occurence
* of a character $c. Return the string after truncating it.
*/
const char* trunc(const char* str, char c){
char* pc = strchr(str, c);
if(pc && *pc && *(pc+1)) *(pc+1)=0;
return str;
}
See how it modifies the string literal str via the pointer pc? That's no bueno.
Here's the right way to do it:
#include <stdio.h>
#include <string.h>
/** Truncate a null-terminated string $str of $sz bytes starting at the first
* occurrence of a character $c. Write the truncated string to the output buffer
* $out.
*/
char* trunc(size_t sz, const char* str, char c, char* out){
char* c_pos = strchr(str, c);
if(c_pos){
ptrdiff_t c_idx = c_pos - str;
if((size_t)n < sz){
memcpy(out, str, c_idx); // copy out all chars before c
out[c_idx]=0; // terminate with null byte
}
}
return 0; // strchr couldn't find c, or had serious problems
}
See how the pointer returned by strchr is used to compute the index of the matching character in the string? The index (also equal to the length up to that point, minus one) is then used to copy the desired part of the string to the output buffer.
You might think "Aw, that's dumb! I don't want to use strchr if it's just going to make me memcpy." If that's how you feel, I've never run into a use case of strchr, strrchr, etc. that I couldn't get away with using a while loop and isspace, isalnum, etc. Sometimes it's actually cleaner than using strchr correctly.
At a recent job interview, I was asked to implement my own string copy function. I managed to write code that I believe works to an extent. However, when I returned home to try the problem again, I realized that it was a lot more complex than I had thought. Here is the code I came up with:
#include <stdio.h>
#include <stdlib.h>
char * mycpy(char * d, char * s);
int main() {
int i;
char buffer[1];
mycpy(buffer, "hello world\n");
printf("%s", buffer);
return 0;
}
char * mycpy (char * destination, char * source) {
if (!destination || !source) return NULL;
char * tmp = destination;
while (*destination != NULL || *source != NULL) {
*destination = *source;
destination++;
source++;
}
return tmp;
}
I looked at some other examples online and found that since all strings in C are null-terminated, I should have read up to the null character and then appended a null character to the destination string before exiting.
However one thing I'm curious about is how memory is being handled. I noticed if I used the strcpy() library function, I could copy a string of 10 characters into a char array of size 1. How is this possible? Is the strcpy() function somehow allocating more memory to the destination?
Good interview question has several layers, to which to candidate can demonstrate different levels of understanding.
On the syntactic 'C language' layer, the following code is from the classic Kernighan and Ritchie book ('The C programming language'):
while( *dest++ = *src++ )
;
In an interview, you could indeed point out the function isn't safe, most notably the buffer on *dest isn't large enough. Also, there may be overlap, i.e. if dest points to the middle of the src buffer, you'll have endless loop (which will eventually creates memory access fault).
As the other answers have said, you're overwriting the buffer, so for the sake of your test change it to:
char buffer[ 12 ];
For the job interview they were perhaps hoping for:
char *mycpy( char *s, char *t )
{
while ( *s++ = *t++ )
{
;
}
return s;
}
No, it's that strcpy() isn't safe and is overwriting the memory after it, I think. You're supposed to use strncpy() instead.
No, you're writing past the buffer and overwriting (in this case) the rest of your stack past buffer. This is very dangerous behavior.
In general, you should always create methods that supply limits. In most C libraries, these methods are denoted by an n in the method name.
C does not do any run time bounds checking like other languages(C#,Java etc). That is why you can write things past the end of the array. However, you won't be able to access that string in some cases because you might be encroaching upon memory that doesn't belong to you giving you a segementation fault. K&R would be a good book to learn such concepts.
The strcpy() function forgoes memory management entirely, therefore all allocation needs to be done before the function is called, and freed afterward when necessary. If your source string has more characters than the destination buffer, strcpy() will just keep writing past the end of the buffer into unallocated space, or into space that's allocated for something else.
This can be very bad.
strncpy() works similarly to strcpy(), except that it allows you to pass an additional variable describing the size of the buffer, so the function will stop copying when it reaches this limit. This is safer, but still relies on the calling program to allocate and describe the buffer properly -- it can still go past the end of the buffer if you provide the wrong length, leading to the same problems.
char * mycpy (char * destination, char * source) {
if (!destination || !source) return NULL;
char * tmp = destination;
while (*destination != NULL || *source != NULL) {
*destination = *source;
destination++;
source++;
}
return tmp;
}
In the above copy implementation, your tmp and destination are having the same data. Its better your dont retrun any data, and instead let the destination be your out parameter. Can you rewrite the same.
The version below works for me. I'm not sure if it is bad design though:
while(source[i] != '\0' && (i<= (MAXLINE-1)))
{
dest[i]=source[i];
++i;
}
In general it's always a good idea to have const modifier where it's possible, for example for the source parameter.
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.
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).
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);
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);
}
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.
I tested this bit in VS2008, and it worked fine.
void NewStrCat(char* dest, const char* src)
{
while (*dest) ++dest;
while (*dest++ = *src++);
}
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.
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.
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!
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);
}
void my_strcat(char* dest, const char* src)
{
while (*dest) ++dest;
while (*dest++ = *src++);
*dest = '\0';
}
Destination string must end with NULL terminated.