Skip to main content

Section D.4 Exercise Sheet 4

1. Compiling and Executing a C Program.

  1. Write a C program which declares two variables of type int, assigns arbitrary values to these variables, and then prints both variable values to the console. To this end, create a file main.c and implement the main function.

    Create an executable file prog and execute the program.

  2. Extend the file main.c from part a) with a function max which receives to int arguments and returns the maximum of both values.

  3. Extract the function written in b) into an own translation unit max.c. Create a header file max.h to make the signature of the function available to main.

    Create an executable prog and execute the program.

202207281550

202205061400
Solution.
  1. Program:

    #include <stdio.h>
    
    int main() {
        int a = 10;
        int b = 20;
    
        printf("a: %d\n", a);
        printf("b: %d\n", b);
    
        return 0;
    }
    

    Create an executable and run it:

    $ cc -o main.o -c main.c
    $ cc -o prog main.o
    $ ./prog
    

    In short:

    $ cc main.c -o prog
    $ ./prog
    

  2. Function max:

    #include <stdio.h>
    
    int max(int x, int y) {
        if(x > y) {
            return x;
        } else {
            return y;
        }
    }
    
    int main() {
        int a = 10;
        int b = 20;
    
        int res = max(a, b);
    
        printf("Maximum von a und b: %d\n", res);
    
        return 0;
    }
    

    Create executable: Same as above.

  3. With a header file:

    max.h

    #ifndef MAX_H
    #define MAX_H
    
    int max(int x, int y);
    
    #endif
    

    max.c

    #include "max.h"
    
    int max(int x, int y) {
        if(x > y) {
            return x;
        } else {
            return y;
        }
    }
    

    main.c

    #include <stdio.h>
    #include "max.h"
    
    int main() {
        int a = 10;
        int b = 20;
    
        int res = max(a, b);
    
        printf("Maximum von a und b: %d\n", res);
    
        return 0;
    }
    

    Create executable and run it:

    $ cc -o main.o -c main.c
    $ cc -o max.o -c max.c
    $ cc -o prog main.o max.o
    $ ./prog
    

    In short:

    $ cc main.c max.c -o prog
    $ ./prog
    

2. C Program Comprehension.

Analyze and understand the following C program:

#include <stdio.h>

int main() {
    for (int i = 1; i < 10; i++) {
        if ((i % 2) == 0) {
            printf("The number %i is ...\n", i);
        } else {
            printf("The number %i is ...\n", i);
        }
    }
    return 0;
}
Run

  1. What is the program output? How can you reasonably replace the ellipsis (...) in the printf statements?

  2. Modify the program such that it prints the numbers in range 0 to 20 (inclusive).

  3. Can you modify the loop in such a way that the numbers from b) are printed in reverse order?

202207281550

202205061400
Solution.
  1. The first ellipsis should be replaced with “even” and the second ellipsis with “odd”. The program the prints the following:

    The number 1 is odd.
    The number 2 is even.
    The number 3 is odd.
    The number 4 is even.
    The number 5 is odd.
    The number 6 is even.
    The number 7 is odd.
    The number 8 is even.
    The number 9 is odd.
    

  2. Modified program:

    #include <stdio.h>
    
    int main() {
        for (int i = 0; i <= 20; i++) {
            if ((i % 2) == 0) {
                printf("The number %i is even\n", i);
            } else {
                printf("The number %i is odd\n", i);
            }
        }
        return 0;
    }
    

  3. With print order reversed:

    #include <stdio.h>
    
    int main() {
        for (int i = 20; i >= 0; i--) {
            if ((i % 2) == 0) {
                printf("The number %i is even\n", i);
            } else {
                printf("The number %i is odd\n", i);
            }
        }
        return 0;
    }
    

3. Understanding and Extending C Programs.

Konrad Klug has read the chapter about C in the book and feels confident enough to write his first C program. No Hau has told him that her first C program printed the numbers from 0 to 100, but this is not enough for Konrad. He wants to print the numbers from 0 to 100 in reverse order and only in steps of five, i.e.: 100, 95, 90, ..., 5, 0.

  1. Konrad is almost finished with the program. Only one important part is missing. Please replace the comment /* something missing here */ in the program below, such that it prints the number sequence mentioned above.

    int main() {
        int j = 100;
        int z = 5;
        do {
            /* something missing here */
        } while (j >= 0);
        return 0;
    }
    

  2. What happens when the variable j is assigned a value smaller than zero? Would it help to use a closed loop instead of an open loop?

  3. Now that Konrad has seen the number sequence, he wants to do the same with different numbers. However, he has heard from No Hau that duplicating code is bad. To avoid this, he wants to write a small function. This function is supposed to receive arbitrary numbers \(j\) and \(z\) and must print the number sequence \(j, j-z, j-2z, \dots, j - \lfloor \frac{j}{z} \rfloor\text{.}\) Unfortunately, he cannot quite remember how this is done. Complete his code analogously to part a), but use a closed loop instead.

    int main() {
        magic(100, 5);
        magic(100, 10);
        magic(5000, 200);
        return 0;
    }
    
    void magic(...) {
        /* ... */
    }
    

