CMPSC 311, Introduction to Systems Programming

How are pointer parameters implemented in C?
How are reference parameters implemented in C++?



Remember the course mantra, System programming requires you to be aware of the resources that your program uses.

This is a lesson about reading assembly code (which may also include guessing what it means, if you've never seen anything like this before), and how functions are translated to assembly code, starting from C and C++.  The "resources" here are the function parameters known to the program.

This is also a lesson about program design for people who know C++ but are just now learning C.  The problem is that some techniques available in C++ are not available in C, and we would like to find a way to deal with that fact.  In particular, C++ has reference parameters, but C doesn't.  Both languages have (and depend on) pointer parameters, so the relationship between pointers and references is important.

There are some exercises at the end; the one marked *** is essential to see if you understand what's going on with these programs.



Reading
Source code
Test systems
Compilers
Compiler options used here (see Makefile for the commands)
Assembly Language output -- various systems and compilers, optimized (.1) or not (.0)

system-compiler-language
             source     unopt.  (lines)   opt.    (lines)
imc-gcc-c89  ref.c      ref.0.s  (174)    ref.1.s  (155)
        c99  ref.c      ref.0.s  (174)    ref.1.s  (155)
        cpp  ref.cpp    ref.0.s  (576)    ref.1.s  (387)
lnx-gcc-c89  ref.c      ref.0.s   (85)    ref.1.s   (62)
        c99  ref.c      ref.0.s   (85)    ref.1.s   (62)
        cpp  ref.cpp    ref.0.s  (609)    ref.1.s  (587)
pmc-gcc-c89  ref.c      ref.0.s   (xx)    ref.1.s   (xx)
        c99  ref.c      ref.0.s   (xx)    ref.1.s   (xx)
        cpp  ref.cpp    ref.0.s   (xx)    ref.1.s   (xx)
sun-gcc-c89  ref.c      ref.0.s   (93)    ref.1.s   (60)
        c99  ref.c      ref.0.s   (93)    ref.1.s   (60)
        cpp  ref.cpp    ref.0.s  (573)    ref.1.s  (481)
sun-sun-c89  ref.c      ref.0.s  (283)    ref.1.s  (251)
        c99  ref.c      ref.0.s  (283)    ref.1.s  (251)
        cpp  ref.cpp    ref.0.s (1044)    ref.1.s  (913)



Examples

We consider the four functions in ref.c and ref.cpp, to illustrate the differences between value parameters and reference parameters in C and C++.  In the compiled code, we left out all labels and directives, as they are extraneous to the topic.  Note that lnx uses the 32-bit Intel architecture, while imc uses the 64-bit Intel architecture, as explained in CS:APP Ch. 3.

function
usage
// pass int by value, C or C++
void valswap(int m, int n)
{
  int t = m;
  m = n;
  n = t;
}
int a, b;
a = 1; b = 2;

valswap(a,b);

now, a is 1, b is 2
// pass int by reference, C++ only
void refswap(int &m, int &n)
{
  int t = m;
  m = n;
  n = t;
}
int a, b;
a = 1; b = 2;

refswap(a,b);

now, a is 2, b is 1
// pass pointer by value, C or C++
void ptrswap(int *m, int *n)
{
  int t = *m;
  *m = *n;
  *n = t;
}
int a, b;
a = 1; b = 2;

ptrswap(&a,&b);

now, a is 2, b is 1
// pass pointer by value, C or C++
// but implement it incorrectly
void badswap(int *m, int *n)
{
  int t = m;
  m = n;
  n = t;
}
int a, b;
a = 1; b = 2;

badswap(a,b);

(see below)

function compiled, imc,
no optimization
compiled, imc,
optimization
valswap
pushq %rbp
movq  %rsp, %rbp

movl  %edi, -20(%rbp)
movl  %esi, -24(%rbp)
movl  -20(%rbp), %eax
movl  %eax, -4(%rbp)
movl  -24(%rbp), %eax
movl  %eax, -20(%rbp)
movl  -4(%rbp), %eax
movl  %eax, -24(%rbp)

leave
ret
pushq %rbp
movq  %rsp, %rbp

leave
ret


(see Exercise 1)
refswap
pushq %rbp
movq  %rsp, %rbp

movq  %rdi, -24(%rbp)
movq  %rsi, -32(%rbp)
movq  -24(%rbp), %rax
movl  (%rax), %eax
movl  %eax, -4(%rbp)
movq  -32(%rbp), %rax
movl  (%rax), %edx
movq  -24(%rbp), %rax
movl  %edx, (%rax)
movq  -32(%rbp), %rdx
movl  -4(%rbp), %eax
movl  %eax, (%rdx)

leave
ret
pushq %rbp
movq  %rsp, %rbp

movl  (%rdi), %edx
movl  (%rsi), %eax
movl  %eax, (%rdi)
movl  %edx, (%rsi)

leave
ret
ptrswap
same as above
same as above
badswap
see below
see below

Compiler error and warning messages, imc

$ gcc -DREFSWAP ref.c
ref.c:14: error: expected ‘;’, ‘,’ or ‘)’ before ‘&’ token

