- A function is a reusable unit of code that may take input(s) and may produce an output
- Already familiar with functions:
main(), printf(), sqrt(), etc. - Functions facilitate code reuse, you don't have to copy-pasta the same code over and over
- Procedural abstraction: functions allow us to ignore the small details of how a certain block of code or algorithm works
- Functions encapsulate functionality into reusable abstract code blocks
- Standard libraries and functions have a lot of design, optimization, testing, debugging, etc. behind them, USE them
- The first question you should ask in problems solving is "is this problem already solved?" that is, does a function already exist to solve my problem?
- As with variables, functions must be declared before they can be used
- IN C, you "declare" a function using a prototype
- IN a prototype, you declare a function's signature
- The name of the function (its "identifier")
- a list of its parameters (or "arguments", ie its inputs)
- the return type: the type of variable value that it returns
/**
* This function computes the Euclidean distance between
* two points defined by (x1, y1) and (x2, y2)
*/
double euclideanDistance(double x1, double y1, double x2, double y2);- Syntax notes: a prototype ends with a semicolon!
- In C, comments are generally attached to the prototype, not the definition; DRY = Don't Repeat Yourself
- Later in the code, you provide a function definition: the code that actually executes when you "call" or "invoke" the function
- It contains the same signature, but instead of a semicolon, has a body denoted with curly brackets
double euclideanDistance(double x1, double y1, double x2, double y2) {
return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
}- Every function in C should be documented with doc-style comments on the prototype, not the definition
- In the original version, we declared a variable called
result - Such a variable is called local: it exists only within the function (ie its scope is the function itself)
- Likewise the 4 parameter variables are also local to the function
- In general you can declare as many local variables as you want
- In Java, functions are part of classes so we call them "methods"
- There are no prototypes in Java, only definitions
- You place methods within a class and they "belong" to the class
- For now, all our methods will be
static: the belong to the class not to instances of the class
public class DistanceUtils {
public static double euclideanDistance(double x1, double y1, double x2, double y2) {
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}
}- To invoke or call a static method in Java use the class name + a period and provide the inputs
- Example:
DistanceUtils.euclideanDistance(0, 0, 10, 10)
Observe the differences:
- Each method belongs to a class, in this case,
DistanceUtils - The
publickeyword makes the method available to any piece of codeprivatewould make it so only the class itself can "see" the methodprotectedwould make it so only the class and its subclasses can "see"- The lack of a keyword modifier means it is "package protected": any class in the same package can "see" the method
- The
statickeyword makes it so that the method belongs to the class itself and not to instances of the class
- New keyword:
void- A function that does not return a value is a "void" function and you use the keyword
voidto indicate its return type - A function that does not take any input (ie no parameters) can have a "void" parameter:
void foo(void); - Alternatively (and arguably better practice): leave it blank
void foo()
- A function that does not return a value is a "void" function and you use the keyword
- Recall how to compute the absolute value in Java:
Math.abs(); this works for all numerical types (double,int, etc.) - In C, there are several functions, one for each type of variable:
fabs()is for floating point numbers,abs()is forinttypes, etc. - Java only has "one" absolute value function that can be used for any type of number, but C has to have several all with different names
- Java supports function overloading: you can have multiple versions of the same function with the same name but with different parameter types
- C does NOT support function overloading, meaning we have to name each version of the absolute value function a different name!
- You can easily "pollute the name space" by creating too many functions with different names; since you can only use a name once, once a function is defined, then no other function can use that name!
- Motivating example: let's write a function to swap two values
- Each time a function is called, a new stack frame is created which stores the function's local variables and parameters
- The parameters are separate variables than those in the original function,
- Only the values of the variables are passed to a function, not the variables themselves, ie COPIES of their values are passed
- Changes to the copies have no effect in the original "calling function"
- Therefore swapping two values using "pass by value" is impossible
- In C, you can "solve" this problem by using pointers and pass-by-reference
- In Java, all variables are pass by value, there is NO pass by reference
- IN both languages, code can quickly become complex with dozens or hundreds or thousands of different functions
- You need a way to organize code when it gets too big
- IN Java: you generally place related functions into classes
- Organize classes into packages: a package is an organized hierarchy of folders (or directories)
- Example:
package unl.cseis a package such that code is located in a directory calledcsewhich is a subdirectory of a directory calledunl
- In C, functions are separated out into separate files
- A header file contains all the prototypes and documentation
- A source file contains all the function definitions
- In general, header files end with a
.hand source files end with a.c
Demonstration: write a general "distance library" of distance functions in separate header/source files
- You place prototypes AND documentation into a header file,
distance.h - You place function definitions into a source file,
distance.c- You use
#include "distance.h"in the source file to include the prototypes
- You use
- In a separate driver source file you have:
- Another
#include "distance.h" - The
mainfunction and - You can now use your library functions
- Another
- To compile (but not link) your library, use the
-cflag:gcc -c distance.cwhich produces an object file,distance.o - To compile the driver program, use
gcc distance.o distanceDriver.cproduces thea.outexecutable
-
IN general, you would want to create a
makefileto specify how more complex projects are built -
How to use makefiles: read a tutorial!
-
Java: you would use Ant or Maven or JenkinsCI
- Every piece of data in a computer is stored in memory
- Memory consists of both an address (location) and contents: the data actually stored at a memory address
- In C, you can create a pointer variable which represents not the contents of memory, but hte address itself
- To create a pointer variable use the star,
*
int a = 10; //normal variable
int *ptrA; //pointer variableptrAis a pointer variable that can point to any memory location that stores an integer- In general, it is best practice to initialize your pointer variables
- If you want them to point to something right away, do it.
- If you don't know right away what you want it to point to, then point it to
NULL(case sensitive, all upper case in C)
int *ptrA = NULL;
//you can then make null pointer checks:
if(ptrA == NULL) {
printf("uninitialized pointer!\n");
}- How can we make a pointer point to an actual memory address?
- To get the memory of any regular variable, use
&
int a = 10;
int *ptrA = &a; //makes ptrA point to A
//another example: make a double pointer
double b = 3.14;
double ptrB = NULL;
ptrB = &b;
//wrong ways:
ptrB = b; //this sets it to some other memory address that may not belong to our program
ptrB = -10;//this set it to a nonexistent memory address- How do we manipulate the contents of memory using pointers?
- You can set what a pointer points to, but to change the contents of a memory location, you need to change the pointer into a "regular variable"
- You can use the dereferencing operator to change a pointer into a regular variable
- The dereferencing operator is simply the asterisk (star):
*
int a = 42;
int *ptrA = &a;
int c = 10 + a; //value of 52
//doing it via its pointer:
c = 10 + *ptrA; //*ptrA makes ptrA into a regular old variable, accessing its contents, also has a value of 52
//You can also indirectly modify the contents via a pointer:
*ptrA = 35;- There are no direct pointers in Java
- However, Java does have references
int a = 10; //this is a primitive variable type,
//no pointer access, there is *no* way to get its memory location
//the following are both objects and their variables
//s, x are references!
String s = "Hello";
String t = "Goodbye";
Integer x = 10;
String u = s;
s = "hello";
System.out.println(s);
System.out.println(u);- Most built-in types are immutable (
String, Double, Integer): once created, their contents cannot be changed - In general, immutability is a Very Good Thing: provides inherent thread safety
- Recall: you used
scanf("%lf", &b)to read in data from the standard input - That ampersand is passing
bby reference! - Passing by reference: passes the memory location of a variable instead
- A memory location is kind of a shared resource or "bucket" that both functions can access and manipulate
- If you only passed by value, then the function would only ever be able to return a single value and would never be able to make changes to your variables
- In C, you can change a regular variable into a reference (a pointer or memory location) using the referencing operator,
& - If you have a pointer variable, you can dereference it using the
*(star or dereferencing operator) - Pointers allow you to pass by reference so that functions can make changes to variables that are "seen" or "realized" in the calling function
- Passing by reference also allows you to "return" multiple values