202207281550

202205061400
Solution.
  1. Completed program:

    int main() {
        int j = 100;
        int z = 5;
        do {
            printf("The number is: %i \n", j ) ;
            j -= z;
        } while (j >= 0);
        return 0;
    }
    

  2. When j is assigned a value smaller than 0, then the loop will be executed once even though the loop condition is false. If a closed loop is used, the loop body would not be executed at all.

  3. Modular version:

    void magic(int j, int z){
        while (j >= 0) {
            printf("The number is: %i \n", j ) ;
            j-=z;
        }
    }
    
    int main(){
        magic(100, 5);
        magic(100, 10);
        magic(5000, 200);
        return 0;
    }
    

4. Leap Year.

According to the gregorian calendar, a year is a leap year if it satifies the following three conditions:

  1. The year number is divisible by 4.

  2. The year number is not divisible by 100.

  3. If the year number is divisible by 400, then rule 2 is void.

Write a C function int leap(int year); which decides for a given input year whether it is a leap year. If yes, the function should return 1, otherwise the function should return 0.

202207281550

202205061400
Solution.
int leap(int year) {
    // not divisible by 4 -> false
    if (year % 4 != 0) {
        return 0;
    }
    // divisible by 4
    // divisible by 100
    if (year % 100 == 0) {
        // divisible by 400 -> true
        if (year % 400 == 0) {
            return 1;
        // not divisible by 400 -> false    
        } else {
          return 0;
        }
    }
    // not divisible by 100 -> true
    return 1;
}

5. Powers of Two.

  1. Konrad Klug loves binary numbers. Unfortunately, he has problems remembering powers of two. Help Konrad by writing a C program int pow2(int n); which receives a number \(n\) and returns \(2^n\text{.}\)

  2. Extend your program in such a way that Konrad can not only calculate powers of two but powers \(a^n\) for arbitrary base \(a \in \mathbb{Z}\text{.}\)

202207281550

202205061400
Solution.
// for a.
int pow(int exp, int base);

int pow2(int exp){
    return pow(exp, 2);
}

// for b.
int pow(int exp, int base) {
    int i = 0;
    int ret = 1;
    while(i < exp) {
        ret *= base;
        i++;
    }
    return ret;
}

6. Pointer Chase.

Try to comprehend every single statement. Does this program produce the output “56-42-13”?

#include <stdio.h>

int main(int argc, char* argv[]) {
    int x = 56;
    int *ap, *bp;
    int **app, **bpp;

    int r[3];

    r[0] = 1;
    r[1] = 2;
    r[2] = 3;

    ap = &r[1];
    bp = &r[2];

    app = &ap;
    bpp = &bp;

    *ap = 43;
    *(*(bpp)) = 13;
    ap++;
    bp -= 2;
    *(bp + 1) = 17;
    ap = &x;
    app = &bp;
    *bp = *ap;
    bpp = &ap;
    ap = &r[2];
    *((*bpp) - 1) = 42;

    printf("%d-%d-%d\n", r[0], r[1], r[2]);
}

202207281550

202205061400
Solution.

Output of the program: “56-42-13”.

