C Interview Questions 02: Makefile, Scope and Lifetime, Call by Value, & Data Types

Makefile | scope & lifetime | call by value | sizes of data types | memory layout

Yu-Cheng (Morton) Kuo
Nerd For Tech
7 min readDec 10, 2023

--

Code regarding Makefile on Github: https://github.com/yu-cheng-kuo-28/makefile-demo

Continuing from the previous article, we now move on to Makefile, memory layout, scope & lifetime, sizes of data types, & type conversion with demos I created on my VPS (Ubuntu 20.04 LTS x64) on Vultr.

C Interview Questions 01: Pointers

C Interview Questions 03: Bitwise Operations / Sizes of Structs / Volatile

Outline

(0) Setup of Vim
(1) Call By Value vs. Call By Reference
(2) Makefile
(3) More on Makefile
(4) Memory Layout
(5) Scope and Lifetime of Storage Classes
(6) Sizes of Data Types
(7) More on Sizes of Data Types
(8) Type Conversion
(9) References

(0) Setup of Vim

  • .vimrc
set cindent
set expandtab " Use spaces for indentation
set tabstop=4 " Number of spaces for a tab stop
set shiftwidth=4 " Number of spaces for auto-indenting

autocmd FileType make set noexpandtab tabstop=4 shiftwidth=4

In Linux systems, place this file in your home directory.

While this article does not require prior knowledge of Vim, you may optionally explore Vim’s functionalities at your discretion.

(1) Call By Value vs. Call By Reference

Q: Compare C & C++ in terms of the forms of function calls.

Ans:

According to C++ Primer, there’re only two main forms of function calls, that is, call by value and call by reference.

  • C: call by value (pointers are call by value of address, which belong to call by value)
  • C++: call by reference

(2) Makefile

Q: Create a simple Makefile to compile main.c and foo.c, and clean up main.o and foo.o files

Ans:

# Create a simple Makefile to compile main.c and foo.c, and clean up main.o and foo.o files

# Target: main
main: main.o foo.o
gcc main.o foo.o -o main

# Target: main.o
main.o: main.c
gcc main.c -c

# Target: foo.o
foo.o: foo.c
gcc foo.c -c

# Target: clean
clean:
rm -rf main.o foo.o

(3) More on Makefile

  • A Makefile is used to control the build process of a project, specifying how to compile and link the program.
Figure: Stages of the compilation process in C. Retrieved from the online course Writing, Running, and Fixing Code in C (Duke) on Coursera.
Figure: The underlying rationale of make and Makefile. Retrieved from the online course Writing, Running, and Fixing Code in C (Duke) on Coursera.
Figure: A detailed and concise illustration of make and Makefile. Retrieved from 簡單學 makefile:makefile 介紹與範例程式

Let’s take a look at an example of Makefile with these 6 files.

  • [1] Makefile
# Makefile
# This Makefile builds the program myProgram from abc.c, xyz.c, and main.c

# The default target:
all: myProgram

# The main program depends on object files.
myProgram: abc.o xyz.o main.o
gcc -o myProgram abc.o xyz.o main.o

# Each .o file depends on its corresponding .c file.
abc.o: abc.c abc.h
gcc -c abc.c

xyz.o: xyz.c xyz.h
gcc -c xyz.c

main.o: main.c xyz.h
gcc -c main.c

# The clean rule for cleaning up
clean:
rm -f abc.o xyz.o main.o myProgram
  • [2] main.c
// main.c
#include "abc.h"
#include "xyz.h"

int main() {
abc_function();
xyz_function();
return 0;
}
  • [3] abc.h
// abc.h
#ifndef ABC_H
#define ABC_H

void abc_function();

#endif
  • [4] abc.c
// abc.c
#include "abc.h"
#include <stdio.h>

void abc_function() {
printf("Function abc_function called.\\n");
}
  • [5] xyz.h
// xyz.h
#ifndef XYZ_H
#define XYZ_H

void xyz_function();

#endif
  • [6] xyz.c
// xyz.c
#include "xyz.h"
#include <stdio.h>

void xyz_function() {
printf("Function xyz_function called.\\n");
}

And here’s the output.

root@DemoYuChengKuo:~/makefileDemo# ls
abc.c abc.h main.c Makefile xyz.c xyz.h

root@DemoYuChengKuo:~/makefileDemo# make
gcc -c abc.c
gcc -c xyz.c
gcc -c main.c
gcc -o myProgram abc.o xyz.o main.o

root@DemoYuChengKuo:~/makefileDemo# ls
abc.c abc.h abc.o main.c main.o Makefile myProgram xyz.c xyz.h xyz.o

root@DemoYuChengKuo:~/makefileDemo# ./myProgram
Function abc_function called.
Function xyz_function called.

root@DemoYuChengKuo:~/makefileDemo# ls
abc.c abc.h abc.o main.c main.o Makefile myProgram xyz.c xyz.h xyz.o

root@DemoYuChengKuo:~/makefileDemo# make clean
rm -f abc.o xyz.o main.o myProgram

root@DemoYuChengKuo:~/makefileDemo# ls
abc.c abc.h main.c Makefile xyz.c xyz.h

Below is what I got on my VPS (Ubuntu 20.04 LTS x64) on Vultr:

Figure: Outputs of commands of my VPS on Vultr

(4) Memory Layout

Q: Explain the differences of stack & heap in memory layout of C.

