The main function has signature int main(int argc, char**argv); so you can use the argc (which is positive) & argv arguments. The argv array is guaranteed to have argc+1 elements. The last is always NULL. The others are non-nil, non-aliased zero-byte terminated strings. Notice that often some shell is globbing the arguments before your program is started by execve(2): see glob(7).
For example, if you type (in a Linux terminal) myprog -i a*.c -o foo.txt and if at the moment you type that the shell has expanded (by globbing) a*.c into a1.c and a2.c (because these are the only files whose name start with a and have a .c suffix in the current directory), your myprog executable main program is called with
argc==6
argv[0] containing "myprog" (so you could test that strcmp(argv[0],"myprog") == 0)
argv[1] containing "-i"
argv[2] containing "a1.c"
argv[3] containing "a2.c"
argv[4] containing "-o"
argv[5] containing "foo.txt"
argv[6] being the NULL pointer
In addition you are guaranteed (by the kernel doing the execve(2)) that all the 6 argv pointers are distinct, non-aliasing, and non-overlapping.
GNU libc gives you several ways to parse these arguments: getopt & argp. getopt is standardized in POSIX (but GNU gives you also the very useful getopt_long(3))
I strongly suggest you to follow GNU conventions: accept at least --help and --version
The fact that e.g. -f is for some option and not a file name is often conventional (but see use of -- in program arguments). If you happen to really want a file named -f (which is a very bad idea), use ./-f
Several shells have autocompletion. You need to configure them for that (and you might configure them even for your own programs).