PERSONAL - Cheat Sheet

GRA Cheat Sheet

Using getopt()

#include <unistd.h> // needed for getopt()
#include <getopt.h> // needed for getopt_long()

// standard getopt, which can only parse single-character command-line arguments of type "-a" (with opt. argument)
int getopt(int argc, char * const argv[],
           const char *optstring);

extern char *optarg;
extern int optind, opterr, optopt;

// getopt_long, which allows for multiple-character command-line arguments of type "--arg"
int getopt_long(int argc, char *const argv[],
                const char *optstring,
                const struct option *longopts, int *longindex);

// necessary for getopt_long:
struct option {
    const char *name; // name of argument as string, e.g. "help" for --help
    int         has_arg; // no_argument, required_argument (if arg req) or optional_argument
    int        *flag; // if NULL, getopt_long() returns val, otherwise return 0 and store val at pointer address
    int         val; // a.k.a. shortname (abbrev.); value to return or to load into variable pointed to by flag address
};

// USEFUL: get program name with "basename(argv[0])" -> #define USAGE_FMT  "%s [-v] [-f hexflag] [-i inputfile] [-o outputfile] [-h]"
#include <unistd.h> // needed for getopt()
#include <getopt.h> // needed for getopt_long()

// standard getopt, which can only parse single-character command-line arguments of type "-a" (with opt. argument)
int getopt(int argc, char * const argv[],
           const char *optstring);

extern char *optarg;
extern int optind, opterr, optopt;

// getopt_long, which allows for multiple-character command-line arguments of type "--arg"
int getopt_long(int argc, char *const argv[],
                const char *optstring,
                const struct option *longopts, int *longindex);

// necessary for getopt_long:
struct option {
    const char *name; // name of argument as string, e.g. "help" for --help
    int         has_arg; // no_argument, required_argument (if arg req) or optional_argument
    int        *flag; // if NULL, getopt_long() returns val, otherwise return 0 and store val at pointer address
    int         val; // a.k.a. shortname (abbrev.); value to return or to load into variable pointed to by flag address
};

// USEFUL: get program name with "basename(argv[0])" -> #define USAGE_FMT  "%s [-v] [-f hexflag] [-i inputfile] [-o outputfile] [-h]"

getopt() Conventions

// example code using getopt()
// source: https://www.gnu.org/software/libc/manual/html_node/Example-of-Getopt.html
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>

int main(int argc, char** argv) {
    int aFlag = 0;
    int bFlag = 0;
    char* cValue = NULL;
    int index;
    int c;
    opterr = 0; // handle '?' case
    // a and b don't take any arguments, whereas c does, as indicated by the ':'
    while ((c = getopt(argc, argv, "abc:")) != -1) {
        switch (c) {
            case 'a':
                aFlag = 1;
                break;
            case 'b':
                bFlag = 1;
                break;
            case 'c':
                cValue = optarg;
                break;
            case '?':
                if (optopt == 'c') fprintf (stderr, "Option -%c requires an argument.\n", optopt);
                else if (isprint(optopt)) fprintf (stderr, "Unknown option `-%c'.\n", optopt);
                else fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
                return EXIT_FAILURE;
            default:
                abort();
        }
    }

    printf("aflag = %d, bflag = %d, cvalue = %s\n", aFlag, bFlag, cValue);

    for (index = optind; index < argc; index++) printf ("Non-option argument %s\n", argv[index]);
    return EXIT_SUCCESS;
}
// example code using getopt()
// source: https://www.gnu.org/software/libc/manual/html_node/Example-of-Getopt.html
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>

int main(int argc, char** argv) {
    int aFlag = 0;
    int bFlag = 0;
    char* cValue = NULL;
    int index;
    int c;
    opterr = 0; // handle '?' case
    // a and b don't take any arguments, whereas c does, as indicated by the ':'
    while ((c = getopt(argc, argv, "abc:")) != -1) {
        switch (c) {
            case 'a':
                aFlag = 1;
                break;
            case 'b':
                bFlag = 1;
                break;
            case 'c':
                cValue = optarg;
                break;
            case '?':
                if (optopt == 'c') fprintf (stderr, "Option -%c requires an argument.\n", optopt);
                else if (isprint(optopt)) fprintf (stderr, "Unknown option `-%c'.\n", optopt);
                else fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
                return EXIT_FAILURE;
            default:
                abort();
        }
    }

    printf("aflag = %d, bflag = %d, cvalue = %s\n", aFlag, bFlag, cValue);

    for (index = optind; index < argc; index++) printf ("Non-option argument %s\n", argv[index]);
    return EXIT_SUCCESS;
}
// example code using getopt_long()
// source: https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

