Pregunta

So I'm working through "Sams Teach Yourself C Programming in One Hour a Day, Seventh Edition" Lesson 10 Exercise 7 which asks to "Write a function that accepts two strings. Use the malloc() function to allocate enough memory to hold the two strings after they have been concatenated (linked). Return a pointer to this new string."

I am sure there are much more elegant ways to go about this than what I have attempted below. I am mostly interested in why my solution doesn't work. I have only been learning C for a few months and have no significant programming background. Please let me know why this crashes on compilation. I am using Code Blocks on Win 7 with GNU GCC Compiler if that makes a difference. Thank you :)

#include <stdio.h>
#include <stdlib.h>
char * concatenated(char array1[], char array2[]);
int ctrtotal;

int main(void)
{
    char *comboString;
    char *array1 = "You\'re the man ";
    char *array2 = "Now Dog!";
    comboString = (char *)malloc(ctrtotal * sizeof(char));

    concatenated(array1, array2);

    if (comboString == NULL)
    {
        puts("Memory error");
        exit(1);
    }
    puts(comboString);
    free(comboString);

    return 0;
}

char * concatenated(char array1[], char array2[])
{
    char *array3;
    int ctr;
    int ctr2;

    for (ctr = 0; array1[ctr] != '\0'; ctr++)
        array3[ctr] = array1[ctr];

    ctr2 = ctr;

    for (ctr = 0; array2[ctr] != '\0'; ctr++)
    {
        array3[ctr2 + ctr] = array2[ctr];
    }

    array3[ctr2 + ctr + 1] = '\0';
    ctrtotal = (ctr2 + ctr + 2);

    return array3;
}

Thank you for the help. After reviewing everyone's feedback on my errors I revised the code to the following:

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

char * concatenated(char array1[], char array2[]);

int main(void)
{
    char *array1 = "Testing Testing One Two ";
    char *array2 = "Three.  Finally, not crashing the mem o ry.";
    char *comboString = malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char));

    comboString = concatenated(array1, array2);

    if (comboString == NULL)
    {
        puts("Memory error");
        exit(1);
    }

    puts(comboString);
    free(comboString);

    return 0;
}

char * concatenated(char array1[], char array2[])
{
    char *array3;
    array3 = malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char) );

    strcat(array3, array1);
    strcat(array3, array2);

    return array3;
}

If anyone sees any redundancies/unnecessary remaining code the could/should be deleted, please let me know. I recognize the benefit of being as concise as possible.

¿Fue útil?

Solución

Your code has a bunch of issues:

  • int ctrtotal is never initialized, so you are mallocing 0 bytes
  • concatenated() is copying characters to an uninitialized array3. This pointer should point to a mallocd buffer.
  • If concatenated is allocating the memory, then main doesn't need to. Instead it should use the result of concatenated.

I don't want to give you the full code, and let you to miss out on this learning opportunity. So concatenated should look like this, in psuedo-code:

count = length_of(string1) + length_of(string2) + 1
buffer = malloc(count)
copy string1 to buffer
copy string2 to buffer, after string1
set the last byte of buffer to '\0' (NUL)
return buffer

In C, strings are represented as a NUL-terminated array of characters. That's why we allocate one additional byte, and terminate it with \0.


As a side-note, when dealing with strings, it is far easier to work with pointers, instead of treating them as arrays and accessing them via indices.

There's a lot of code here that just doesn't make any sense. I suggest that you first write this program on paper. Then, "execute" the program in your head, stepping through every line. If you get to something you don't understand, then you need to either fix your understanding, or your incorrect code. Don't try to write code that looks like some other bit of code.


There's also a library function called strcat which will make this task even easier. See if you can figure out how to use it here.

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

                                                                                                    char *concatenate2(const char* s1, const char* s2);

                                                                                                    int main(void)
                                                                                                    {
                                                                                                        char *comboString;
                                                                                                        char *array1 = "You're the man ";
                                                                                                        char *array2 = "Now Dog!";

                                                                                                        comboString = concatenate2(array1, array2);

                                                                                                        if (comboString == NULL)
                                                                                                        {
                                                                                                            puts("Memory error");
                                                                                                            exit(1);
                                                                                                        }
                                                                                                        puts(comboString);
                                                                                                        free(comboString);

                                                                                                        return 0;
                                                                                                    }

                                                                                                    char *concatenate2(const char* s1, const char* s2)
                                                                                                    {
                                                                                                        char *result;

                                                                                                        result = malloc(strlen(s1) + strlen(s2) + 1);

                                                                                                        *result = '\0';

                                                                                                        strcat(result, s1);
                                                                                                        strcat(result, s2);

                                                                                                        return result;
                                                                                                    }

