0

I am writing a simple function for my program in C that will print a usage statement and exit the program if called. I want to have the option to pass a string as the argument and have it displayed, but also to pass nothing and skip the line. My code is as follows with an example main function:

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

typedef struct opt_info
{
    char *start_path;                  /* Path to directory to start find in */
    int  name_flag;                            /* flag for the name argument */
    int  type_flag;                            /* flag for the type argument */
} opt_info;

/* function declarations */
int   process_args( opt_info *opts, int ac, char *av[] );
void  init_params( opt_info *opts );
void  print_usage();

int main ( int ac, char *av[] ) 
{
    opt_info opts;                            /* struct to store run options */
    process_args( &opts, ac, av );         /* handle the argument processing */

    printf("%s\n", opts.start_path);

    return 0;
}

int process_args( opt_info *opts, int ac, char* av[] )
{
    if ( ac < 2 )                                         /* not enough args */   
        print_usage();

    init_params( opts );                                  /* initialize opts */
    opts->start_path = av[1];                                  /* store path */

    return 0;
}

void init_params( opt_info *opts )
{
    opts->name_flag = opts->type_flag = 0;            /* set type flags to 0 */
}

void print_usage( char *message )
{
    if( message != NULL )           /* check for empty message string */
        printf( "%s\n", message );

    printf( "USAGE: pfind starting_path [-name filename-or-pattern] " );
    printf( "[-type {f|d|b|c|p|l|s}]\n" );

    exit( 1 );
}

When I compile with gcc and run the function without passing an argument (i.e. calling print_usage();) the if statement still evaluates as true and I get a line printed out with some garbage data before the "USAGE:..." is printed.

Is there something about C pointers I am missing here, and is there a better way to have this functionality?

edit: updated code to have same function calls as the code I'm having issues with, as requested. The strange thing is when I took away some of the unnecessary code (such as the call to process_args()) I did end up with a compiler warning, but this compiles on my system fine.

Luke
  • 3
  • 3
  • 3
    Please show the [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) which illustrates how you were able to compile it at all. – Weather Vane Feb 26 '16 at 21:40
  • Possible duplicate of [C/C++ Checking for NULL pointer](http://stackoverflow.com/questions/3825668/c-c-checking-for-null-pointer) – Samer Feb 26 '16 at 21:40
  • 2
    And why did you ignore the compiler warning resp. error you get? Do not wonder about strange behaviour if you intentionally invoke undefined behaviour. – too honest for this site Feb 26 '16 at 21:41
  • 2
    Enable warnings and you will see why. http://ideone.com/XWu48S – Adam Feb 26 '16 at 21:42
  • @Olaf the code compiled with no errors using `gcc -Wall -Werror -o pfind pfind.c`. What error should I be seeing? – Luke Feb 26 '16 at 21:43
  • 2
    "Nothing" is not `NULL`, which is well defined. However "Nothing" has no meaning at all. – Weather Vane Feb 26 '16 at 21:44
  • C doesn't have default parameters so you can not do `void print_usage( char *message=NULL )` like in C++ so you could call it without input but get a defaulted `NULL`, in C you need a variadic function https://en.wikipedia.org/wiki/Variadic_function#Example_in_C – user3528438 Feb 26 '16 at 21:44
  • 5
    @user3528438: Nonsense., A variandic function requires at least one argument to determine where to start. And that is very likely not the solution anyway. – too honest for this site Feb 26 '16 at 21:45
  • @WeatherVane I edited my original post with an example of how it would be called in main without an argument. – Luke Feb 26 '16 at 21:54
  • When you compile that `main` code, you should either get a warning that you haven't provided a declaration for `print_usage`, or a warning that you passed too few arguments to `print_usage`. – user3386109 Feb 26 '16 at 21:57
  • 1
    @Luke "MCVE" means an example I can copy, paste and compile as is. Complete with `#include`s and function prototypes. – Weather Vane Feb 26 '16 at 21:57
  • @WeatherVane sorry I'm very new to stack overflow! I just edited my post with code that compiles on my system (adding some stuff that I had in separate header files so that it should compile now). When I simplified it down even more I did start to get compiler errors on my computer, so it is strange that this seems to work. – Luke Feb 26 '16 at 22:20
  • @Luke one of the purposes of preparing the MVCE is exactly that: it will reveal errors before you even post it! – Weather Vane Feb 26 '16 at 22:22
  • The fault is in the prototype `void print_usage();` which does not specifiy its arguments - defeating the point of having the prototype anyway. Strangely (or not) your posted code compiles without a warning on my MSVC. I believe it is deprecated by more rigorous compilers, I could be wrong. – Weather Vane Feb 26 '16 at 22:30
  • You did not get a warning because the prototype for `print_usage` is incomplete. The prototype `void print_usage()` effectively tells the compiler to allow any number or type of arguments when calling that function. That disables the warnings about passing the wrong number or the wrong type of arguments to the function. Your function prototype should always exactly match your function definition. So in this case, the function prototype at the top of the file should have been `void print_usage(char *message);`. – user3386109 Feb 26 '16 at 22:33
  • @WeatherVane The clang compiler needs `-Weverything` to catch the problem, and the warning you get is the (almost always worthless) `[-Wmissing-prototypes]` warning. – user3386109 Feb 26 '16 at 22:37
  • @WeatherVane You are absolutely right, I must have made a mistake when altering my code. The function declarations are in a header file so I must have forgotten to update when I added the argument in my code. Thank you and user3386109 for catching the issue! – Luke Feb 26 '16 at 22:42
  • @Luke When declaring a function that takes no arguments, use the `void` keyword, e.g. `void print_usage(void);`. That way, you have a complete function prototype, and the compiler warnings will be enabled. – user3386109 Feb 26 '16 at 22:46

1 Answers1

6

Pointers are not initialized to NULL by default. In fact, no C variable is initialized until you assign a value to it.

Before you assign a value to any C variable, it exists in an undefined state, typically called a "garbage value". This is whatever happened to be at that memory location already, and is typically not 0 (or NULL).

To properly initialize message when calling print_usage(), simply pass it as a parameter:

print_usage(NULL);
Crashworks
  • 40,496
  • 12
  • 101
  • 170
  • This is probably the way best way to get the functionality I am looking for thanks! – Luke Feb 26 '16 at 21:58