0

I made a program which receives from 2 till 5 strings and concatenate all together using a variable arguments function.

So far the program works OK, but it always show at the end 3 random characters before showing the complete string.

For example:

Please insert number of strings: 3
string 1: car
string 2: bike
string 3: plane

Full string:

=$>carbikeplane

I have made several tweaks to the program trying to find the reason and fix it, however I always get the same result.

The full program is showed below. Few comments about the program:

  1. I am printing the strings in different parts of the programs because I was trying to locate where the problem was coming. So some of the printf() functions may not have sense.
  2. The main function seems to be fine, the problem is in the function defined later.

NOTE: I'm still learning C, so there may be some code that can/might be creating undefined behavior, if there is, I would appreciate if you can point those out.

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

char *function(int num, ...);

int main(void)
{
    char line1[80] = " ", line2[80] = " ", line3[80] = " ", line4[80] = " ", line5[80] = " ";
    int count = 0, count2;
    char *newStr;
    int i;
    int status;

    do {
        fflush(stdin);
        printf("\nPlease select the number of strings (max. 5): ");
        scanf("%d", &count);

    }while(count < 2 && count > 5);

    count2 = count;
    fflush(stdin);

    status = 1;
    for( i = 1 ; count > 0; count--, i++)
    {
        switch(status)
        {
            case 1:
            {
                printf("\nInput string[%d]: ", i);
                gets(line1);
                status = 2;
                break;
            }
            case 2:
            {
                printf("\nInput string[%d]: ", i);
                gets(line2);
                status = 3;
                break;
            }
            case 3:
            {
                printf("\nInput string[%d]: ", i);
                gets(line3);
                status = 4;
                break;
            }
            case 4:
            {
                printf("\nInput string[%d]: ", i);
                gets(line4);
                status = 5;
                break;
            }
            case 5:
            {
                printf("\nInput string[%d]: ", i);
                gets(line5);
                status = 6;
                break;
            }
        }

    }
        printf("\n%s\n%s\n%s\n%s\n%s\n", line1, line2, line3, line4, line5);
    /*call the function of variable arguments*/
        /*memory allocation of newstr*/
        newStr = (char *)malloc(420*sizeof(char) +1);
    switch(count2)
    {
        case 2:
        {
            newStr = function(2, line1, line2);
            break;
        }
        case 3:
        {
            newStr = function(3, line1, line2, line3);
            break;
        }
        case 4:
        {
            newStr = function(4, line1, line2, line3, line4);
            break;
        }
        case 5:
        {
            newStr = function(5, line1, line2, line3, line4, line5);
        }
    }

    printf("\nThe final string is: \n");
    printf("%s", newStr);

    return 0;

}


