Randalfien
Randalfien

Reputation: 396

How to check for type in C in function

I'm making a function that takes two pointer to strings as arguments.It works fine, as long as you pass it valid arguments. I wanna know how to check that these pointer are valid and not for example two random ints . How do I do that?

char ** LCS ( char * s1, char * s2)    //thats the function

...

LCS(0,0) //...awful crash.. How do I avoid it?

Upvotes: 0

Views: 281

Answers (9)

Arkku
Arkku

Reputation: 42139

Does it make sense for someone to call your function with NULL arguments? If not, you should disallow NULL arguments in the contract of your function, e.g. by adding a comment above the declaration saying that it only works on valid, non-NULL arguments. In other words, anyone who uses your function agrees not to give NULL arguments; it's then their responsibility to check against this, not yours.

If it does make sense for either or both of the arguments to be NULL, then you need to decide on how your function behaves in that case and implement it thus. In this case you are agreeing to support NULL arguments and do something sensible with them, and therefore it becomes your responsibility to check for this and act accordingly (e.g. if (s1 == NULL)).

If you cannot think of any sensible behaviour for NULL arguments, then go with the first option and disallow them altogether. If you do this, then your example call LCS(0,0); is in breach of contract (i.e. passes NULL pointers when the function does not agree to accept them) and should be removed. In a more complex scenario if you are passing the arguments from variables and there is a chance that those variables point to NULL, then you must check before calling LCS, e.g. if (v1 && v2) { LCS(v1,v2); } else { … }.

To track possible errors relating to this, you could use assert to check, e.g.:

#include <assert.h>

char **LCS (char *s1, char *s2) {
    assert(s1);
    assert(s2);
    …
}

This will cause your program to exit if s1 or s2 is NULL, unless NDEBUG was defined before including assert.h (in which case the assertions do nothing). So the assertions are a way to check, during development, that the caller is not giving you NULL arguments but it's still an error if they do.

As for other invalid pointers, you cannot really even check reliably, e.g. there's no way of knowing whether the caller has a really strange string or if they just passed the wrong address. This, too, is their responsibility to avoid, and LCS should simply assume that the caller is giving you valid data. Of course if you have additional restrictions, e.g. maximum length of the argument strings, then you must make these restrictions clear to the caller (i.e. specify the contract for the function, “this function does X [your responsibility as the implementor of LCS] provided that … [their responsibilities as the user of LCS]”). This applies to all programming, for example the C standard specifies how the language itself and the standard library functions must be used (e.g. cannot divide by zero, argument strings for strcpy cannot overlap, etc).

Upvotes: 1

nategoose
nategoose

Reputation: 12392

You can't really do this. First, if a programmer passes in arbitrary integers cast as pointers then they may actually be valid pointers within your address space -- they might even point to null terminated character arrays (in fact, if they are within your address space they will point because the data there will be treated as characters and at some point there will be a 0 byte).

You can test for several invalid (for applications, anyway) pointer values, including NULL and maybe even any value that would point to the first page of the processes address space (it is usually not mapped and can safely be assumed not to be valid). On some systems there are other pages that are not ever mapped (like the last page). Some systems also have ways ask about the memory map of a process (/proc/self/maps under Linux) which you could (with a lot of trouble) look at and see if the pointer was within a mapped area with the appropriate access.

If you are using a *nix system something you could do would be to register a signal handler for SIGSEGV which gets raised when your program tries to access memory that it shouldn't be accessing. Then you could catch that and with some work figure out what has happened. Another thing you could do would be to call a system call that takes a pointer and use the pointers you have been passed as arguments and see if it fails (with errno == EFAULT). This is probably not good since system calls do things besides just testing memory for read and/or write permissions. You could always write the first byte pointed to by a pointer to /dev/null or /dev/zero (using the write system call, not stdio functions) to determine if you have read permissions, but if you read a byte from /dev/zero or /dev/random into the first byte pointed to (using the read system calls, not stdio functions), but if the data at that area is important then you would have over written a byte of it. If you were to have tried to save a copy of that data into a local variable so that you could restore it after the test then you might have caused an error when you read from it within your program. You could get elaborate and write it out and then read it back in to test both access rights, though, but this is getting complicated.

Your best bet is to just rely on the user of your function to do the right thing.

Upvotes: 0

HowdyHowdyHowdy
HowdyHowdyHowdy

Reputation: 1211

First, as everyone else has said:

  • Check for NULL parameters

Everything else is heuristics and careful programming

You can provide a function prototype to your callers and turn the warnings to 11: (at least -Werror -Wall and -Wextra for gcc). This will cause a compilation error if a parameter of an improper type is passed in. It doesn't help if the caller first casts his parameters to char *s (e.g. LCS((char*)1, (char*)1 )

You could call strlen on your arguments, but if the values are non-NULL but still illegal values, the strlen could crash.

You could attempt to see if the pointers are in valid segments for the program. This is not portable, and still not foolproof.

So, to summarize, check for NULL, and turn the warnings to 11. This is what's done in practice.

Upvotes: 0

pmg
pmg

Reputation: 108938

With documentation and by following the C motto: "trust the programmer".

/* s1 and s2 must be both valid pointers to null-terminated strings
** otherwise the behaviour is undefined */
char ** LCS ( char * s1, char * s2);

Upvotes: 2

Natansh
Natansh

Reputation: 31

The ideology of C revolves around the principle that 'The programmer knows what (s)he is doing.' Half the reason as to why C is so lightweight and speedy, is because it doesn't perform such type checks.

If you really need to perform such checks, you might be better off in C++, using references (which are assured to be non-null) instead of pointers.

Upvotes: 0

onemasse
onemasse

Reputation: 6584

You can implement your own type checking using a struct like this. But you could also just use a language with proper type checking. :)

typedef struct Var {
    enum Type { int, ptr, float ... } type;

    union {
        int Int;
        void *Ptr;
        float Float;
        ...
    } data;
} Var; 

Upvotes: 0

John Bode
John Bode

Reputation: 123488

The best you can do is check against NULL (0). Otherwise, there's no standard way to tell whether a non-NULL pointer value is valid. There may be some platform-specific hacks available, but in general this problem is dealt with by documentation and good memory management hygiene.

Upvotes: 0

Max
Max

Reputation: 3180

In C, I'm afraid you have to just be careful and hope the programmers know what to do.

In this case, 0 (zero, null, NULL) is valid input for the function.

Normally is that case, you would at least protect the function by checking if the input is valid.

for example ...

char** LCS (char *s1, char *s2 )
{
  if ( s1 == 0 )
    return ...;
  if ( s2 == 0 )
    return ...;

  if ( strlen( s1 ) == 0 )
     return ...

/// do something ...
}

Upvotes: 0

Adam Vandenberg
Adam Vandenberg

Reputation: 20651

In the body of the function, check:

if ((s1==NULL) || (s2==NULL)) {
  /* Do something to indicate bad parameters */
}

Upvotes: 2

Related Questions