Step Line \(\texttt{x}\) \(\texttt{ap}\) \(\texttt{bp}\) \(\texttt{app}\) \(\texttt{bpp}\) \(\texttt{r[0]}\) \(\texttt{r[1]}\) \(\texttt{r[2]}\)
1 4 \(?\) \(-\) \(-\) \(-\) \(-\) \(-\) \(-\) \(-\)
2 10 \(\color{red}{56}\) \(?\) \(?\) \(?\) \(?\) \(?\) \(?\) \(?\)
3 11 \(56\) \(?\) \(?\) \(?\) \(?\) \(\color{red}{1}\) \(?\) \(?\)
4 12 \(56\) \(?\) \(?\) \(?\) \(?\) \(1\) \(\color{red}{2}\) \(?\)
5 14 \(56\) \(?\) \(?\) \(?\) \(?\) \(1\) \(2\) \(\color{red}{3}\)
6 15 \(56\) \(\color{red}{\verb|&|\texttt{r[1]}}\) \(?\) \(?\) \(?\) \(1\) \(2\) \(3\)
7 17 \(56\) \(\verb|&|\texttt{r[1]}]\) \(\color{red}{\verb|&|\texttt{r[2]}}\) \(?\) \(?\) \(1\) \(2\) \(3\)
8 18 \(56\) \(\verb|&|\texttt{r[1]}\) \(\verb|&|\texttt{r[2]}\) \(\color{red}{\verb|&|\texttt{ap}}\) \(?\) \(1\) \(2\) \(3\)
9 20 \(56\) \(\verb|&|\texttt{r[1]}\) \(\verb|&|\texttt{r[2]}\) \(\verb|&|\texttt{ap}\) \(\color{red}{\verb|&|\texttt{bp}}\) \(1\) \(2\) \(3\)
10 21 \(56\) \(\verb|&|\texttt{r[1]}\) \(\verb|&|\texttt{r[2]}\) \(\verb|&|\texttt{ap}\) \(\verb|&|\texttt{bp}\) \(1\) \(\color{red}{43}\) \(3\)
11 22 \(56\) \(\verb|&|\texttt{r[1]}\) \(\verb|&|\texttt{r[2]}\) \(\verb|&|\texttt{ap}\) \(\verb|&|\texttt{bp}\) \(1\) \(43\) \(\color{red}{13}\)
12 23 \(56\) \(\color{red}{\verb|&|\texttt{r[2]}}\) \(\verb|&|\texttt{r[2]}\) \(\verb|&|\texttt{ap}\) \(\verb|&|\texttt{bp}\) \(1\) \(43\) \(13\)
13 24 \(56\) \(\verb|&|\texttt{r[2]}\) \(\color{red}{\verb|&|\texttt{r[0]}}\) \(\verb|&|\texttt{ap}\) \(\verb|&|\texttt{bp}\) \(1\) \(43\) \(13\)
14 25 \(56\) \(\verb|&|\texttt{r[2]}\) \(\verb|&|\texttt{r[0]}\) \(\verb|&|\texttt{ap}\) \(\verb|&|\texttt{bp}\) \(1\) \(\color{red}{17}\) \(13\)
15 26 \(56\) \(\color{red}{\verb|&|\texttt{x}}\) \(\verb|&|\texttt{r[0]}\) \(\verb|&|\texttt{ap}\) \(\verb|&|\texttt{bp}\) \(1\) \(17\) \(13\)
16 27 \(56\) \(\verb|&|\texttt{x}\) \(\verb|&|\texttt{r[0]}\) \(\color{red}{\verb|&|\texttt{bp}}\) \(\verb|&|\texttt{bp}\) \(1\) \(17\) \(13\)
17 28 \(56\) \(\verb|&|\texttt{x}\) \(\verb|&|\texttt{r[0]}\) \(\verb|&|\texttt{bp}\) \(\verb|&|\texttt{bp}\) \(\color{red}{56}\) \(17\) \(13\)
18 29 \(56\) \(\verb|&|\texttt{x}\) \(\verb|&|\texttt{r[0]}\) \(\verb|&|\texttt{bp}\) \(\color{red}{\verb|&|\texttt{ap}}\) \(56\) \(17\) \(13\)
19 30 \(56\) \(\color{red}{\verb|&|\texttt{r[2]}}\) \(\verb|&|\texttt{r[0]}\) \(\verb|&|\texttt{bp}\) \(\verb|&|\texttt{ap}\) \(56\) \(17\) \(13\)
20 32 \(56\) \(\verb|&|\texttt{r[2]}\) \(\verb|&|\texttt{r[0]}\) \(\verb|&|\texttt{bp}\) \(\verb|&|\texttt{ap}\) \(56\) \(\color{red}{42}\) \(13\)

7. Pointer Chase Advanced.

Calculate by hand which output the following program prints. Only use a computer to validate your solution.

#include <stdio.h>

int main(int argc, char* argv[]) {
    int aa[6] = { 50, 60, 70, 80, 90, 100 };
    int bb[6] = { -2, 2, 0, 1, -1, 3 };

    int *a, **b, *c, **d, *e;

    c = & bb[5]; //9
    d = &c; //10
    a = bb; //11
    b = &a; //12

    for (e = *b; e <= *d; e++) { //142
        *e = *(aa + 3 - *e);
    }

    printf("%d %d %d %d %d %d\n", bb[0], bb[1], bb[2], bb[3], bb[4], bb[5]);

    return 0;
}