Otros consejos

You forgot to allocate memory for third, concatenated, array of chars (in function) You should do something like this:

char *array3;
array3 = (char *)malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char) ); // +1 for '\0' character.

and then write chars from first and second array into third.

Perhaps a stroll through the question code is best.

#include <stdio.h>
#include <stdlib.h>
char * concatenated(char array1[], char array2[]);
int ctrtotal;

Notice that the above line declares ctrtotal to be an integer, but does not specify the value of the integer.

int main(void)
{
   char *comboString;

   char *array1 = "You\'re the man ";
   char *array2 = "Now Dog!";
   comboString = (char *)malloc(ctrtotal * sizeof(char));

Notice that the above line allocates memory and sets 'comboString' to point at that memory. However, how much memory is being allocated?

(ctrtotal[???] * sizeof(char)[1])

What is the value of (??? * 1) ? This is a problem.

   concatenated(array1, array2);

The intent of the line above is that array1["You\'re the man "] and array2["Now Dog!"] will be joined to form a new string["You\'re the man Now Dog!"], which will be placed in allocated memory and returned to the caller.

Unfortunately, the returned memory containing the string is not captured here. For example, perhaps the above line should be:

   comboString = concatenated(array1, array2);

While this make sense, for this line, it begs a question of the purpose of the lines:

   comboString = (char *)malloc(ctrtotal * sizeof(char));

as well as the global variable:

   int ctrtotal;

and the later reference:

   ctrtotal = (ctr2 + ctr + 2);

Perhaps all of these 3 lines should be deleted?

   if (comboString == NULL)
   {
      puts("Memory error");
      exit(1);
   }

   puts(comboString);
   free(comboString);

   return 0;
}

 char * concatenated(char array1[], char array2[])
   {
   char *array3;

Notice that '*array3' is now a defined pointer, but it is not pointing anywhere specific.

   int ctr;
   int ctr2;

The purpose of 'concatenated()' is to join array1 and array1 into allocated array3. Unfortunately, no memory is allocated to array3.

Below, the memory where array3 is pointing will be modified. Since array3 is not pointing anywhere specific, this is not safe.

Prior to modifying memory where array 3 is pointing, it is important to point array3 at memory where it is safe to modify bytes. I suggest that the following code be inserted here:

   array3 = malloc(strlen(array1) + strlen(array2) + 1);

Now, array3 points to allocated memory, large enough to hold both strings plus the string termination character '\0'.

   for (ctr = 0; array1[ctr] != '\0'; ctr++)
      array3[ctr] = array1[ctr];

   ctr2 = ctr;

   for (ctr = 0; array2[ctr] != '\0'; ctr++)
   {
      array3[ctr2 + ctr] = array2[ctr];
   }

   array3[ctr2 + ctr + 1] = '\0';
   ctrtotal = (ctr2 + ctr + 2);

   return array3;
   }

I am responding to your revised code. There are a few bugs in it.

...
char *array2 = "Three.  Finally, not crashing the mem o ry.";
char *comboString = malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char));

comboString = concatenated(array1, array2);
...

The malloc is unnecessary here and actually a bug in your code. You are allocating a block of memory, but you then replace the value of the pointer comboString with the pointer from the call to concatenated. You lose the pointer to the block of memory allocated in main and thus never are able to free it. Although this will not be a problem in the code you have right now since main returns soon after, it could cause a memory leak in an application that ran for a longer time.

strcat(array3, array1);

This is also a bug. strcat is going to walk through array3 to find '\0' and then once it is found copy in array1 from that index on, replacing the '\0'. This works fine here since the memory block that was allocated for array3 is going to be zeroed out** as no block has yet been freed by your program. However, in a longer running program you can end up with a block that does not start with a '\0'. You might end up corrupting your heap, getting a segfault, etc.

To fix this, you should use strcpy instead, array3[0] = '\0', or *array3 = '\0'

** When the operating system starts your program it will initialize the memory segment it reserves for it with zeroes (this actually isn't a necessity but will be true on almost any operating system). As your program allocates and frees memory, you will eventually wind up with values that are not zero. Note that the same bug can occur with uninitialized local variables such as:

int i;
for (; i < 10; i++);

This loop will run 10 times whenever the space on the runtime stack where i is stored is already 0.

Overall, the takeaway is to be very careful with arrays and dynamic memory allocation in C. C offers you none of the protections that modern languages do. You are responsible for making sure you stay within the bounds of your array, initialize your variables, and properly allocate and free your memory. Neglecting these things will lead to obscure bugs that will take you hours to find, and most of the times these bugs will not appear right away.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top