Like Tree3Likes

Arrays and functions (again)

This is a discussion on Arrays and functions (again) within the C Programming forums, part of the General Programming Boards category; I read a (seemingly good) tutorial on how to "return arrays from functions". This is one of the methods described: ...

  1. #1
    Registered User
    Join Date
    Nov 2009
    Posts
    111

    Arrays and functions (again)

    I read a (seemingly good) tutorial on how to "return arrays from functions". This is one of the methods described: "Have the caller allocate an array and use that within the function":
    Code:
    char *func(char *buf);
    
    main (int argc, char *argv) {
    	char buf[25];
    
    	char *str = func(buf);
    
    	printf("%s\n", str);
            printf("%s\n", buf);
    }
    
    char *func(char *buf) {
    	strcpy(buf, "abcdefghifklmnopqrstuvwxy");
    
    	return buf;
    }
    However, isn't this unnecessary?

    1. Returning the pointer from the *func() function isn't necessary. The functions inserts the string into the allocated array, using the pointer passed to it. Why returning anything here? Just for the look of it?

    2. When doing it this way, couldn't you just use void methods to populate the arrays?

    3. If returning isn't necessary, why should the caller create two arrays (buf and str)? Couldn't you just do like this:
    Code:
    main (int argc, char *argv) {
    	char buf[25];
    
    	func(buf);
    	printf("%s\n", buf);
    
    	return 0;
    }
    
    char *func(char buf[25]) {
    	strcpy(buf, "abcdefghifklmnopqrstuvwxy");
    }

  2. #2
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,699
    If it looks like the return value is unnecessary in those examples that's because it is.

    For most library functions though it is a different story. Plenty of functions promise not to change the state of a buffer passed in when there is an error so that is why they return the passed in pointer when the operation is successful and something different entirely when it's not.

  3. #3
    Registered User
    Join Date
    Nov 2009
    Posts
    111
    Ok, about what I thought. But is there a reason for using f.ex. a *char function which returns nothing, or could it just as well be a void function?

  4. #4
    Registered User
    Join Date
    Jan 2010
    Posts
    412
    Quote Originally Posted by cnewbie1 View Post
    Ok, about what I thought. But is there a reason for using f.ex. a *char function which returns nothing, or could it just as well be a void function?
    Not possible. If you don't want to return anything then you have to use the void type. Anything else and you must return a value.

  5. #5
    Registered User
    Join Date
    Jun 2005
    Posts
    6,289
    There is no specific requirement that the caller use the value returned by a function.

    With functions that return error codes (for example, I/O functions that return an indication of whether they succeeded or failed) it is often a good idea to check a function's return value. But it is always a judgement call as to whether it is actually necessary.

    Given a choice between code that unnecessarily checks all return values of all called functions versus code that neglects to check some critical error status, I'll always choose the first. When doing something is optional, there will always be some idiot who neglects to do it when they really should.
    _Mike and CommonTater like this.
    Right 98% of the time, and don't care about the other 3%.

  6. #6
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by grumpy View Post
    There is no specific requirement that the caller use the value returned by a function.
    Conversely there are (rare) cases where ignoring the return value of a function is helpful.

    For example: When you use malloc() to create a struct or array, you may not want any minor errors between malloc() and free() to cause the code to bail on you as a means of ensuring the free() call is actually made. So maybe you ignore an error response from something like sscanf() or fgets() along the way.

    These situations are rare and should not be adopted as matter of course programming. Any error that can negatively affect the outcome from your code should always be checked, as Grumpy says.

  7. #7
    Registered User
    Join Date
    Jan 2010
    Posts
    412
    Quote Originally Posted by CommonTater View Post
    Conversely there are (rare) cases where ignoring the return value of a function is helpful.

    For example: When you use malloc() to create a struct or array, you may not want any minor errors between malloc() and free() to cause the code to bail on you as a means of ensuring the free() call is actually made. So maybe you ignore an error response from something like sscanf() or fgets() along the way.

    These situations are rare and should not be adopted as matter of course programming. Any error that can negatively affect the outcome from your code should always be checked, as Grumpy says.
    I sort of understand what you mean, but you can code around that without ignoring error codes. Just because a function fails doesn't mean you instantly have to 'return' from it.
    Code:
    pointer = malloc(size);
    if(pointer)
    {
     if(something_that_might_fail() != failure_code)
     {
      if(sscanf(..) == number_of_items_I_need)
      {
       // do stuff
       error_code = success_code;
      }
      else
      {
       error_code = sscanf_error;
      }
     }
     else
     {
      error_code = some_error;
     }
     free(pointer);
    }
    else
    {
     error_code = malloc_error;
    }
    return error_code;
    The only thing that would prevent free() from being called is if something causes a segfault/crash, and then you'd got bigger things to worry about anyway.

    Sidenote (which I'm prepared to get flamed for): If you don't like all those nested if's there's always 'goto'. Cleanup code is about the only thing I would consider using goto for.

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Hi Mike... I take your point but it's far simpler to just do this...
    Code:
    pointer = malloc(size);
    if(!pointer)
      return MallocErrorCode;
    
     sscanf(..);
     ProcessingErrorCode = DoProcessing();
     free(pointer);
     return ProcessingErrorCode; }
    It's less convoluted and there's a far better chance of still being able to follow it 5 years later.
    Last edited by CommonTater; 06-23-2011 at 08:11 AM.

  9. #9
    Registered User
    Join Date
    Jan 2010
    Posts
    412
    Quote Originally Posted by CommonTater View Post
    Hi Mike... I take your point but it's far simpler to just do this...
    ....
    It's less convoluted and there's a far better chance of still being able to follow it 5 years later.
    True, I guess it just depends on how specific you want your error codes to be.
    Personally I want as much detail as possible, but I wouldn't complain if I saw someone doing it your way either.

  10. #10
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by _Mike View Post
    True, I guess it just depends on how specific you want your error codes to be.
    Personally I want as much detail as possible, but I wouldn't complain if I saw someone doing it your way either.
    Nor I with yours...

    In my case I always strive to keep my code as simple and readable as possible. Error reporting is an essential part of programming... but so is being able to actually fix the problem

  11. #11
    Registered User
    Join Date
    Nov 2009
    Posts
    111
    Hm, ok. I think I understand. You could f.ex. return the pointer to an array, and in the calling code use that to check wether or not the function did it's job; if the returned pointer is null, then it didn't work. Something like that?

    In my example code:
    Code:
    char buf[25];
    char *str = func(buf);
    I could check *str to see if the function did it's job? (If for some reason I couldn't check buf[] directly...)

    Or one could make the function int, f.ex., and return 0 or 1 depending on wether the function's task was successfull or not?
    Last edited by cnewbie1; 06-23-2011 at 08:53 AM.

  12. #12
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by cnewbie1 View Post
    Hm, ok. I think I understand. You could f.ex. return the pointer to an array, and in the calling code use that to check wether or not the function did it's job; if the returned pointer is null, then it didn't work. Something like that?

    In my example code:
    I could check *str to see if the function did it's job? (If for some reason I couldn't check buf[] directly...)

    Or one could make the function int, f.ex., and return 0 or 1 depending on wether the function's task was successfull or not?
    The advantage of using the return value is that it's a go/nogo signal, whereas checking the buffer may involve complex analytical steps. Let the function do the work for you, then check the result...

    The return doesn't have to be 0 or 1 ... 0 or non-0 is adequate. When you see NULL for string pointers or with malloc() it's actually returning a pointer to 0 which is effectively a 0 return in that case.

    Code:
    char buf[25];
    char *str = func(buf);
    if (! str)
      { printf("Oh crap, that didn't work!\n\n");
         exit(StrError); }
    Or an even simpler version....

    Code:
    char buf[25];
    if (!  func(buf) )
      { printf("Oh crap, that didn't work!\n\n");
         exit(StrError); }
    Last edited by CommonTater; 06-23-2011 at 09:15 AM.

  13. #13
    Registered User
    Join Date
    Mar 2009
    Posts
    344
    A reason to many C string function return the input buffer has nothing do to with error checking. It is so you can string (ha!) several calls together :

    strcat(strcat(strcat(func(buf), " plus"), " more"), " words");

    Buffer overruns abound, but you get the idea. I haven't seen much code like this in the wild, which is probably a good thing.

    And to your #3, there's only one character array in either example. In the first example, there's an additional pointer to the array, but not two different strings. In other words, buf[1] is the exact same character as str[1] - change one and you change the other since they're the same location in memory.

  14. #14
    Registered User
    Join Date
    Nov 2009
    Posts
    111
    Quote Originally Posted by KCfromNC View Post
    And to your #3, there's only one character array in either example. In the first example, there's an additional pointer to the array, but not two different strings. In other words, buf[1] is the exact same character as str[1] - change one and you change the other since they're the same location in memory.
    Of course. Impresise wording on my part.

    However, I still don't see the point of using the extra pointer when you can just pass buff[] to the function. As per the examples above like this:
    Code:
    char buf[25];
    if (!func(buf)) { 
         printf("Oh crap, that didn't work!\n\n");
         exit(StrError); 
    }
    What would be the point of this when the above accomplishes the same without the extra pointer:
    Code:
    char buf[25];
    char *str = func(buf);
    if (!str) { 
         printf("Oh crap, that didn't work!\n\n");
         exit(StrError);
    }
    ?

    So, in a practical example, what would be a good way of doing this if I:

    - Have one method which creates an array.
    - Another method which handles file reading, sorting of the results into an array (possibly by way of calling other functions again and so on)

    What is a good way to error-check the code, if we concentrate on the calling code (which also creates the empty array) and the function which populates the array?
    Last edited by cnewbie1; 06-23-2011 at 01:48 PM.

  15. #15
    Registered User
    Join Date
    Mar 2009
    Posts
    344
    There's no difference in the two examples you posted. Either way is fine. They both check the return code of the function you called and error out if it's NULL. Personal preference if declaring an extra variable is worth it. I'd expect the compiler to generate the same code in either case. You might use variation on the second if you wanted to make several buffers and check for errors at the end

    Code:
            char buf1[25];
            char buf2[25];
            char buf3[25];
            char *str1 = func1(buf1);
            char *str2 = func2(buf2);        
            char *str3 = func3(buf3);
            if (!str1 || !str2 || !str3) { 
                 printf("Oh crap, that didn't work!\n\n");
                 exit(StrError);
            }
    Even so you could still just check if (!func1(buf1) || !func2(buf2) || !func3(buf3)), but with short-circuiting the behavior is slightly different. Or more practically, with longer, more descriptive function names the code will get harder to read.

    Note that in my examples in my earlier post, there's no error checking going on. The functions can't fail in any way so there's no point in returning success or failure. Since the return code wasn't needed for that, the functions instead return the input buffer so you could chain calls together.

    Error checking is going to depend on what how things can fail. For something like allocating an array, either it works or not. In that case, you can return NULL for the failure and a pointer to the array otherwise. For more complex tasks, you can return an int as an error code. Different values mean either success or a specific type of failure if you need that kind of information.

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

Similar Threads

  1. help with arrays and functions
    By joeman in forum C++ Programming
    Replies: 3
    Last Post: 03-27-2010, 10:17 AM
  2. arrays, functions, HELP
    By beginner1 in forum C Programming
    Replies: 4
    Last Post: 05-20-2009, 03:29 PM
  3. Arrays and Functions
    By KunoNoOni in forum Game Programming
    Replies: 12
    Last Post: 10-04-2005, 09:41 PM
  4. arrays and functions
    By dantestwin in forum C++ Programming
    Replies: 9
    Last Post: 07-08-2004, 03:30 PM
  5. arrays and functions help
    By Unregistered in forum C Programming
    Replies: 7
    Last Post: 03-02-2002, 07:05 AM

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