$ gcc -DBADSWAP ref.c
ref.c: In function ‘badswap’:
ref.c:32: warning: initialization makes integer from pointer without a cast
ref.c:34: warning: assignment makes pointer from integer without a cast
ref.c: In function ‘main’:
ref.c:58: warning: passing argument 1 of ‘badswap’ makes pointer from integer without a cast
ref.c:58: warning: passing argument 2 of ‘badswap’ makes pointer from integer without a cast

$ g++ -DBADSWAP ref.cpp
ref.cpp: In function ‘void badswap(int*, int*)’:
ref.cpp:30: error: invalid conversion from ‘int*’ to ‘int’
ref.cpp:32: error: invalid conversion from ‘int’ to ‘int*’
ref.cpp: In function ‘int main()’:
ref.cpp:56: error: invalid conversion from ‘int’ to ‘int*’
ref.cpp:56: error:   initializing argument 1 of ‘void badswap(int*, int*)’
ref.cpp:56: error: invalid conversion from ‘int’ to ‘int*’
ref.cpp:56: error:   initializing argument 2 of ‘void badswap(int*, int*)’

function
compiled, lnx,
no optimization
compiled, lnx,
optimization
valswap
pushl %ebp
movl  %esp, %ebp

subl  $4, %esp
movl  8(%ebp), %eax
movl  %eax, -4(%ebp)
movl  12(%ebp), %eax
movl  %eax, 8(%ebp)
movl  -4(%ebp), %eax
movl  %eax, 12(%ebp)

leave
ret
pushl %ebp
movl  %esp, %ebp

leave
ret


(see Exercise 1)
refswap
pushl %ebp
movl  %esp, %ebp

subl  $4, %esp
movl  8(%ebp), %eax
movl  (%eax), %eax
movl  %eax, -4(%ebp)
movl  8(%ebp), %edx
movl  12(%ebp), %eax
movl  (%eax), %eax
movl  %eax, (%edx)
movl  12(%ebp), %edx
movl  -4(%ebp), %eax
movl  %eax, (%edx)

leave
ret
pushl %ebp
movl  %esp, %ebp

pushl %ebx
movl  8(%ebp), %edx
movl  12(%ebp), %ecx
movl  (%edx), %ebx
movl  (%ecx), %eax
movl  %eax, (%edx)
movl  %ebx, (%ecx)
popl  %ebx

leave
ret
ptrswap
same as above
same as above
badswap
see below
see below

Compiler error and warning messages, lnx

% gcc -DREFSWAP ref.c
ref.c:14: error: syntax error before '&' token
ref.c: In function `refswap':
ref.c:16: error: `m' undeclared (first use in this function)
ref.c:16: error: (Each undeclared identifier is reported only once
ref.c:16: error: for each function it appears in.)
ref.c:17: error: `n' undeclared (first use in this function)

% gcc -DBADSWAP ref.c
ref.c: In function `badswap':
ref.c:32: warning: initialization makes integer from pointer without a cast
ref.c:34: warning: assignment makes pointer from integer without a cast
ref.c: In function `main':
ref.c:58: warning: passing arg 1 of `badswap' makes pointer from integer without a cast
ref.c:58: warning: passing arg 2 of `badswap' makes pointer from integer without a cast

% g++ -DBADSWAP ref.cpp
ref.cpp: In function `void badswap(int*, int*)':
ref.cpp:30: error: invalid conversion from `int*' to `int'
ref.cpp:32: error: invalid conversion from `int' to `int*'
ref.cpp: In function `int main()':
ref.cpp:56: error: invalid conversion from `int' to `int*'
ref.cpp:56: error:   initializing argument 1 of `void badswap(int*, int*)'
ref.cpp:56: error: invalid conversion from `int' to `int*'
ref.cpp:56: error:   initializing argument 2 of `void badswap(int*, int*)'




Exercises

1.  Why did the body of valswap() reduce to nothing when optimization was turned on?

2.  Why is the badswap() function badly written?
3.  *** Explain how to rewrite a C++ function that uses a reference parameter as a C function that uses a pointer parameter.



Last revised, 7 Jan. 2012