Getting a function to return a string

This is a discussion on Getting a function to return a string within the C Programming forums, part of the General Programming Boards category; I'm creating a program that rates bowlers based on their average score and assigns a star value for their performance(ex ...

  1. #1
    Registered User
    Join Date
    Oct 2011
    Posts
    14

    Getting a function to return a string

    I'm creating a program that rates bowlers based on their average score and assigns a star value for their performance(ex : *** means their average is between 170 and 199) and I need to create a function that allows me to do that.

    I successfully created a function that allows me to calculate the averages but the thing I'm having trouble doing is making the function output a string. As of now I have the following :


    Code:
               printf("Enter Name of Bowler\n");                    
                        scanf("%s", name);
                        printf("Enter Three Scores\n");
                        scanf("%d\n %d\n %d", &score1, &score2, &score3);
                        sum += (score1 + score2 + score3);
                        avg = findaverage(score1, score2, score3);
                        printf("%s Had An Average Of %d and a rating of %s\n", name, avg, findstars(avg));
    and then some stuff then..



    Code:
    char findstars(int d){
        int avrg = d;
        char zero[MAX] = "NR";
        char one[MAX] = "*";
        char two[MAX] = "**";
        char three[MAX] = "***";
        char four[MAX] = "****";
        char stars[MAX];
    
    
        if(avrg >= 200)
            strcpy(stars, four);
        else if(avrg >= 170 && avrg <= 199)
            strcpy(stars, three);
        else if (avrg >= 125 && avrg <= 169)
            strcpy(stars, two);
        else if (avrg >= 100 && avrg <= 124)
            strcpy(stars, one);
        else
            strcpy(stars, zero);
        return stars;
    }

    Am I defining these functions correctly? Should they be str findstars for example? Or char* findstars?


    The error I'm getting in my compiler is that my function returns an address of local variables.

    MAX is defined before everything as 100.

  2. #2
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,163
    You're declaring those strings on the stack, local to that function, but then trying to return a pointer to it (sort of), which is a nono.

    You can fix problem 1 by declaring each of those strings as static:
    Code:
    static char zero[MAX] = "NR";
    static char one[MAX] = "*";
    ...
    That way, when the function returns, the memory that those strings reside in remains valid.

    Once you're done with that, you just need to change your function declaration to return a char * instead of just a char.
    Code:
    char *findstars(ind d)[
    If you understand what you're doing, you're not learning anything.

  3. #3
    Registered User
    Join Date
    Oct 2011
    Posts
    14
    These simple fixes drive me insane.. Thank you so much. I appreciate your help. Fixed my problem!

  4. #4
    Registered User
    Join Date
    Jun 2005
    Posts
    6,266
    No, you are not doing things correctly. The error messages from your compiler are legitimate. However, in your defence, there is no completely correct approach.

    To return a string from a function, there are generally four approaches that can be used. All have advantages and disadvantages.

    The first is to return a pointer to char, and (inside the function) return a statically allocated string. For example;
    Code:
    char *first_approach(int v)
    {
         static char retval[MAX];
         if (v > 0)
              strcpy(retval, "Hello");
         else
              strcpy(retval, "Bye");
         return retval;
    }
    The disadvantage of this approach is that, if the function cannot be called multiple times in one statement. For example, printf("%s %s\n", first_approach(0), first_approach(1)) does not produce predictable output, since the static buffer is reused (and overwritten) the second time the function is called. A function that uses any static data also is quite difficult to use safely in a multithreaded environment.

    The second approach is to return a pointer to char, and dynamically allocate memory to return. For example;
    Code:
    char *second_approach(int v)
    {
          char *retval = malloc(MAX);
         if (v > 0)
              strcpy(retval, "Hello");
         else
              strcpy(retval, "Bye");
         return retval;
    }
    The disadvantage of this approach is that, if the caller does not correctly free() the returned pointer, there will be a memory leak. printf("%s %s\n", second_approach(0), second_approach(1)) produces predictable output, but also guarantees a memory leak.

    The third approach is to pass an array to the function, write to it, and return it. For example;
    Code:
    char *third_approach(int v, char *buffer)
    {
         if (v > 0)
              strcpy(buffer, "Hello");
         else
              strcpy(buffer, "Bye");
         return buffer;
    }
    The disadvantage of this approach is that the caller must know what buffer lengths are needed. If the function assumes an array of 100 char, and the caller only passes an array of 10 char, then the function may overwrite random areas of memory. That often means a crashing program. The usage is also more complex. For example, printf("%s %s\n", third_approach(0, first_buffer), third_approach(1, second_buffer)) will work, but it is also easier for the programmer to get wrong. It also has the same problems as the first approach above if the caller forgets to pass distinct arrays.

    The fourth approach is to wrap the string in a struct. For example;
    Code:
    struct Something {char buffer[MAX];};
    
    struct Something fourth_approach(int v)
    {
         struct Something retval;
         if (v > 0)
              strcpy(retval.buffer, "Hello");
         else
              strcpy(retval.buffer, "Bye");
         return retval;
    }
    The disadvantage of this approach is that the caller needs to know how to extract members from a "struct Something" in order to use them (as well as knowing buffer lengths).
    Right 98% of the time, and don't care about the other 3%.

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,660
    The problem with returning a pointer to a static local is that you must be careful not to call the function again before copying what the pointer points to, otherwise you will find that your data has mysteriously changed.

    I suggest that you write a function like this instead:
    Code:
    int determine_num_stars(int average) {
        int stars = 0;
        if (average >= 200)
            stars = 4;
        else if (average >= 170 && average <= 199)
            stars = 3;
        else if (average >= 125 && average <= 169)
            stars = 2;
        else if (average >= 100 && average <= 124)
            stars = 1;
        return stars;
    }
    Then you could use it like this:
    Code:
    /* Assume a variable named average already exists */
    char stars[MAX] = "";
    int num_stars = determine_num_stars(average);
    if (num_stars == 0) {
        strcpy(stars, "NR");
    } else {
        int i;
        for (i = 0; i < num_stars; ++i) {
            stars[i] = '*';
        }
    }
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    Registered User
    Join Date
    Oct 2011
    Posts
    14
    I see..

    Although the first reply fixed my problem I will definitely bookmark this page to make sure I know what I'm doing if I face function errors in the future. You guys have been a great help.

    One last question however.

    If I want to return multiple types of data (2 strings and number) what would I declare the function as? Both or just one? I need to create a function that returns the name, average, and rating of each bowler. Should I just translate the integer average into a string?

  7. #7
    Registered User
    Join Date
    Jun 2005
    Posts
    6,266
    Quote Originally Posted by uural4792 View Post
    If I want to return multiple types of data (2 strings and number) what would I declare the function as? Both or just one? I need to create a function that returns the name, average, and rating of each bowler. Should I just translate the integer average into a string?
    The whole purpose of (non-const) pointer arguments is to allow the caller to supply something that a function can change. So, if you want a function that returns two strings and a number, one approach is that the function accepts three pointers (two char *, and one to the number) and writes to them. For example;
    Code:
    int some_function(char *first_string, char *second_string, int *number)
    {
         strcpy(first_string, "Hello");
         strcpy(second_string("Bye");
         *number = 42;
         return 26;
    }
    is perfectly acceptable. There is no universal rule that everything done by a function is reflected in a return value. The caller supplies the pointers, so knows how to access them.

    It is also possible to return a dedicated struct that contains both strings and the number.
    Right 98% of the time, and don't care about the other 3%.

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by uural4792 View Post
    I'm creating a program that rates bowlers based on their average score and assigns a star value for their performance(ex : *** means their average is between 170 and 199) and I need to create a function that allows me to do that.

    I successfully created a function that allows me to calculate the averages but the thing I'm having trouble doing is making the function output a string. As of now I have the following :
    ...
    Am I defining these functions correctly? Should they be str findstars for example? Or char* findstars?
    The error I'm getting in my compiler is that my function returns an address of local variables.
    MAX is defined before everything as 100.
    C does not know how to return an array from a function. Strings are just arrays of characters. So that ain't gonna happen. (Yeah I know... WTF??? ... but that's the way it is.)

    Here's an example to demonstrate why you should not return arrays from functions...
    Code:
    #include <stdio.h>
    
    int* MyFunction(int a, int b, int c)
      {  static int array[3];
         array[0] = a;
         array[1] = b;
         array[2] = c;
         return array;  } // return a pointer.
    
    
    int main (void)
      { int *a1, *a2;  // int pointers
    
        printf("calling a1 = MyFunction(10,20,30);\t");
        a1 = MyFunction(10,20,30);
        printf("a1 has %d %d %d\n",a1[0],a1[1],a1[2]);
    
        printf("calling a2 = MyFunction(100,200,300);\t");
        a2 = MyFunction(100,200,300);
        printf("a2 has %d %d %d\n",a2[0],a2[1],a2[2]);
    
        printf("\nLooks good, except...\t"); 
        printf("a1 now has %d %d %d\n",a1[0],a1[1],a1[2]);
    
        getchar();
        return 0; }
    Compile this and run it... Try it both with and without the static keyword.... you'll get the idea.
    If you want to modify an array from within a function, pass in a pointer to the array and work on the pointers.

  9. #9
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,673
    Quote Originally Posted by CommonTater View Post
    Compile this and run it... Try it both with and without defined behavior.... you'll get the idea.
    FTFY. The static keyword is the only reason the array is accessible in your example, and I never found examples based on undefined behavior to be very instructive. Between compilers the results can be very different. The idea to get is that returning local arrays is undefined behavior.

  10. #10
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by CommonTater
    Compile this and run it... Try it both with and without defined behavior.... you'll get the idea.
    Oh boy... talk about stepping in it.

    Whiteflags.... Editing quotations to make me appear to say something I clearly did not say is both dishonest and despicable.

    The actual sentence I used (clearly visible in post #8) was...
    Compile this and run it... Try it both with and without the static keyword.... you'll get the idea.
    (And FWIW... This is not the first time I've observed this tactic in use on these Forums!)
    Last edited by CommonTater; 11-01-2011 at 04:22 AM.

  11. #11
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,673
    The truth can be presented in a dishonest and despicable way. It was quite clear how I edited your post since I linked back to it. I've written the same thing before though. I'm glad you actually read it this time.

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,660
    Quote Originally Posted by CommonTater
    Editing quotations to make me appear to say something I clearly did not say is both dishonest and despicable.
    It is not dishonest when the revised version was explicitly termed as "fixed"... though it is still a little despicable, heh.

    whiteflags' has a point though: no point trying without the static keyword since the results of undefined behaviour is not necessarily instructive. Besides, an attempt to do so was the reason why this thread was started in the first place.

    Other than that, your example is an elaboration of certain points made in posts #4 and #5, with the recommendation to just go for a pointer parameter approach.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  13. #13
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by whiteflags View Post
    The truth can be presented in a dishonest and despicable way. It was quite clear how I edited your post since I linked back to it. I've written the same thing before though. I'm glad you actually read it this time.
    WRONG ANSWER... Trying to justify a dishonest practice is not going to let you off the hook... not by a long shot.

    KNOW THIS... I will be watching every post you make from now on and I will report you EVERY TIME you alter another person's words!

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,660
    Quote Originally Posted by CommonTater
    WRONG ANSWER... Trying to justify a dishonest practice is not going to let you off the hook... not by a long shot.
    The practice is not dishonest. Basically, whiteflags was trying to say that this:
    Compile this and run it... Try it both with and without the static keyword.... you'll get the idea.
    should have been:
    Compile this and run it... Try it both with and without defined behavior.... you'll get the idea.
    But whiteflags merely injected a little twisted humour by presenting it as a "fixed" quote, hence "FTFY" (Fixed That For You). After all, we shouldn't be all so glum around here, right?
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by laserlight View Post
    It is not dishonest when the revised version was explicitly termed as "fixed"... though it is still a little despicable, heh.
    And how many mods would descend on me if I did that?

    If you want to correct something someone has said... quote their original words then write... You should have said: "__________________"

    You do not, ever, alter a quotation and attribute to the original poster.


    whiteflags' has a point though: no point trying without the static keyword since the results of undefined behaviour is not necessarily instructive. Besides, an attempt to do so was the reason why this thread was started in the first place.
    Did you actually read what I wrote???
    Quote Originally Posted by CommonTater
    Here's an example to demonstrate why you should not return arrays from functions...
    How much more explicit do you want me to be????

    And YES there is equal value in knowing what does and does not work.
    How the heck else do you expect us to avoid making these kinds of mistakes unless we know it's not going to work as we expect?


    Other than that, your example is an elaboration of certain points made in posts #4 and #5, with the recommendation to just go for a pointer parameter approach.
    Other than that... my posting was a sincere effort to assist the OP in the thread to understand one of C's foibles and give an active demonstration so he could see it for himself.

    You cannot say the same thing for whiteflag's post which was basically a case of putting words in my mouth then lecturing me about them.

    Should I now conclude that this level of dishonesty is tolerated here?

Page 1 of 2 12 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Is it possible to return a string in a function?
    By buggerboy_3 in forum C Programming
    Replies: 7
    Last Post: 04-28-2007, 01:14 PM
  2. Replies: 6
    Last Post: 04-09-2006, 04:32 PM
  3. Return String from a function
    By muk_luk in forum C Programming
    Replies: 2
    Last Post: 03-21-2004, 11:14 AM
  4. Getting a function to return a string
    By Necrodeemer in forum C++ Programming
    Replies: 5
    Last Post: 01-17-2003, 06:43 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21