202207281550

202205061400
Solution.

Output of the program:

Step Line \(\texttt{a}\) \(\texttt{b}\) \(\texttt{c}\) \(\texttt{d}\) \(\texttt{e}\) \(\texttt{aa}\) \(\texttt{bb}\)
1 4 - - - - - - -
2 5 - - - - - \(\color{red}{[50, 60, 70, 80, 90, 100]}\) -
3 7 - - - - - \([50, 60, 70, 80, 90, 100]\) \(\color{red}{[-2, 2, 0, 1, -1, 3]}\)
4 9 \(?\) \(?\) \(?\) \(?\) \(?\) \([50, 60, 70, 80, 90, 100]\) \([-2, 2, 0, 1, -1, 3]\)
5 10 \(?\) \(?\) \(\color{red}{\verb|&|\texttt{bb[5]}}\) \(?\) \(?\) \([50, 60, 70, 80, 90, 100]\) \([-2, 2, 0, 1, -1, 3]\)
6 11 \(?\) \(?\) \(\verb|&|\texttt{bb[5]}\) \(\color{red}{\verb|&|\texttt{c}}\) \(?\) \([50, 60, 70, 80, 90, 100]\) \([-2, 2, 0, 1, -1, 3]\)
7 12 \(\color{red}{\verb|&|\texttt{bb[0]}}\) \(?\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(?\) \([50, 60, 70, 80, 90, 100]\) \([-2, 2, 0, 1, -1, 3]\)
8 14 \(\verb|&|\texttt{bb[0]}\) \(\color{red}{\verb|&|\texttt{a}}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(?\) \([50, 60, 70, 80, 90, 100]\) \([-2, 2, 0, 1, -1, 3]\)
9 15 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\color{red}{\verb|&|\texttt{bb[0]}}\) \([50, 60, 70, 80, 90, 100]\) \([-2, 2, 0, 1, -1, 3]\)
10 14 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\verb|&|\texttt{bb[0]}\) \([50, 60, 70, 80, 90, 100]\) \([\color{red}{100}, 2, 0, 1, -1, 3]\)
11 15 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\color{red}{\verb|&|\texttt{bb[1]}}\) \([50, 60, 70, 80, 90, 100]\) \([100, 2, 0, 1, -1, 3]\)
12 14 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\verb|&|\texttt{bb[1]}\) \([50, 60, 70, 80, 90, 100]\) \([100, \color{red}{60}, 0, 1, -1, 3]\)
13 15 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\color{red}{\verb|&|\texttt{bb[2]}}\) \([50, 60, 70, 80, 90, 100]\) \([100, 60, 0, 1, -1, 3]\)
14 14 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\verb|&|\texttt{bb[2]}\) \([50, 60, 70, 80, 90, 100]\) \([100, 60, \color{red}{80}, 1, -1, 3]\)
15 15 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\color{red}{\verb|&|\texttt{bb[3]}}\) \([50, 60, 70, 80, 90, 100]\) \([100, 60, 80, 1, -1, 3]\)
16 14 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\verb|&|\texttt{bb[3]}\) \([50, 60, 70, 80, 90, 100]\) \([100, 60, 80, \color{red}{70}, -1, 3]\)
17 15 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\color{red}{\verb|&|\texttt{bb[4]}}\) \([50, 60, 70, 80, 90, 100]\) \([100, 60, 80, 70, -1, 3]\)
18 14 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\verb|&|\texttt{bb[4]}\) \([50, 60, 70, 80, 90, 100]\) \([100, 60, 80, 70, \color{red}{90}, 3]\)
19 15 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\color{red}{\verb|&|\texttt{bb[5]}}\) \([50, 60, 70, 80, 90, 100]\) \([100, 60, 80, 70, 90, 3]\)
20 14 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\verb|&|\texttt{bb[5]}\) \([50, 60, 70, 80, 90, 100]\) \([100, 60, 80, 70, 90, \color{red}{50}]\)
21 18 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\color{red}{\verb|&|\texttt{bb[6]}}\) \([50, 60, 70, 80, 90, 100]\) \([100, 60, 80, 70, 90, 50]\)
22 20 \(\verb|&|\texttt{bb[0]}\) \(\verb|&|\texttt{a}\) \(\verb|&|\texttt{bb[5]}\) \(\verb|&|\texttt{c}\) \(\verb|&|\texttt{bb[6]}\) \([50, 60, 70, 80, 90, 100]\) \([100, 60, 80, 70, 90, 50]\)

