[Courses] C Programming For Absolute Beginners, Lesson 5A: All About Functions, Part 2

Christopher Howard christopher.howard at frigidcode.com
Mon Apr 2 07:47:51 UTC 2012


Hi. If I may, I will pick on one or two small points, in the interest of
accuracy:

On 04/01/2012 09:06 PM, Carla Schroder wrote:
> 
> The return type is whatever C data type you want to use. Every function in a 
> program must have a unique name. If there is no return value you can use 
> "void" for your return type. If there are no arguments then you should enter 
> "void", because as we learned in Part 1 the empty parentheses means an unknown 
> number of arguments. It is always best to be explicit, so we want to use 
> "void" to indicate that there are zero arguments.
> 

If I'm not mistaken, it is more accurate to say that every function with
external linkage must have a unique name. The Holy Gnu programming
manual states:

quote
--------
You can define a function to be static if you want it to be callable
only within the source file where it is defined:

  static int
  foo (int x)
  {
    return x + 42;
  }

This is useful if you are building a reusable library of functions and
need to include some subroutines that should not be callable by the end
user.
--------

So, for example:

code:
--------
$ cat a.c
int myfunc();

int main()
{
  return myfunc();
}
$ cat b.c
int myfunc()
{
  return 2;
}
$ cat c.c
static int myfunc()
{
  return 4;
}
$ gcc -O2 c.c b.c a.c
$ ./a.out
$ echo $?
2
--------

> 
> C functions can be written five different ways:
> 
> 1--No arguments or return values
> 2--Arguments and no return values
> 3--Arguments and return values
> 4--No arguments and return values
> 5--Return multiple values
>

Item 5 should be excluded from the list. Speaking technically, all
functions in C return one value and one value only. This might be a
struct containing other data objects, yet even in such a case it is only
one value that is returned by the function (the memory address of the
struct). I suppose one could argue semantic nuances here, but even on
the highest level we recognize that a "return" statements returns only
one evaluated result.

I'm not sure whether it is, technically speaking, proper to refer to a
function returning "void" as returning zero or one arguments. My c99
draft copy states:

quote:
--------
The (nonexistent) value of a void expression (an expression that has
type void) shall not be used in any way, and implicit or explicit
conversions (except to void) shall not be applied to such an expression.
If an expression of any other type is evaluated as a void expression,
its value or designator is discarded. (A void expression is evaluated
for its side effects.)
--------


> 
> #include <stdio.h>
> 
> int main (void)
> {
>   printf( "Hello, and welcome to the Beginning C Course!\n" );
>   return 0;
> }
> 
> =======================
> 
> Yes, this has been modified for correctness :) This is the simplest possible 
> function, using only main and no other functions. We can modify this so that 
> main calls a second function to do the same thing:
> 
> =======================
> 

Actually, it is think it is possible to write a program without a main()
function, by using a shortcut through the _start function. (But, as far
as I know, it is not possible to write a program without a _start function:

code:
--------
$ cat a.c
#include <stdlib.h>

_start()
{
  exit(3);
}

cmhoward at enigma /scratch/cmhoward/test3 $ gcc -nostartfiles a.c
cmhoward at enigma /scratch/cmhoward/test3 $ ./a.out
cmhoward at enigma /scratch/cmhoward/test3 $ echo $?
3
--------

> 
> HOMEWORK
> 
> Take any of these examples and modify them to accept user input, just like we 
> did in our previous lessons. Put as much functionality as possible into 
> separate functions and then call them from main.
> 
> If there are any gurus wishing for a bit of a challenge, give us some examples 
> of using functions efficiently and correctly.
> 

Actually, speaking purely in terms of performance optimization (making
your code run faster) functions can be a very dangerous thing. Making a
function call often requires the processor to make a large jump in
memory, which means (depending on where or how far you jump) that the
processor may have to waste precious cache space or pull code from
slower memory. Furthermore, when you make a function call, of course the
process has to go to the trouble not only of moving arguments into the
proper registers, but it may also need to push additional data out of
the registers onto the stack, so that this data is preserved across the
function call.

The "inline" keyword, if I am not mistaken, actually suggests to the
compiler that it should directly replace the function call with the
actual function code:

code:
--------
$ cat a.c
#include <stdio.h>

inline void howardsfunction(a)
{
  puts ("hello world");
}

int main()
{
  howardsfunction ();
  howardsfunction ();
  return 0;
}
$ gcc -O2 a.c
$ ./a.out
hello world
hello world
$ objdump -d a.out
...snip...
0000000000400570 <howardsfunction>:
  400570:	bf 8c 06 40 00       	mov    $0x40068c,%edi
  400575:	e9 b6 fe ff ff       	jmpq   400430 <puts at plt>
  40057a:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)

0000000000400580 <main>:
  400580:	48 83 ec 08          	sub    $0x8,%rsp
  400584:	bf 8c 06 40 00       	mov    $0x40068c,%edi
  400589:	e8 a2 fe ff ff       	callq  400430 <puts at plt>
  40058e:	bf 8c 06 40 00       	mov    $0x40068c,%edi
  400593:	e8 98 fe ff ff       	callq  400430 <puts at plt>
  400598:	31 c0                	xor    %eax,%eax
  40059a:	48 83 c4 08          	add    $0x8,%rsp
  40059e:	c3                   	retq
  40059f:	90                   	nop
...snip...
--------

As you can see, the code does not actually call howardsfunction(), but
basically just repeats the call to the puts function.

However, if I remove the inline keyword, the resulting object code is:

code:
--------
...snip...
0000000000400570 <howardsfunction>:
  400570:	bf 8c 06 40 00       	mov    $0x40068c,%edi
  400575:	e9 b6 fe ff ff       	jmpq   400430 <puts at plt>
  40057a:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)

0000000000400580 <main>:
  400580:	48 83 ec 08          	sub    $0x8,%rsp
  400584:	31 c0                	xor    %eax,%eax
  400586:	e8 e5 ff ff ff       	callq  400570 <howardsfunction>
  40058b:	31 c0                	xor    %eax,%eax
  40058d:	e8 de ff ff ff       	callq  400570 <howardsfunction>
  400592:	31 c0                	xor    %eax,%eax
  400594:	48 83 c4 08          	add    $0x8,%rsp
  400598:	c3                   	retq
  400599:	90                   	nop
...snip...
--------

-- 
frigidcode.com
indicium.us



More information about the Courses mailing list