[Courses] C Programming For Absolute Beginners, Lesson 5A: All About Functions, Part 2
Christopher Howard
christopher.howard at frigidcode.com
Tue Apr 3 21:42:49 UTC 2012
On 04/03/2012 07:11 AM, Carla Schroder wrote:
> I don't see how this makes a case against creating unique function names.
> Sure, you can do it. Regardless of any circumstances where duplicate function
> names would not cause a program to run incorrectly, why would you want the
> confusion?
>
Strictly speaking, I wasn't trying to make a case against creating
unique function names. I was only responding to your statement that
function names "must" be unique throughout an entire program, which as I
demonstrated is not true.
@Peck: Personally, the ability to create static functions is important
to me because it gives me the ability to name a bunch of local functions
whatever the heck I want without having to wonder each single time
whether or not I have used or ever will use that function name in any
other source file in my project, or in any past or present or future
library I will ever use. I like convenience and freedom -- so sue me. I
mean, really... how can you really be certain that every function name
you every write is and always will be totally unique, unless you always
use really long and cumbersome names like
"my_program_name_network_module_local_3_bit_nand()". Just use "static
3_bit_nand()" and then you can change it later in that one source code
file in the unlikely event that you ever need to.
If, as you say, I need to rename the function so I can export it, or so
I can import a similarly named function, this is not hard. The function
only needs to be renamed in that source file, which in Emacs can be done
with a single search-and-replace command, which takes about two seconds
to type.
>
>
>> --------
>>
>>> 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.
>
> Nah, it stays on the list :). Whatever the semantic nuances, which are good to
> understand clearly, it's still operating with multiple returned values.
>
Well, you're the teacher... But the C99 states "The return type
[singular] of a functions shall be void or an object type [singular]
other than array type." (sect. 6.9.1) So long as every one understands
that a C function can only return one object (or void) and that it
cannot return an array of objects (only a single reference to one) that
is probably good enough.
>
> Fish fiddle dee dee, functions are not dangerous! Functions are your program.
> Modern compilers perform all manner of optimizations, including deciding which
> functions to inline. Manually inlining a function is compiler-dependent, and
> useful in limited circumstances, which we will cover in this course.
>
> Would you explain your inline object code example? What performance difference
> does this show?
>
Sorry, my previous example was not very good as a performance
demonstration. This is a little better:
code:
--------
$ cat howardonimize.c
#include <stdio.h>
inline int howardonimize (int x)
{
return x*x + 3*x + 12;
}
int main ()
{
int a;
// Get all data from standard input.
// Could be a huge file of space separated numbers
while(1)
{
int ret = scanf("%d", &a);
// out of data
if(ret == EOF)
{
break;
}
// to get rid of any "junk" in the buffer
if(ret != 1)
{
getchar();
continue;
}
// sends space separated output numbers to
// standard out
printf ("%d ", howardonimize(a));
// in real life, you probably want overflow check also
}
// nice end of line at end of file, to be compliant
// with HOWARDX data file standards
puts("");
}
$ gcc -O2 howardonimize.c -o howardonimize
$ cat testinputdata | ./howardonimize > testoutputdata
$ cat testoutputdata
1515973170 270953758 55470 -265027752 1416 21982042 13968916 1198100708
-319032502 468550 991030 -1900284126 1902579614 971220 9910 96737070
1492 969250 5710 87413160 120 30 82 -1347342946 1172069470 706450 2460
-1722971754 894638020 7482970
$ objdump -d howardonimize
...snip...
00000000004006d0 <howardonimize>:
4006d0: 8d 47 03 lea 0x3(%rdi),%eax
4006d3: 0f af c7 imul %edi,%eax
4006d6: 83 c0 0c add $0xc,%eax
4006d9: c3 retq
4006da: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
00000000004006e0 <main>:
4006e0: 53 push %rbx
4006e1: 48 83 ec 10 sub $0x10,%rsp
4006e5: 48 8d 5c 24 0c lea 0xc(%rsp),%rbx
4006ea: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
4006f0: 31 c0 xor %eax,%eax
4006f2: 48 89 de mov %rbx,%rsi
4006f5: bf 4c 08 40 00 mov $0x40084c,%edi
4006fa: e8 b1 fe ff ff callq 4005b0 <__isoc99_scanf at plt>
4006ff: 83 f8 ff cmp $0xffffffffffffffff,%eax
400702: 74 3c je 400740 <main+0x60>
400704: 83 f8 01 cmp $0x1,%eax
400707: 74 17 je 400720 <main+0x40>
400709: 48 8b 3d 28 09 20 00 mov 0x200928(%rip),%rdi #
601038 <__bss_start>
400710: e8 8b fe ff ff callq 4005a0 <_IO_getc at plt>
400715: eb d9 jmp 4006f0 <main+0x10>
400717: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
40071e: 00 00
400720: 8b 44 24 0c mov 0xc(%rsp),%eax
400724: be 4f 08 40 00 mov $0x40084f,%esi
400729: bf 01 00 00 00 mov $0x1,%edi
40072e: 8d 50 03 lea 0x3(%rax),%edx
400731: 0f af d0 imul %eax,%edx
400734: 31 c0 xor %eax,%eax
400736: 83 c2 0c add $0xc,%edx
400739: e8 42 fe ff ff callq 400580 <__printf_chk at plt>
40073e: eb b0 jmp 4006f0 <main+0x10>
400740: bf 52 08 40 00 mov $0x400852,%edi
400745: e8 26 fe ff ff callq 400570 <puts at plt>
40074a: 48 83 c4 10 add $0x10,%rsp
40074e: 5b pop %rbx
40074f: c3 retq
...snip...
--------
So, as you can see, howardonimize() itself is never called, but it's
internal code is inlined into the main() function, at address 40072e, I
believe. Because of that, the code didn't have to go to the trouble of
making a jump to the function's memory address, or arranging the
function's parameters, or preserving registers across the function call.
Now, a good compiler might inline the function anyway, even if you don't
use the "inline" keyword. I'm not trying to push the usage of the
"inline" keyword, but only to demonstrate my original point: all else
being equal, at the lowest level code with a function call will actually
be slightly less efficient (in terms of raw cycles needed) than the
equivalent code without one. With a small data set, that difference will
be negligible, but if you had three trillion numbers to crunch, it might
make a measurable difference.
(There may be complex caching issues at work here as well... I don't
dare broach that subject.)
--
frigidcode.com
indicium.us
More information about the Courses
mailing list