4

My task is to be able to concat two string and return a new string using pointers. It is supposed to return a new string but it returns a blank space currently. Here is my code:

char* concat(char* first, char* second){
    int string1Len = strlen(first);
    int string2Len = strlen(second);
    int length = string1Len + string2Len;
    char* string = malloc(sizeof(char)*length);
    int i;
    while(i < string1Len){
        *(string + i) = *first;
        first+= 1;
        i+= 1;
    }
    while( i < length){
        *(string + i) = *second;
        second+= 1;
        i+= 1;
    }

    return string;
}
12
  • 5
    i is not initialized to 0. Commented Apr 26, 2018 at 0:32
  • @DanieleCappuccio that not the only error... Commented Apr 26, 2018 at 0:33
  • @Stargateur what other error do you see? Perhaps he missed NULL to terminate the string? Commented Apr 26, 2018 at 0:35
  • 1
    Remember that strlen() does not count the null byte that terminates a string, and that you must allocate space for the null byte, and set the last byte to zero before returning. Also, malloc() can fail — you should check the return value. Commented Apr 26, 2018 at 0:49
  • 3
    But strlen() is a part of the string library. Commented Apr 26, 2018 at 2:37

4 Answers 4

2

Besides the unitialized int i = 0, your code seems fine. This worked on my machine:

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

char* concat(char* first, char* second){
    int string1Len = strlen(first);
    int string2Len = strlen(second);
    int length = string1Len + string2Len;
    char* string = malloc(sizeof(char)*length + 1);
    assert(string);
    int i=0;
    while(i < string1Len){
        string[i] = *first;
        first+= 1;
        i+= 1;
    }
    while(i < length){
        string[i] = *second;
        second += 1;
        i += 1;
    }
    string[i] = '\0';
    return string;
}

int main(){
        char* x = "foo";
        char* y = "bar";
        printf("%s\n", concat(x, y));
        // prints 'foobar'
}

P.S. use string[i] instead of *(string + i), it's generally considered more readable.

Sign up to request clarification or add additional context in comments.

11 Comments