8. L- and R-Evaluation.

Take a look at the following C function:

void foo() {
  int *w;
  int **y;
  int x[2];
  int z;
  z = 13;
  w = x;
  y = &w;
  *x = z;
  *(x+1) = 42;
  z = *w;
}

  1. For every expression and subexpressions, write down whether L-evaluation or R-evaluation occurs.

  2. Write down all expressions which can be L-evaluated. Which expressions can also be R-evaluated?

  3. Complete the following execution trace. For every line, shortly describe the L- or R-evaluation.

Line \(\texttt{w}\) \(\texttt{x[0]}\) \(\texttt{x[1]}\) \(\texttt{y}\) \(\texttt{z}\)
2
3 \(?\)

Initialization of w

4 \(?\) \(?\)

Initialization of y

5 \(?\) \(?\) \(?\) \(?\)

Initialization of x

6 \(?\) \(?\) \(?\) \(?\) \(?\)

Initialization of z

7 \(?\) \(?\) \(?\) \(?\) \(13\)

L-evaluation of variable z gives the address of the container. The R-evaluation of the constant 13 gives the value 13. Therefore, 13 is written into the container of z.

8 \(\verb|&x[0]|\) \(?\) \(?\) \(?\) \(13\)

...

\(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\)

202207281550

202205061400
Solution.
    • z = 13: z is L-evaluated and 13 is R-evaluated.

    • w = x: w is L-evaluated and x is R-evaluated.

    • y = &w: y is L-evaluated, &w is R-evaluated and w is L-evaluated.

    • *x = z: *x is L-evaluated, x is R-evaluated and z is R-evaluated

    • *(x+1) = 42: *(x+1) is L-evaluated, x is R-evaluated, x+1 is R-evaluated and 42 is R-evaluated.

    • z = *w: z is L-evaluated and 13 is R-evaluated, *w is R-evaluated and w is R-evaluated.

  1. w, y, z, *w, *x, *(x+1) are all L-evaluable expression that occur in the program. All expressions are R-evaluable. x is an array an therefore not L-evaluable anywhere in the program. Arrays behave a little weird in C. Contrary to popular belief, arrays are not just a pointer to the first element. Array values are L-evaluable, but will almost always be converted to a pointer to the first element when used in the program, which is not L-evaluable anymore. Compare C11 Standard Section 6.3.2.1 (3).

  2. Line \(\texttt{w}\) \(\texttt{x[0]}\) \(\texttt{x[1]}\) \(\texttt{y}\) \(\texttt{z}\)
    2
    3 \(?\)

    Initialization of w

    4 \(?\) \(?\)

    Initialization of y

    5 \(?\) \(?\) \(?\) \(?\)

    Initialization of x

    6 \(?\) \(?\) \(?\) \(?\) \(?\)

    Initialization of z

    7 \(?\) \(?\) \(?\) \(?\) \(13\)

    L-evaluation of variable z gives the address of the container. The R-evaluation of the constant 13 gives the value 13. Therefore, 13 is written into the container of z.

    8 \(\verb|&x[0]|\) \(?\) \(?\) \(?\) \(13\)

    The L-evaluation of the variable w gives the address of the container of w. The R-evaluation of x gives an array value, which is converted to the address of the first element of the array x is bound to. The address is written into the container of w.

    9 \(\verb|&x[0]|\) \(?\) \(?\) \(\verb|&w|\) \(13\)

    The L-evaluation of the variable y gives the address of the container of y. The expression is L-evaluable: The R-evaluation of the expression & gives the L-evaluation of w, which is the address of the container of w.

    10 \(\verb|&x[0]|\) \(13\) \(?\) \(\verb|&w|\) \(13\)

    The L-evaluation of the expression \(\texttt{*x}\) is the result of the R-evaluation of x, which is the base address of the corresponding array after conversion. The R-evaluation of z gives the content of the container which one receives when L-evaluating z. This is the value 13. Hence, the value 13 is written into the first container of the array that x is bound to.

    Line \(\texttt{w}\) \(\texttt{x[0]}\) \(\texttt{x[1]}\) \(\texttt{y}\) \(\texttt{z}\)
    11 \(\verb|&x[0]|\) \(13\) \(42\) \(\verb|&w|\) \(13\)

    The L-evaluation of the expression \(\texttt{*(x+1)}\) is the result of the R-evaluation of \(\texttt{x+1}\text{,}\) which is the base address of the array x is bound to plus \(\texttt{sizeof(int)}\text{.}\) This results in the address of the second container of the array. The R-evaluation of the constant 42 yields the value 42. Hence, the value 42 is written into the second container of the array x is bound to.

    12 \(\verb|&x[0]|\) \(13\) \(42\) \(\verb|&w|\) \(13\)

    The L-evaluation of z gives the address of the container of z. The expression \(\texttt{*w}\) is L-evaluable and gives the R-evaluation of w, which is the contained value in the container of w, i.e. the base address of the array x is bound to. The R-evaluation of a L-evaluable expression reads the content of the container, of which the L-evaluation gives the address. Hence, the content of the first container that belongs to the array x is bound to is written into the container of z.