/* Flag set by ‘--verbose’. */
static int verbose_flag;

int main (int argc, char **argv) {
  int c;

  while (1) {
      static struct option long_options[] =
        {
          /* These options set a flag. */
          {"verbose", no_argument,       &verbose_flag, 1},
          {"brief",   no_argument,       &verbose_flag, 0},
          /* These options don’t set a flag.
             We distinguish them by their indices. */
          {"add",     no_argument,       0, 'a'},
          {"append",  no_argument,       0, 'b'},
          {"delete",  required_argument, 0, 'd'},
          {"create",  required_argument, 0, 'c'},
          {"file",    required_argument, 0, 'f'},
          {0, 0, 0, 0} // required!
        };
      
      /* getopt_long stores the option index here. */
      int option_index = 0;
      c = getopt_long (argc, argv, "abc:d:f:", long_options, &option_index);
      
      /* Detect the end of the options. */
      if (c == -1) break;
      switch (c) {
        case 0: // long option found
          /* If this option set a flag, do nothing else now. */
          if (long_options[option_index].flag != 0) break;
          printf("option %s", long_options[option_index].name);
          if (optarg) printf (" with arg %s", optarg);
          printf("\n");
          break;

        // standard getopt() procedure
        case 'a':
          puts("option -a\n");
          break;

        case 'b':
          puts("option -b\n");
          break;

        case 'c':
          printf("option -c with value `%s'\n", optarg);
          break;

        case 'd':
          printf("option -d with value `%s'\n", optarg);
          break;

        case 'f':
          printf("option -f with value `%s'\n", optarg);
          break;

        case '?':
          /* getopt_long already printed an error message. */
          break;

        default:
          abort();
        }
    }

  /* Instead of reporting ‘--verbose’
     and ‘--brief’ as they are encountered,
     we report the final status resulting from them. */
  if (verbose_flag) puts ("verbose flag is set");

  /* Print any remaining command line arguments (not options). */
  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc) printf ("%s ", argv[optind++]);
      putchar ('\n');
    }

  return EXIT_SUCCESS;
}
// example code using getopt_long()
// source: https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

/* Flag set by ‘--verbose’. */
static int verbose_flag;

int main (int argc, char **argv) {
  int c;

  while (1) {
      static struct option long_options[] =
        {
          /* These options set a flag. */
          {"verbose", no_argument,       &verbose_flag, 1},
          {"brief",   no_argument,       &verbose_flag, 0},
          /* These options don’t set a flag.
             We distinguish them by their indices. */
          {"add",     no_argument,       0, 'a'},
          {"append",  no_argument,       0, 'b'},
          {"delete",  required_argument, 0, 'd'},
          {"create",  required_argument, 0, 'c'},
          {"file",    required_argument, 0, 'f'},
          {0, 0, 0, 0} // required!
        };
      
      /* getopt_long stores the option index here. */
      int option_index = 0;
      c = getopt_long (argc, argv, "abc:d:f:", long_options, &option_index);
      
      /* Detect the end of the options. */
      if (c == -1) break;
      switch (c) {
        case 0: // long option found
          /* If this option set a flag, do nothing else now. */
          if (long_options[option_index].flag != 0) break;
          printf("option %s", long_options[option_index].name);
          if (optarg) printf (" with arg %s", optarg);
          printf("\n");
          break;

        // standard getopt() procedure
        case 'a':
          puts("option -a\n");
          break;

        case 'b':
          puts("option -b\n");
          break;

        case 'c':
          printf("option -c with value `%s'\n", optarg);
          break;

        case 'd':
          printf("option -d with value `%s'\n", optarg);
          break;

        case 'f':
          printf("option -f with value `%s'\n", optarg);
          break;

        case '?':
          /* getopt_long already printed an error message. */
          break;

        default:
          abort();
        }
    }

  /* Instead of reporting ‘--verbose’
     and ‘--brief’ as they are encountered,
     we report the final status resulting from them. */
  if (verbose_flag) puts ("verbose flag is set");

  /* Print any remaining command line arguments (not options). */
  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc) printf ("%s ", argv[optind++]);
      putchar ('\n');
    }

  return EXIT_SUCCESS;
}