Overflow on int is undefined behavior, and size_t generally hold bigger value than int there is not good reason to use an int in this case. For your second point, "Testing shows the presence, not the absence of bugs" - Dijkstra (1969)
The test shown leaks memory. You should try freeing memory, and run some loops with different sizes of strings so that the memory is probably reused. You don't null terminate your string (and NULL is not necessarily the same as a null byte, '\0' — it is often ((void *)0) which generates warnings when used in an assignment to a char). The use of size_t vs int doesn't leave me panic-stricken; for many purposes, using int will be fine — but truly robust code might care about using size_t and checking for overflows (though they're very unlikely). OTOH, using size_t isn't hard.
I agree with you guys, I really do. But the op is clearly at his first steps in C programming and debating about size_t and int seems unreasonable to me.
Using assert(first != 0 && second != 0); to check the arguments would be reasonable — the interface definition implies that two valid strings should be passed and if either pointer is null, it is not valid. Using assert(string) to check the return value from malloc() is not good — that is a runtime error that the calling code cannot control, necessarily. It would be better to use if (string == 0) return 0; (or spell 0 as NULL — your choice) or equivalent. That way, the calling code can control what happens on error, but your code works as best it can even in the face of error.
Man, I see your points. The fact is, I give the op an answer I think he can understand. I would never write code like that. If you don't like my answer, then downvote it.
|
1
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

// You should use char const * as your function don't need to change the character of your input string
char *concat(char const *first, char const *second) { // put * on the variable name not the type
    assert(first && second); // test only include for debug build concat expect valid string
    size_t first_len = strlen(first); // strlen() return a size_t
    size_t second_len = strlen(second);
    // you could check for overflow case here
    size_t length = first_len + second_len; // malloc take a size_t as argument
    char *string = malloc(length + 1); // don't forget the nul terminate byte, sizeof(char) == 1
    if (!string) { // don't forget to check malloc() error
        return NULL;
    }
    size_t i = 0; // don't forget to initialize your variable
    while (*first) {
        string[i++] = *first++; // use [] notation is more readeable  in that case
    }
    while (*second) {
        string[i++] = *second++;
    }
    string[i] = '\0'; // never forget the nul terminate byte
    return string;
}

int main(void) {
    char *my_amazing_new_string = concat("Stack ", "Overflow");
    if (!my_amazing_new_string) {
        return EXIT_FAILURE;
    }
    printf("%s", my_amazing_new_string);
    free(my_amazing_new_string);
}

4 Comments

I think your answer would be better with running text (or bullet points) to highlight the key changes. The code looks solid though. It might be possible to add assert(first != 0 && second != 0), but that's not really necessary.
Also first and second can be declared const (if we're in improving-the-code mood)
Question title says "Without using array notation"
@weston This don't make sense. I don't care about a stupid homework rule. If OP want write *(string + i++) in place of string[i++], I don't care. This doesn't matter in this question.
1

OP's code errors

Index not initialized

// int i;
int i = 0;

No null character appended nor allocated

// char* string = malloc(sizeof(char)*length);
char* string = malloc(length + 1);
....
*(string + i) ='\0'; // add this
return string;

Minor stuff

sizeof(char) is always 1

//char* string = malloc(sizeof(char)*length);
char* string = malloc(length);  // Also +1 as noted above

int may be too narrow

// int i;
size_t i;

Can't use strlen()

"without string library functions"

Allocation lacks check

char* string = malloc(...);
if (string == NULL) {  // add
  return NULL;
}

As source strings are not modified, use const.

This allows greater use of the function and potential optimizations.

//char* concat(char* first, char* second){
char* concat(const char* first, const char* second){

Some untested alternative code. Note no integer variables.

char* concat(const char* first, const char* second) {
  const char *p1 = first;
  while (*p1) {
    p1++;
  }
  const char *p2 = second;
  while (*p2) {
    p2++;
  }
  char* string = malloc((p1 - first) + (p2 - second) + 1);
  if (string) {
    char *ps = string;
    while ((*ps = *first++)) {
      ps++;
    }
    while ((*ps = *second++)) {
      ps++;
    }
  }
  return string;
}

Comments

1

String Libraries? I never use them :P

char * concat(char * dest, const char * src)
{
    // creates a pointer to `dest` so we can return it from from its start position.
    char * ptr = dest;          
    // increment dest until the null-terminated which is 0 (false//loop stops).
    while (*dest) dest++;   
    // so we assign dest pointer to src pointer while incrementing them.    
    while (*dest++ = *src++);   
    // return the `dest` pointer.
    return ptr;                 
}

Note that you'll need to allocate the first parameter yourself cause this function does concat the dest pointer itself, So the return value (is Optional ^^).

I also noticed that for some reason... everyone's allocating memory in the function itself, so I did my 2nd version. However, it's not like 1st one (You'll need the return value though).

char * concat(char * s1, const char * s2)
{
    size_t i = 0;
    // length of s1.
    while (*s1++) i++;
    size_t s1_len = i; i = 0;
    s1 -= s1_len + 1;
    // length of s2.
    while (*s2++) i++;
    size_t s2_len = i; i = 0;
    s2 -= s2_len + 1;
    // allocates memory.
    char * newStr = (char *)malloc((s1_len + s2_len)+1); // +1 is for the nul-terminated character.
    if(!newStr) return newStr; // if malloc fails, return (null) pointer.
    // points to position 0 of the 1st string.
    char * ptr = newStr;
    // copies the content of s1 to the new string
    while (*ptr++ = *s1++);
    // get back to the null-terminated to overwrite it.
    *ptr--;
    // concentrate
    while (*ptr++ = *s2++);
    // return the `newStr` pointer.
    return newStr;                  
}

8 Comments

Why +2? Everyone else has a +1
Curious mixed use of int and size_t. Suggest only size_t.
@chux I'd approve it of course since we're not dealing with negatives. Before I post this, I was writing the algorithm differently which needed the type of int (for other tasks than size). Thanks btw.
@KiraSama: dereferencing a null pointer is definitely UB: it may crash as you assume or not crash on some non protected architectures... testing this condition and returning NULL is needed for defined operation.
@KiraSama: the very concept of undefined behavior is once you have it, the program can fail in unpredictable ways, including no signs of failure at all! If you are lucky, the failure will be a crash that may be easy to investigate with a debugger, but can cause irreparable damage on the field, such as crashing the Mars lander. Do not rely on the OS protection to catch such problems, test the allocation failure and either gracefully exit the program with a diagnostic or return NULL and let the caller handle the condition appropriately.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.