Re: Should function argument be changed in function body?



Gordon Burditt wrote:

Recently, I found one guy posted code like foo1 in one C lang forum.
I pointed out that it is not good code standard.
Surprisingly,  it seems that everybody criticised me as dogmatic.
It is claimed that changing arugment value will not affect caller code
and it saves stack size.  I understand their point
but I *do* remember that it is commmon that
argument is not suppsoed to be changed.

Now I am confused. Is there anybody who can give some explaination,
"Why it is a code rule that argument should not be changed?"


Generally I prefer not to change the formal arguments to a function,
because sooner or later I want the original value
(even if it's in a debugger only) for some reason.
I don't consider it a "rule".

There are several possible sources of confusion.
First, a function should *not* modify any of its *actual* arguments.

I'll disagree strongly here, if what you mean is that you shouldn't modify what a pointer passed in as an argument points at. How, otherwise, would you handle something like the second argument of strtol() or strtod()? Or, for that matter, any function whose purpose is to copy strings (strcpy(), strcat(), sprintf(), etc.)?

Obviously, any such argument should be clearly documented.
Occasionally, a pointed-at value is used as both input AND output
(e.g. it's a pointer to where you left off in the parsing
 and the new value is where it left off after parsing the next token,
 used occasionally in code intended to be a reentrant version of strtok()
 with a necessarily modified argument list).

Instead of

       void f(int* p) {
         ++(*p);
         }

you should write

       int f(const int* p) {
         return *p + 1;
         }


That's nice if the return value isn't being used for something else.
It is common for a function to need to return
both a value and a status of whether it worked or not.
Sometimes this can be combined into one
(e.g. return NULL if it failed, pointer to something otherwise)
but there are objections to overloading the return value like that also).
Sometimes it can't (like if you want to return an indicator of WHY it failed
and introducing more global variables used like errno is also frowned
on).

The preferred solution is to return both values in a struct as in

       div_t div(int numerator, int denominator);
       ldiv_t ldiv(long numerator, long denominator);
       lldiv_t lldiv(long long numerator, long long denominator);

You should try to avoid passing a non const pointer to a function
unless the object must be modified *in-place*.  Usually,
only container objects (arrays for example) are modified in-place --
strtol(), strtod(), strcpy(), strcat(), strtok(), etc.

C does not have a very good exception handling mechanism.
Using a function argument to return status is a hack.
Generally, you can't use the function in an expression
because you must check the status code first.

        > cat main.c
        #include <stdio.h>

        typedef struct Pair {
          int   value;
          int   status;
          } Pair;

        inline static
        Pair f(const int i) {
          Pair  p;
          p.value = i + 13;
          p.status = 0;
          return p;
          }

        int main(int argc, char* argv[]) {
          const
          Pair  p = f(42);
          fprintf(stdout, "value = %d\tstatus = %d\n",
                         p.value, p.status);
          return 0;
          }

        > gcc -Wall -std=c99 -pedantic -O2 -o main main.c
        > ./main
        value = 55      status = 0

.



Relevant Pages

  • Re: Memory Structure Pointer Problems
    ... typedef struct sta { ... char* name; ... int num_cmpnds; ... A pointer to a struct cmp is almost ...
    (comp.lang.c)
  • Re: C# - getting binary data from .lib
    ... Use Marshal.AllocHGlobal to allocate the memory and pass this IntPtr to the ... int retCode = create(id, scale, ptrImage); ... You now control the pointer returned. ...
    (microsoft.public.dotnet.framework.interop)
  • Re: Another spinoza challenge
    ... You should test against the int type's limits: ... typedef struct complex ... a pointer to an integer ... A macro is preferable because it is replaced by inline code, ...
    (comp.lang.c)
  • Re: C# - getting binary data from .lib
    ... int create(int id, int scale, unsigned char *image); ... unsigned char* ptrImage = NULL; ... // Read through entire pointer byte by byte and put into managed array ...
    (microsoft.public.dotnet.framework.interop)
  • Re: C# - getting binary data from .lib
    ... then there is a problem with the ptrImage ... int retCode = create(id, scale, ptrImage); ... You now control the pointer returned. ...
    (microsoft.public.dotnet.framework.interop)

Loading