Converting Strings to Numbers

#include <stdlib.h>

// string -> integer (long, long long)
long strtol(const char *restrict nptr, char **restrict endptr, int base);
long long strtoll(const char *restrict nptr, char **restrict endptr, int base);

// string -> floating point number (float, double, long double)
double strtod(const char *restrict nptr, char **restrict endptr);
float strtof(const char *restrict nptr, char **restrict endptr);
long double strtold(const char *restrict nptr, char **restrict endptr);
#include <stdlib.h>

// string -> integer (long, long long)
long strtol(const char *restrict nptr, char **restrict endptr, int base);
long long strtoll(const char *restrict nptr, char **restrict endptr, int base);

// string -> floating point number (float, double, long double)
double strtod(const char *restrict nptr, char **restrict endptr);
float strtof(const char *restrict nptr, char **restrict endptr);
long double strtold(const char *restrict nptr, char **restrict endptr);
// example for strtol()
// source: man 3 strtol
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>

int main(int argc, char *argv[])
{
   int base;
   char *endptr, *str;
   long val;

   if (argc < 2) {
       fprintf(stderr, "Usage: %s str [base]\n", argv[0]);
       exit(EXIT_FAILURE);
   }

   str = argv[1];
   base = (argc > 2) ? atoi(argv[2]) : 0; // ew, atoi

   errno = 0;    /* To distinguish success/failure after call */
   val = strtol(str, &endptr, base);

   /* Check for various possible errors. */

   if (errno != 0) {
       perror("strtol");
       exit(EXIT_FAILURE);
   }

   if (endptr == str) {
       fprintf(stderr, "No digits were found\n");
       exit(EXIT_FAILURE);
   }

   /* If we got here, strtol() successfully parsed a number. */

   printf("strtol() returned %ld\n", val);

   if (*endptr != '\0')        /* Not necessarily an error... */
       printf("Further characters after number: \"%s\"\n", endptr);

   exit(EXIT_SUCCESS);
}
// example for strtol()
// source: man 3 strtol
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>

int main(int argc, char *argv[])
{
   int base;
   char *endptr, *str;
   long val;

   if (argc < 2) {
       fprintf(stderr, "Usage: %s str [base]\n", argv[0]);
       exit(EXIT_FAILURE);
   }

   str = argv[1];
   base = (argc > 2) ? atoi(argv[2]) : 0; // ew, atoi

   errno = 0;    /* To distinguish success/failure after call */
   val = strtol(str, &endptr, base);

   /* Check for various possible errors. */

   if (errno != 0) {
       perror("strtol");
       exit(EXIT_FAILURE);
   }

   if (endptr == str) {
       fprintf(stderr, "No digits were found\n");
       exit(EXIT_FAILURE);
   }

   /* If we got here, strtol() successfully parsed a number. */

   printf("strtol() returned %ld\n", val);

   if (*endptr != '\0')        /* Not necessarily an error... */
       printf("Further characters after number: \"%s\"\n", endptr);

   exit(EXIT_SUCCESS);
}

File I/O

// example code
// source: ERA W07

// read contents of file to string
static char* read_file(const char* path) {
    char* string = NULL;
    FILE* file;
    
    // check if file can be opened with reading permissions
    if (!(file = fopen(path, "r"))) {
        perror("Error opening file!");
        return NULL;
    }
    
    // check if file statistics can be retrieved and stored in statbuf
    struct stat statbuf;
    if (fstat(fileno(file), &statbuf)) {
        fprintf(stderr, "Error retrieving file stats!\n");
        goto cleanup;
    }
    
    // check if file is regular and has valid size
    if (!S_ISREG(statbuf.st_mode) || statbuf.st_size <= 0) {
        fprintf(stderr, "Error processing file: Not a regular file or invalid size!\n");
        goto cleanup;
    }
    
    // allocate sufficient bytes for file contents and extra null-byte to terminate string
    if (!(string = malloc(statbuf.st_size + 1))) {
        fprintf(stderr, "Error reading file: Could not allocate enough memory!\n");
        goto cleanup;
    }
    
    // read contents and check if number of bytes read matches with file size in bytes
    if (fread(string, 1, statbuf.st_size, file) != (size_t) statbuf.st_size) {
        fprintf(stderr, "Error reading file!\n");
        free(string);
        string = NULL;
        goto cleanup;
    }
    
    // append null byte
    string[statbuf.st_size] = '\0';
    
    // perform end-of-function / failure cleanup to prevent memory leak (!)
    cleanup:
    if (file) fclose(file);
    
    // returns the string containing the data from the file or NULL if something went wrong
    return string;
}