9. C Types.

Decide whether the folowing statements are valid in C. If a statement is not valid, mark it as invalid and proceed with the remaining program.

int i, b;
char c = 8;
c = i;
void x = 2;
char* d;
d = i;
d = "Types are fun";
i = d[0] + 2;
d = (1==2);
int* x = 0;
char* y;
y = 1;
d = x | y;
void* e;
x = (int*) e;
int a = (b = 3) + 1;
int f = (int)("The End?"[0]);

202207281550

202205061400
Solution.

Line Valid? Explanation
1 yes

Variable declaration. Variable has complete type.

2 yes

Variable declaration with initialization. Variable has complete type. Type int (for the constant 8) is implicitly convertible to char.

3 yes

Assignment. Type int is implicitly convertible to char.

4 no

void is an incomplete type. A variable of incomplete type may not be declared.

5 yes

Variable declaration. Variable has complete type char*.

6 no

Assignment. Type int is not implicitly convertible to char*. Conversion requires an explicit cast (see also C standard 6.5.4p3)

7 yes

Assignment. String literal has type char[24]. Type is implicitly converted to char* (see also C standard 6.3.2.1p3), which is the type of the variable that is assigned to.

8 yes

Right hand side expression has type int, because we add up a char (d[0]) and an int (2), which are convertable.

9 no

Right hand side expression has type int, which is not implicitly convertible to char*.

10 yes

Variable declaration with complete type. Initialization is valid since 0 is the null pointer constant with type convertible to int*.

11 yes

Variable declaration with complete type.

12 no

Assignment: Conversion from int to char* requires explicit cast.

13 no

Expression has type int. Expression is well-typed but conversion from int to char* requires explicit cast.

14 yes

Variable declaration with complete type. Every pointer type is a complete type, even void*.

15 yes

Assignment: void* is implicitly convertible into any other pointer type (here: int*). The cast is not even needed.

Line Valid? Explanation
16 yes

Variable declaration with complete type int. The assignment b=3 is well-typed since both b and 3 have type int. It has the type of the left hand side int. Hence, the initializer expression also has type int, which coincides with the type of the declared variable.

17 yes

Declaration with complete type. String literal is implicitly converted to type char*. Casted expression therefore has type char and is casted to int, which is valid. Initialization is valid since the righthand side also has type int.

10. Palindrome.

Palindromes are strings which are identical when read both forwards and backwards. Write a program which checks whether a C string, which is passed as a parameter of type char*, is a palindrome. Your program should return 1 for a positive and 0 for a negative answer.

Remark: Try to calculate the length of the string first.

202207281550

202205061400
Solution.

First idea: First, reverse the string and then compare it with the original one.

int palindrome(char* s) {
    // Calculate length of string (without null terminator)
    char* cpy_s = s;
    int len = 0;
    while (*cpy_s != 0) {
        cpy_s++;
        len++;
    }

    // Reverse string
    char* rev = malloc(len+1 * sizeof(char));
    for (int i = 0; i < len; i++) {
        rev[i] = s[len-1-i];
    }
    rev[len] = 0;

    // Compare strings
    for (int i = 0; i < len; i++) {
        if (rev[i] != s[i]) {
            return 0;
        }
    }

    free(rev);

    return 1;
}

Second idea: Traverse string from the front (s[i]) and back (s[len-1-i]) at the same time.

int palindrome2(char* s) {
    // Calculate length of string (without null terminator)
    char* cpy_s = s;
    int len = 0;
    while (*cpy_s != 0) {
        cpy_s++;
        len++;
    }

    // Compare strings
    for (int i=0; i < len; i++) {
        if (s[i] != s[len-1-i]) {
            return 0;
        }
    }
    return 1;
}