Homework 3 Solutions

  1. Suppose you are writing a program that takes two command line arguments, an integer representing the number of lines of input to read and a floating point number representing the proportion of the input to write out. For example, as sample invocation might be:
    	print_lines 60 .40
    	
    Write a code fragment that you would have to place in main to convert these command line arguments to an integer and a floating point number respectively.

    Solution: C places the command line arguments in the argv array, which is an array of character strings. 60 will be represented as the character string "60" in argv[1] and .40 will be represented as the character string ".40" in argv[2]. We will need to use sscanf to convert these strings to the appropriate values:

            // first declare a couple variables to hold the result
            int num_lines;
            float proportion_to_print;
    
    	// use sscanf to do the conversions. I did not require you to type
            // check the input for correctness but I have done so in the solution
            // so that you can see how it is done. sscanf returns the number of
            // values that it was able to correctly convert, so if it converts
            // one value then the right value was input by the user; otherwise
            // an incorrect value was input by the user.
            if (sscanf(argv[1], "%d", &num_lines) != 1) {
                fprintf(stderr, "num_lines, %s, must be an integer\n", argv[1]);
                exit(1);
            }
            if (sscanf(argv[2], "%f", &proportion_to_print) != 1) {
                fprintf(stderr, "proportion, %6.2f, must be a floating point number\n", argv[2]);
                exit(1);
            }
            
    Notice that I used fprintf to print an error message to stderr. stderr is an output stream like stdout and typically the output of stderr and stdout are co-mingled. However, it is good form to write error messages to stderr rather than stdout.

  2. You are given the following declarations:
    	char first_name[10];
    	char last_name[20];
    	char save_name[10];
    	char *temp_name;
    	int length;
    	
    1. Write a statement that assigns the sum of the lengths of first name and last name to length.
                  length = strlen(first_name) + strlen(last_name);
                  
    2. Write a statement that copies first_name to save_name.
                  strcpy(save_name, first_name);
                  
    3. Write a statement that copies first_name to temp_name and that assures that memory will be allocated for temp_name.
                  temp_name = strdup(first_name);
                  
    4. Write a statement that finds the first occurence of the letter 'a' in first_name and assigns the result to temp_name.
                  temp_name = strchr(first_name, 'a');
                  
    5. Write a statement(s) that concatenates first_name and last_name and places the result in temp_name. You should make sure that temp_name points to enough memory to hold the result.
                  temp_name = (char *)malloc(strlen(first_name) + strlen(last_name) + 1);
                  strcpy(temp_name, first_name);
                  strcat(temp_name, last_name);
                  
      Notice that I added 1 to the sum of the string lengths of first_name and last_name so that there would be room for the string terminating character, \0. I also would not quibble with your answer if you provided an extra space for a blank between first_name and last_name and concatenated a blank space to the end of temp_name after copying first_name to temp_name.

  3. Write a program using the fields library that reads a file from standard input and that:
    1. prints the 6th word from each line of input or "None" if the line is shorter than 6 words,
    2. prints the total number of words in the file, and
    3. prints the smallest of the 6th words at the end of the program.
    It is not necessary to compile and execute this program although you are welcome to do so.
    #include <stdio.h>
    #include <string.h>
    #include "fields.h"
    
    main() {
      IS input;   // the input stream
      char *min_word = 0;  // minimum word found thus far
      int num_fields = 0;  // total number of fields found thus far
      
      input = new_inputstruct(0);  // get a fields struct for stdin
      while (get_line(input) >= 0) {
        num_fields += input->NF;
        if (input->NF >= 6) {
          // the instructions did not ask you to print the line number but I have
          // done so to show you how it is done
          printf("%d: %s\n", input->line, input->fields[5]);
    
          // notice that there are two ways that min_word might get set. It might
          // not have been initialized, in which case it will be a null pointer, or
          // the current word might be less than the smallest word seen so far.
          // The order of the conditional tests is important. If the comparison
          // with the null pointer is successful, the second test will not be
          // executed and the conditional will work properly. However, if the 
          // second test were placed first and min_word were null, then the
          // program would seg fault because strcmp would try to do a comparison
          // with a null pointer.
          if ((min_word == 0) || (strcmp(input->fields[5], min_word) < 0))
    	min_word = strdup(input->fields[5]);
        }
        else
          printf("%d: none\n", input->line);
      }
      printf("\nnumber of fields = %d\n", num_fields);
      if (min_word != 0)
          printf("smallest sixth word = %s\n", min_word);
      else
          printf("none of the lines was longer than 5 words\n");
    }
    

  4. Suppose you have the following declarations:
    	struct employee {
    	    int start_year;
    	    char name[15];
    	    char *occupation;
    	}
            struct person *new_employee;
    	struct person employee1;
    	char first_name[10];
    	char last_name[10];
    	struct person employee_array[5];
    
    	void person_lookup(struct person *e);
    	
    Consider each of the following statements or group of statements in isolation from the others. Place a checkmark next to any statement that would cause a compiler error and explain why it would cause a compiler error. Place an "X" next to any statement that could cause a runtime execution error and explain what type of error it might cause. Do not assume that any malloc statements have been executed before these statements are executed.
       _/ = checkmark 
    1. _/ employee1->start_year = 1960;
      Explanation: employee1 is statically allocated so the -> should be a period.
    2. person_lookup(&employee_array[3]);
      Explanation: the statement is correct because the employee_array consists of statically allocated structs. Therefore, by taking the address of the fourth entry, we are properly passing a "struct person *" to person_lookup
    3. X scanf("%d %s %s", employee1.age, employee1.name, employee1.occupation);
      Explanation: The statement will compile but at runtime we might get a seg fault because no memory has been allocated to employee1.occupation. scanf assumes that enough memory has been allocated to hold the string. It will not allocate memory for you. Hence it functions like strcpy rather than strdup.
    4. _/ person_lookup(employee1);
      Explanation: We are trying to pass a "struct person" to a function that is expecting a "struct person *".
    5. X strcpy(employee1.name, first_name); strcat(employee1.name, last_name);
      Explanation: These statements will compile but name is only 15 characters long and the combined first and last names could be 19 characters long, thus causing memory to be overwritten. The reason that the combined first and last names can only be 19 characters long is that the maximum length of each of the character strings is 9 characters since the 10th character is needed for the string delimiter, \0. Hence their maximum combined length is 18 characters plus the string delimiter character, for a total of 19 characters.
    6. X strcpy(employee1.occupation, first_name);
      Explanation: The statement will compile but no memory has been allocated for occupation.
    7. _/ new_employee = malloc(struct person);
      Explanation: There are two problems with this statement. First the return value of malloc has not been cast to a "char *". Second, we need to use the sizeof function to get the size of a person struct. The correct statement would read:
                  new_employee = (char *)malloc(sizeof(struct person));
                  
    8. employee_array[2].start_year = 2003;
      Explanation: The statement is correct. The structs in employee_array are statically allocated and hence their fields should be referenced using the '.' operator.