// write string to file
static void write_file(const char* path, const char* string) {
    FILE* file;
    
    // check if file can be opened with writing permissions
    if (!(file = fopen(path, "w"))) {
        perror("Error opening file!");
        return;
    }

    // attempt to write contents and check if number of bytes written matches with file size in bytes
    const size_t stringlength = strlen(string);
    if (fwrite(string, 1, stringlen, file) != stringlen) {
        fprintf(stderr, "Error writing to file!\n");
    }
    
    // once again, close the file when done!
    fclose(file);
}
// example code
// source: ERA W07

// read contents of file to string
static char* read_file(const char* path) {
    char* string = NULL;
    FILE* file;
    
    // check if file can be opened with reading permissions
    if (!(file = fopen(path, "r"))) {
        perror("Error opening file!");
        return NULL;
    }
    
    // check if file statistics can be retrieved and stored in statbuf
    struct stat statbuf;
    if (fstat(fileno(file), &statbuf)) {
        fprintf(stderr, "Error retrieving file stats!\n");
        goto cleanup;
    }
    
    // check if file is regular and has valid size
    if (!S_ISREG(statbuf.st_mode) || statbuf.st_size <= 0) {
        fprintf(stderr, "Error processing file: Not a regular file or invalid size!\n");
        goto cleanup;
    }
    
    // allocate sufficient bytes for file contents and extra null-byte to terminate string
    if (!(string = malloc(statbuf.st_size + 1))) {
        fprintf(stderr, "Error reading file: Could not allocate enough memory!\n");
        goto cleanup;
    }
    
    // read contents and check if number of bytes read matches with file size in bytes
    if (fread(string, 1, statbuf.st_size, file) != (size_t) statbuf.st_size) {
        fprintf(stderr, "Error reading file!\n");
        free(string);
        string = NULL;
        goto cleanup;
    }
    
    // append null byte
    string[statbuf.st_size] = '\0';
    
    // perform end-of-function / failure cleanup to prevent memory leak (!)
    cleanup:
    if (file) fclose(file);
    
    // returns the string containing the data from the file or NULL if something went wrong
    return string;
}

// write string to file
static void write_file(const char* path, const char* string) {
    FILE* file;
    
    // check if file can be opened with writing permissions
    if (!(file = fopen(path, "w"))) {
        perror("Error opening file!");
        return;
    }

    // attempt to write contents and check if number of bytes written matches with file size in bytes
    const size_t stringlength = strlen(string);
    if (fwrite(string, 1, stringlen, file) != stringlen) {
        fprintf(stderr, "Error writing to file!\n");
    }
    
    // once again, close the file when done!
    fclose(file);
}
// some more example code
// source: wikipedia
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char buffer[5];
    FILE* fp = fopen("myfile", "rb");

    if (fp == NULL) {
        perror("Failed to open file \"myfile\"");
        return EXIT_FAILURE;
    }

    for (int i = 0; i < 5; i++) {
        int rc = getc(fp);
        if (rc == EOF) {
            fputs("An error occurred while reading the file.\n", stderr);
            return EXIT_FAILURE;
        }
        
        buffer[i] = rc;
    }

    fclose(fp);

    printf("The bytes read were... %02x %02x %02x %02x %02x\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);

    return EXIT_SUCCESS;
}
// some more example code
// source: wikipedia
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char buffer[5];
    FILE* fp = fopen("myfile", "rb");

    if (fp == NULL) {
        perror("Failed to open file \"myfile\"");
        return EXIT_FAILURE;
    }

    for (int i = 0; i < 5; i++) {
        int rc = getc(fp);
        if (rc == EOF) {
            fputs("An error occurred while reading the file.\n", stderr);
            return EXIT_FAILURE;
        }
        
        buffer[i] = rc;
    }

    fclose(fp);

    printf("The bytes read were... %02x %02x %02x %02x %02x\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);

    return EXIT_SUCCESS;
}

Compiler Options