char *function(int num, ...)
{
    va_list arg_ptr;
    int b; 
    char *string;
    char *curstr;

    va_start(arg_ptr, num);     /*initialize the arg_ptr*/

    string = (char *)malloc(420*sizeof(char) + 1);
    *string = " ";

    for(b=0; b < num; b++)
    {
        curstr = va_arg(arg_ptr, char * );
        string = strcat(string,curstr);
    }

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

    va_end(arg_ptr);

    return string;

} 
Peter O.
  • 32,158
  • 14
  • 82
  • 96
  • 4
    what compiler do you use? `*string = " ";` is not valid and should not compile ... – Ferenc Deak Sep 10 '14 at 08:18
  • 4
    Daily reminder not to cast return value of `malloc()`. – Igor Pejic Sep 10 '14 at 08:20
  • 1
    To complete @Igor statement, I recommend you to read this question, because i think it is an interesting thing to understand : http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc – Logar Sep 10 '14 at 08:22
  • 1
    You forgot to include `stdlib.h` and don't use `fflush(stdin);` and `gets();`. – mch Sep 10 '14 at 08:23
  • Also, you should not use `gets()` which is vulnerable to buffer overflow attacks. See: http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1049157810&id=1043284351 Use `fgets̀ instead, for instance. – Aif Sep 10 '14 at 08:23
  • Seriously don't use `gets()`, [**Read here**](http://en.cppreference.com/w/c/io/gets): "The gets() function does not perform bounds checking, therefore this function is extremely vulnerable to buffer-overflow attacks. It cannot be used safely (unless the program runs in an environment which restricts what can appear on stdin). For this reason, the function has been deprecated in the third corrigendum to the C99 standard and removed altogether in the C11 standard. fgets() and gets_s() are the recommended replacements". And `fflush(stdin);` is utterly non-standard. Find another way. – WhozCraig Sep 10 '14 at 08:25
  • will add the stdlib, i forgot, then I will check the results. thanks for all your suggestions. The compiler im using is MinGW gcc – Andres Guerra Sep 10 '14 at 08:29
  • which version of gcc? And since you're on Windows I really recommend getting a Visual Studio Express. They're free, they come with a nice IDE and a decent compiler. – Ferenc Deak Sep 10 '14 at 08:30
  • 1
    `while(count < 2 && count > 5)` - this will never be true! – zoska Sep 10 '14 at 08:33
  • gcc version 4.8.1. I will download visual studio express to try it out. Thank you. I am recently compiling through the command prompt with gcc, and using sublime text, somehow i find this text editor very confortable to work with, and since I started to using it it helps me to make less syntax mistakes – Andres Guerra Sep 10 '14 at 08:37
  • There is an interesting concatenation routine here: [**Copying-and-Concatenation**](http://www.gnu.org/software/libc/manual/html_node/Copying-and-Concatenation.html) – David C. Rankin Sep 10 '14 at 08:38
  • `*string = " ";` --> `*string = '\0';` – BLUEPIXY Sep 10 '14 at 08:47
  • http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc you forgot to include stdlib.h and the cast hides your severe problem – phuclv Sep 10 '14 at 10:09

4 Answers4

4

The real problem is that you can compile the line: *string = " "; This is not valid anymore and should not compile. Assumingly you put that line there to initialize your string to have an initial value. But this can easily be solved by allocating the string like this:

string = calloc(420, sizeof(char));

ie: use calloc which sets the memory to zero. This way you have a valid string which can be used by strcat.

I'm not telling you to do not use gets or fflush because it is obvious that this is a home assignment and the suggested fgets has its own problems when dealing with the input string. Certainly if you will use gets in production code someone will kick you at that time.

And about casting the return value of malloc again, it's a two sided sword. If you know for sure that you will compile your project as a C project (ie: filename ends in .c and you use gcc to compile) then yes. Do not cast. However in other circumstances, such as naming the files .cpp or compiling with g++ .... well. You will get the error: error: invalid conversion from ‘void*’ to ‘char*’ without the cast. And I have the feeling that at a beginner level, while doing home assignments for school you more or less concentrate on making your code to compile and run, rather than stick to be pedantic. However for the future, it is recommended that you will be pedantic.

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • 1
    BTW: `sizeof(char) == 1` by specification. – datenwolf Sep 10 '14 at 09:21
  • @fritzone the program works now thanks to the calloc function and removing the " ", thanks for the suggestions. I am not into C++ yet, but will eventually. I am not at school heheheheheheh, just learning by myself with couple of books. – Andres Guerra Sep 10 '14 at 09:23
0

Here is a quick and dirty way to concatenate as many strings as the user wants to enter. Simply press ctrl+d when done to end input:

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

char *concat (const char *str1, const char *str2) {

    size_t len1 = strlen (str1);         /* get lenghts */
    size_t len2 = strlen (str2);

    char * s = malloc (len1 + len2 + 2); /* allocate s and concat with ' ' */
    memcpy (s, str1, len1);
    s [len1] = ' ';
    memcpy(s + len1 + 1, str2, len2); /* does not include terminating null */
    s [len1 + len2 + 1] = '\0';       /* force null termination            */

    return s;
}

int main (void) {

    char *line = NULL;  /* pointer to use with getline ()  */
    ssize_t read = 0;
    size_t n = 0;
    int cnt = 0;
    char *str;

    printf ("\nEnter a line of text to concatenate (or ctrl+d to quit)\n\n");

    while (printf (" input: ") && (read = getline (&line, &n, stdin)) != -1) {

        if (line[read-1] == '\n') {     /* strip newline */
            line[read-1] = 0;
            read--;
        }

        if (cnt == 0)                   /* if more than 1 word, concat */
            str = strdup (line);
        else
            str = concat (str, line);

        cnt++;
    }

    printf ("\n\n  Concatenated string: %s\n\n", str);

    return 0;

}

output:

$ ./bin/concat

Enter a line of text to concatenate (or ctrl+d to quit)

  input: my dog
  input: has lots of fleas
  input: my cat
  input: has some too.
  input:

    Concatenated string: my dog has lots of fleas my cat has some too.
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
-1

Length of your code is proportional to max strings user can enter. That doesn't sound good, right? (what if someone in the future will ask you to change it to allow user to enter 10 strings? 20? 100???).

In such cases usually in help come arrays - instead of having 5 different variables just use an array of them:

So change:

char line1[80], line2[80], line3[80], line4[80], line5[80];

to:

char lines[5][80];

So when in need of setting for e.g. second string, you can get it through:

char* line2 = lines[1]; \\ remember about indexes starting from 0, 
                        \\ so second element has index 1

So now instead of 5 switch cases you can go with:

for( i = 1 ; count > 0; count--, i++)
{
    printf("\nInput string[%d]: ", i);
    fgets(lines[i], 80, stdin);
}

Moreover you don't need variable arguments function, as you can just pass array and it's size:

char *function(int array_elements, char ** array);
//Usage:
concatenated_string = function(5, lines);

Also it's good practice to put all const values into variables (so when changing max amount of string user can enter, you need to change only one place).

const int MAX_STRINGS = 5;
const int MAX_STRING_LENGTH = 80;

Now comes the real problem:

string = (char *)malloc(420*sizeof(char) + 1);
*string = " ";

Why would you allocate 420 bytes? What if user enters only one string - what do you need the rest 340 bytes for?

To get the length of concatenate strings, iterate through lines (from 0 to array_size), get lengths of lines (with strlen), sum them together and add 1 for trailing '\0'. Now you won't have any unnecessary memory allocated.

Next - *string = " "; should not compile as *string is a char and " " i a string (char *). Do *string = '\0' instead or call calloc instead of malloc.

zoska
  • 1,684
  • 11
  • 23
-1

This is the modified code working fine

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

    char *function(int num, ...);

    int main(void)
    {
        char line1[80] = " ", line2[80] = " ", line3[80] = " ", line4[80] = " ", line5[80] = " ";
        int count = 0, count2;
        char *newStr;
        int i;
        int status;

        do {
            fflush(stdin);
            printf("\nPlease select the number of strings (max. 5): ");
            scanf("%d", &count);

        }while(count < 2 && count > 5);

        count2 = count;
        fflush(stdin);

        status = 1;
        for( i = 1 ; count > 0; count--, i++)
        {
            switch(status)
            {
                case 1:
                {
                    printf("\nInput string[%d]: ", i);
                    gets(line1);
                    status = 2;
                    break;
                }
                case 2:
                {
                    printf("\nInput string[%d]: ", i);
                    gets(line2);
                    status = 3;
                    break;
                }
                case 3:
                {
                    printf("\nInput string[%d]: ", i);
                    gets(line3);
                    status = 4;
                    break;
                }
                case 4:
                {
                    printf("\nInput string[%d]: ", i);
                    gets(line4);
                    status = 5;
                    break;
                }
                case 5:
                {
                    printf("\nInput string[%d]: ", i);
                    gets(line5);
                    status = 6;
                    break;
                }
            }

        }
            printf("\n%s\n%s\n%s\n%s\n%s\n", line1, line2, line3, line4, line5);
        /*call the function of variable arguments*/
            /*memory allocation of newstr*/
            newStr = (char *)malloc(420*sizeof(char) +1);
        switch(count2)
        {
            case 2:
            {
                newStr = function(2, line1, line2);
                break;
            }
            case 3:
            {
                newStr = function(3, line1, line2, line3);
                break;
            }
            case 4:
            {
                newStr = function(4, line1, line2, line3, line4);
                break;
            }
            case 5:
            {
                newStr = function(5, line1, line2, line3, line4, line5);
            }
        }

        printf("\nThe final string is: \n");
        printf("%s", newStr);

        return 0;

    }


    char *function(int num, ...)
    {
        va_list arg_ptr;
        int b; 
        char *string;
        char *curstr;

        va_start(arg_ptr, num);     /*initialize the arg_ptr*/

        string = (char *)malloc(420*sizeof(char) + 1);
        //*string = " ";

        for(b=0; b < num; b++)
        {
            curstr = va_arg(arg_ptr, char * );
            string = strcat(string,curstr);
        }

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

        va_end(arg_ptr);

        return string;

    } 

Just included stdlib and commented *string

Have a nice day

Ashok
  • 93
  • 1
  • 1
  • 9
  • 1
    this code is totally not good (yes, maybe it will work and compile, but it would be better not to repeat mistakes made by OP in code design itself) – zoska Sep 10 '14 at 08:54
  • Butsince he is a new user, this will simplify his hardwork, anyways I did not attempt to explain the concept completely – Ashok Sep 10 '14 at 09:42