Figure: Memory Layout of C. Retrieved from Memory Layout of C Program & Mips memory layout

(5) Scope and Lifetime of Storage Classes

Q: Compare and explain static & extern variables.

Ans:

Figure: Comparison table of all storage classes in C on scope and lifetime
Figure: Comparison table of all storage classes in C on memory layout

(6) Sizes of Data Types

Q: Fill out the "?"s.

size of char = ?? byte(s)
size of unsigned char = ?? byte(s)

size of short = ?? byte(s)
size of unsigned short = ?? byte(s)

size of int = ?? byte(s)
size of unsigned int = ?? byte(s)

size of long = ?? byte(s)
size of unsigned long = ?? byte(s)

size of long long = ?? byte(s)
size of unsigned long long = ?? byte(s)

size of float = ?? byte(s)
size of double = ?? byte(s)

Ans:

For a 64-bit machine,

size of char = 1 byte(s)
size of unsigned char = 1 byte(s)

size of short = 2 byte(s)
size of unsigned short = 2 byte(s)

size of int = 4 byte(s)
size of unsigned int = 4 byte(s)

size of long = 4 byte(s)
size of unsigned long = 4 byte(s)

size of long long = 8 byte(s)
size of unsigned long long = 8 byte(s)

size of float = 4 byte(s)
size of double = 8 byte(s)

The output above came from the code below:

(1) Makefile

sizeData: sizeData.c
gcc -o sizeData sizeData.c

clean:
rm -f sizeData

(2) sizeData.c

#include <stdio.h>

int main() {
// %u : unsigned integer value
// %zu : unsigned integer value to print the value of a size_t, such as the result of the sizeof operator.
printf("size of char = %zu byte(s)\n"
"size of unsigned char = %zu byte(s)\n\n"
"size of short = %zu byte(s)\n"
"size of unsigned short = %zu byte(s)\n\n"
"size of int = %zu byte(s)\n"
"size of unsigned int = %zu byte(s)\n\n"
"size of long = %zu byte(s)\n"
"size of unsigned long = %zu byte(s)\n\n"
"size of long long = %zu byte(s)\n"
"size of unsigned long long = %zu byte(s)\n\n"
"size of float = %zu byte(s)\n"
"size of double = %zu byte(s)\n",
sizeof(char),
sizeof(unsigned char),
sizeof(short),
sizeof(unsigned short),
sizeof(int),
sizeof(unsigned int),
sizeof(long),
sizeof(unsigned long),
sizeof(long long),
sizeof(unsigned long long),
sizeof(float),
sizeof(double));
return 0;
}

Below is what I got on my VPS (Ubuntu 20.04 LTS x64) on Vultr:

Figure: Outputs of commands of my VPS on Vultr

(7) More on Sizes of Data Types

Now, let’s dig deeper into it with the following tables.

Figure: Sizes of all data types in C: on a 64-bit machine & a 32-bit machine

(8) Type Conversion

  • [1] Type Casting (Explicit Conversion): Type casting is when a programmer explicitly converts a value from one type to another. This is done using the casting operator, which is specified in parentheses before the value to be converted.
  • [2] Type Conversion (Implicit Conversion): Type conversion can also occur implicitly, which means the compiler automatically converts one type to another when it is necessary and legal to do so.

Let’s observe this by an instance:

(1) Makefile

typeDemo: typeDemo.c
gcc -o typeDemo typeDemo.c

clean:
rm -f typeDemo

(2) typeDemo.c

#include <stdio.h>

int main() {
int integerVar = 10;
int integerVar_2 = 3;
double doubleVarExplicit;
double doubleVarImplicit;

// Explicitly cast an int to a double
doubleVarExplicit = (double)integerVar;

// Implicit conversion from int to double
doubleVarImplicit = integerVar; // No casting operator needed


printf("Integer variable: %d\n", integerVar);
printf("Double variable after explicit casting: %.2f\n", doubleVarExplicit);
printf("Double variable after implicit conversion: %.2f\n\n", doubleVarImplicit);

// printf("%.2f\n", integerVar/integerVar_2); // CE or Get 0.00 since they are all integers
printf("%.2f\n", (float)integerVar/integerVar_2); // Make one of them to float
printf("%.2f\n", integerVar/(float)integerVar_2); // Make one of them to float

return 0;
}

Below is what I got on my VPS (Ubuntu 20.04 LTS x64) on Vultr:

Figure: Outputs of commands of my VPS on Vultr

(9) References

  1. Lippman, S. B., Lajoie, J., & Moo, B. E. (2012). C++ Primer (5th ed.). Addison Wesley.
  2. 蔡文龍、何嘉益、張志成、張力元、歐志信、陳士傑(2021)。C & C++程式設計經典(第五版)。台北:碁峯資訊。
  3. 劉邦鋒(2019)。由片語學習C程式設計(第二版)。台北:國立臺灣大學出版中心。
  4. My interview experience [2023/11/30]
  5. C/C++ — 常見 C 語言觀念題目總整理(適合考試和面試) [2017]
  6. Classic C Interview Questions [2016]
  7. [面試] 聯發科技 MTK (內含考題) [2011]

--

--

Yu-Cheng (Morton) Kuo
Nerd For Tech

CS/DS blog with C/C++/Embedded Systems/Python. Embedded Software Engineer. Email: yc.kuo.28@gmail.com