Debugging with GDB

$ gdb ./main
$ b main
$ layout regs
$ r
$ s
$ gdb ./main
$ b main
$ layout regs
$ r
$ s

ASM File Layout

.intel_syntax noprefix	// ensure intel syntax because intel rules and AT&T drools
.global function_name	// make function externally visible to C program

.text
function_name:
    ...
    ret
.intel_syntax noprefix	// ensure intel syntax because intel rules and AT&T drools
.global function_name	// make function externally visible to C program

.text
function_name:
    ...
    ret

Makefile Layout

# Standard Rule layout

# Target contains prerequisites "prerequisite1", "prerequisite2", ...
# These prerequisites can be source code files or other targets.
# The recipes are shell commands.

target : prerequisite1 prerequisite2 ...
    recipe1
    recipe2
# Standard Rule layout

# Target contains prerequisites "prerequisite1", "prerequisite2", ...
# These prerequisites can be source code files or other targets.
# The recipes are shell commands.

target : prerequisite1 prerequisite2 ...
    recipe1
    recipe2
# Layout of a typical Makefile to compile any C program
# Source: https://opensource.com/article/18/8/what-how-makefile

# Usage:
# make      			# compile all binary
# make clean			# remove ALL binaries and objects

.PHONY = all clean 		# define phony targets all and clean

CC = gcc			# compiler to use

LINKERFLAG = -lm		# define flags to be used with GCC in a recipe

SRCS := $(wildcard *.c)		# function for filenames: stores all files with extension .c
BINS := $(SRCS:%.c=%)		# substitution reference: output files have same names as source files, without .c extension

# phony target all calls values in${BINS} as individual targets
all: ${BINS}

# Equivalent to:
# foo: foo.o
# 	@echo "Checking.."
# 	gcc -lm foo.o -o foo

%: %.o
    @echo "Checking.."
    ${CC} ${LINKERFLAG} $< -o $@

# Equivalent to:
# foo.o: foo.c
# 	@echo "Creating object.."
# 	gcc -c foo.c

%.o: %.c
    @echo "Creating object.."
    ${CC} -c $<

# remove binaries and object files
clean:
    @echo "Cleaning up..."
    rm -rvf *.o ${BINS}
# Layout of a typical Makefile to compile any C program
# Source: https://opensource.com/article/18/8/what-how-makefile

# Usage:
# make      			# compile all binary
# make clean			# remove ALL binaries and objects

.PHONY = all clean 		# define phony targets all and clean

CC = gcc			# compiler to use

LINKERFLAG = -lm		# define flags to be used with GCC in a recipe

SRCS := $(wildcard *.c)		# function for filenames: stores all files with extension .c
BINS := $(SRCS:%.c=%)		# substitution reference: output files have same names as source files, without .c extension

# phony target all calls values in${BINS} as individual targets
all: ${BINS}

# Equivalent to:
# foo: foo.o
# 	@echo "Checking.."
# 	gcc -lm foo.o -o foo

%: %.o
    @echo "Checking.."
    ${CC} ${LINKERFLAG} $< -o $@

# Equivalent to:
# foo.o: foo.c
# 	@echo "Creating object.."
# 	gcc -c foo.c

%.o: %.c
    @echo "Creating object.."
    ${CC} -c $<

# remove binaries and object files
clean:
    @echo "Cleaning up..."
    rm -rvf *.o ${BINS}
# Rewritten, assuming only one file (foo.c) is used

.PHONY = all clean

CC = gcc

LINKERFLAG = -lm

SRCS := foo.c
BINS := foo

all: foo

foo: foo.o
    @echo "Checking.."
    gcc -lm foo.o -o foo

foo.o: foo.c
    @echo "Creating object.."
    gcc -c foo.c

clean:
    @echo "Cleaning up..."
    rm -rvf foo.o foo
# Rewritten, assuming only one file (foo.c) is used

.PHONY = all clean

CC = gcc

LINKERFLAG = -lm

SRCS := foo.c
BINS := foo

all: foo

foo: foo.o
    @echo "Checking.."
    gcc -lm foo.o -o foo

foo.o: foo.c
    @echo "Creating object.."
    gcc -c foo.c

clean:
    @echo "Cleaning up..."
    rm -rvf foo.o foo

Summary by Flavius Schmidt, ge83pux, 2023.
https://home.in.tum.de/~scfl