University of California San Diego Department of Electrical and Computer Engineering ECE 15 Final Exam Tuesday, March 21, 2017 3:00 p.m. 6:00 p.m. Room 109, Pepper Canyon Hall Name Class Account: ee15w Student ID Number Signature Grading 1. 6 points INSTRUCTIONS The exam consists of six problems worth a total of 100 points. Write your answers in the spaces provided. If you need extra space, please use the back of the previous page. Partial credit will be given only for substantial progress. Good luck! 2. 9 points 3. 12 points 4. 20 points 5. 18 points 6. 35 points TOTAL
Problem 1 (6 points) Consider the following simple function: int isequal(int x, int y) if (x == y) return 1; else return 0; Rewrite this function below without using the keywords if and else. You are also required to use the keyword return exactly once. int isequal(int x, int y) Hint: Use the ternary conditional operator? :.
Problem 2 (9 points) Consider the following simple function: void foo(int n) if (n %2 == 0) printf("2"); else if (n %3 == 0) printf("3"); else if (n %5 == 0) printf("5"); else if (n %7 == 0) printf("7"); else printf("no divisors!"); Rewrite this function below without using the keywords else or switch. You also cannot use the keyword if more than once in your source code. void foo(int n) Hint: Store the numbers 2, 3, 5, 7 in an array, and process this array in a loop.
Problem 3 (12 points) Consider the following C program that contains global, local, and static variables: #include <stdio.h> #define N 5 int i,j; /* global variables */ /* Forward function declarations */ void f1(int); int *f2(int *); int f3(int); int main() int a = N; int myarray[n] = ; int *p = myarray; for (i = 0; i < N; i++) int j = i % 2; /* local variable */ if (i == 0) f1(a); else if (j == 0) p = f2(p); else if (j == 1) a = f3(a); *p = a; // most of the action happens here! for (j = 0; j < N; j++) printf("%d ", myarray[j]); printf("\n"); return 0; void f1(int a) a += ++j; int *f2(int *a) return a += j++; int f3(int a) static int i = 0; return a+i++;
You should assume that the program compiles and runs successfully. Your task is to determine the output of the program. Write your answers in the space provided. Explanations are not required.
Problem 4 (20 points) In this problem, you are given several exercises, each containing a short C program. You should assume that the program compiles and runs successfully. Your task is to determine, in each case, the output of the program. Write your answers in the space provided. Explanations are not required. a. #include <stdio.h> int main() int x,y,z; x = y = z = 1; ++x && ++y ++z; printf("a. x = %d, y = %d, z = %d\n", x,y,z); x = y = z = 1; x += y += z; printf("b.? = %d, ", (x < y)? x++ : y++); printf("x = %d, y = %d\n", x,y); x = 1; y = 2; z = 3; printf("c.? = %s\n", (z > y > x)? "True" : "False"); return 0; The program output is: a. x =, y =, z = b.? =, x =, y = c.? =
b. #include <stdio.h> void foo(int, int *, int *); int main() int a[3] = 1729,42,0; int *ptr = a; int x = 10, z = 12; foo(x, ptr, &z); printf("x = %d, z = %d\n", x,z); printf("array elements: %d, %d, %d\n", a[0],a[1],a[2]); return 0; void foo(int x, int *y, int *z) printf("x = %d, z = %d\n", x,*z); x++; (*z)++; printf("x = %d, z = %d\n", x,*z); x = *(y++); *z = *(y++); (*y)++; The program output is: x =, z = x =, z = x =, z = Array elements:
c. #include <stdio.h> int main() int i,j; int M[5][5] = 0; for (i = 0; i < 5; i++) for (j = 0; j < 5; j++) if ((i*j) % 2) *(*(M+i)+j) = i*j; *((*M+i)+j) = i*j; for (i = 0; i < 5; i++) for (j = 0; j < 5; j++) printf("%d ", M[i][j]); if (j == 4) printf("\n"); return 0; The program output is:
Problem 5 (18 points) Write a C program, called anagrams.c, that prompts the user to enter two strings of characters S 1 and S 2, and then determines whether these strings are anagrams of each other. The strings S 1 and S 2 are said to be anagrams of each other if the characters in S 1 can be re-arranged to produce S 2. For example, the words student and stunted are anagrams, as are the words admirer and married. On the other hand, the words cats and stack are not anagrams. Clearly, a necessary condition for S 1 and S 2 to be anagrams is that they have the same length. Note that the strings S 1 and S 2 do not have to consist of letters. For example, 123456789! and 9876!54321 are valid anagrams. On the other hand, the words Admirer and Married are not anagrams, since our definition of anagrams is case sensitive. Write a C program that prompts the user to enter two strings (terminated by the newline character \n), determines whether these strings are anagrams of each other, and reports the results to the user. Here are several sample runs of this program: /home/userxyz/ece15/final> anagrams Enter the first string: 123456789! Enter the second string: 9876!54321 Your strings "123456789!" and "9876!54321" are anagrams! /home/userxyz/ece15/final> anagrams Enter the first string: admirer Enter the second string: married Your strings "admirer" and "married" are anagrams! /home/userxyz/ece15/final> anagrams Enter the first string: Admirer Enter the second string: Married Your strings "Admirer" and "Married" are NOT anagrams! /home/userxyz/ece15/final> anagrams Enter the first string: cats Enter the second string: stack Your strings "cats" and "stack" are NOT anagrams! Hint: For each character in S 1, count how many times this character appears in S 1 and S 2, respectively. The strings are anagrams if and only if these counts coincide for every character of S 1. Notes: You can assume that the user input to your program is always valid. That is, when prompted, the user always enters a string (without whitespace) terminated by the newline character \n. You can also assume that the longest string the user will enter does not exceed MAXLENGTH characters, where MAXLENGTH is a symbolic constant.
#include <stdio.h> #include <string.h> #define MAXLENGTH 80 int main() return 0;
Problem 6 (35 points) Your goal in this problem is to write a C program, called life.c, that implements the Game of Life invented by John H. Conway in 1970. For the purposes of this problem, the Game of Life is played on an n n two-dimensional square grid (or board, or matrix, or array) of cells, where n is a variable the program should read from its input. Each cell can be in one of two possible states: alive or dead. Every cell interacts with its 8 neighbors (or 5 neighbors, or 3 neighbors, if the cell is close to an edge or a corner of the grid). The neighbors of a cell grid[i, j] are the cells that are horizontally, vertically, or diagonally adjacent to the cell grid[i, j]. Note that grid[i, j] is not considered its own neighbor. The game begins with an initial configuration, called the seed, that specifies the state of every cell in the grid. The program should read this initial configuration from its input. The game then evolves in a sequence of generations according to the following rules: Any live cell with 2 or 3 live neighbors lives on to the next generation. Any live cell with more than 3 live neighbors dies (by overcrowding), and any live cell with less than 2 live neighbors also dies (from loneliness). Any dead cell with exactly 3 live neighbors becomes a live cell in the next generation (by reproduction). Any dead cell with more than 3 or less than 3 live neighbors remains dead. The initial seed is considered the 0-th generation. The first generation is created by applying the above rules simultaneously to every cell in the seed array. The rules then continue to be applied repeatedly to create further generations, where each generation is completely determined by the previous one. Your C program should read its input from the file life input.dat. The first line of the input file contains a positive integer that specifies the dimension n of the n n grid on which the game is played. The second line contains another positive integer that specifies the number of generations you are required to compute. The following n lines specify the initial seed configuration of the grid, where a dead cell is given as 0 and a live cell is given as 1. Here is a sample input file: 9 5 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 You can assume that the input file will conform to the specifications above. You can also assume that the required number of generations (second line of input) will be at most 8192. However, you should not assume anything regarding the size n of the grid (first line), except that allocating a few n n arrays of int will not exhaust the available memory. After reading the input file, the C program should iteratively compute the required number of generations and print them to stdout, starting
with the 0-th generation (read from the input file). Here is a sample run of the program, assuming the input file life input.dat is exactly as shown on the previous page: /home/userxyz/ece15/final> life Generation 0: ---**---- ----*-*-- -*-----*- -**---*-- --*-*---- ---*----- Generation 1: ---***--- ---***--- -**--***- -**------ -***----- ---*----- ---*----- Generation 2: ----*---- ---*-*--- -*---**-- *-----*-- -*-*----- ---**---- Generation 3: ----*---- ----*---- ----***-- -----**-- ***--**-- --***---- --***---- Generation 4: ---**---- ----*-*-- -*-----*- -**---*-- --*-*---- ---*----- Generation 5: ---***--- ---***--- -**--***- -**------ -***----- ---*----- ---*----- Note that in the program output above dead cells are represented by - while live cells are represented by * (also note that, in this example, the generations repeat in a cycle starting with the 4-th one).
a. Provided below is part of the source code that implements the C program you are asked to write. This part contains all the global variable(s), all the forward function declarations, and the complete implementation of the function main(). #include <stdio.h> #include <stdlib.h> FILE *input; // // int **alloc int matrix(int n); void read int matrix(file *input, int **grid, int n); void print int matrix(int **grid, int n); void copy int matrix(int **from, int **to, int n); int inbounds(int i, int j, int n); int new state(int **grid, int i, int j, int n); void new generation(int **grid, int n); int main() int **grid; // int n,generations,i; // // if ((input = fopen("life input.dat","r")) == NULL) return 1; // fscanf(input,"%d",&n); fscanf(input,"%d",& generations); // grid = alloc int matrix(n); read int matrix(input,grid,n); // printf("generation 0:\n"); print int matrix(grid,n); // for (i = 1; i <= generations; i++) new generation(grid,n); printf("\ngeneration %d:\n",i); print int matrix(grid,n); return 0; Add comments above, where indicated by // or anywhere else you wish, in order to show your understanding of what the various pieces of code do. In particular, indicate which variables are local and which are global, and for pointer variables state their type.
b. The function main() calls the function read int matrix(input,grid,n), which reads the initial seed configuration of the grid from the file pointed to by input into a twodimensional square array of int, of a given size n by n. The function main() also calls the function print int matrix(grid,n) that prints the current state of the grid array (using * for a live cell and - for a dead cell) to stdout. Implement these functions below. void read int matrix(file *input, int **grid, int n) void print int matrix(int **grid, int n) Notes: Both functions should assume that the two-dimensional int array grid is already allocated. No need to worry about memory allocation here. Comments are not required, but could help you receive partial credit: programs that are not correct and are difficult to understand will receive zero credit, even if partially correct.
c. The function main() calls the function alloc int matrix(n). The latter function dynamically allocates a memory structure that can serve as a two-dimensional array of int of size n by n, and then returns a pointer to this structure. Implement this function below. int **alloc int matrix(int n) Notes: You can call either malloc or calloc standard library functions. The corresponding header file <stdlib.h> is already included in part (a). Do not forget to check for the situation where the requested block of memory is not available, and then deal with this situation if it arises.
d. We will need the functions inbounds(i,j,n) and copy int matrix(from,to,n) later in this program. Let us implement these functions here. The function inbounds(i,j,n) simply checks if the pair i,j is a valid index for a twodimensional array of size n by n. This function returns 1 (True) if i,j is a valid index and 0 (False) otherwise. The function copy int matrix(from,to,n) simply copies a twodimensional array of int of size n by n from one place (pointed to by from) to another place (pointed to by to). Implement the two functions below. int inbounds(int i, int j, int n) void copy int matrix(int **from, int **to, int n)
e. Implement the function new state(grid,i,j,n). This function receives as arguments (a pointer to) a two-dimensional array grid, a pair of integers i,j that is assumed to be a valid index for the array, and an integer n that specifies the size of the array. The function uses the current cell states stored in the array grid to compute the state of the cell grid[i][j] in the next generation. This new state of grid[i][j], namely dead (0) or alive (1), is returned by new state(grid,i,j,n). Implement this function below. int new state(int **grid, int i, int j, int n) Notes: This is where the rules of life and/or death in the next generation are implemented. See the description of the Game of Life at the beginning of this problem. To correctly count the number of live neighbors, it would be useful to call inbounds. Also note that a cell is not a neighbor of itself.
f. Now implement the last and most important function new generation(grid,n). This function receives as arguments (a pointer to) a two-dimensional array grid and an integer n that specifies its size. It computes the new state (the state in the next generation) for each cell in the grid, by invoking the function new state(grid,i,j,n). It then overwrites the values of all the cells in the grid with their state in the next generation. void new generation(int **grid, int n) Notes: This function should also call copy int matrix() and alloc int matrix(). It would be helpful to understand why this is so before proceeding. The function needs to dynamically allocate memory to itself. But beware of memory leaks over many generations! There are two ways to proceed. One option is to free the memory every time before the function terminates. A better option is to allocate the memory only once only the first time the function is called (hint: use static variables).