the first word below, void in this case, specifes to the compiler wether there will be a return value or not. and if there is a return value, what TYPE (and how big) it is. The word void specifically means this function does not return a value
void foo()
{
}
In actuality a return value is a value that is stored in a cpu register. On intel x86 architecture the register where the return value is stored is called $EAX or $RAX depending on the size of the type returned.
Registers are the fastest form of memory known to mankind. They are located phyiscally on the CPU in each core. In modern cpu's the register are 8 bytes wide that is 64 bits. So far I have mentioned $pc the program counter, which on an x86 cpu is called $RIP, but in gdb you can reference as $pc (program counter). The $rip registers stores the address in memory where the next instruction to be executed is stored. Now I am discussing the $rax register where function store RETURN values right before they exit.
here are some of the registers and the values stored there in hex and base10
Lets change foo so that it returns an TYPE called int. If we view the disassembly in gdb notice what happens right before we return from the function
int foo() { int y = 4; return 255; }
Now look what happens in main when we return from foo. The return value was stored in $eax so it is copied from $eax to the memory location specified by the symbol r. r happens to be located at the memory address specified by the $rbp register-4 bytes.
void main()
{
int r = foo();
}
The next part is of the defintion is the name of the function. In this case foo, which is a symbol for the starting address in memory where a sequence of cpu instructions begin.
// function defintion
void foo(void)
{
}
The part of the function definition in between the parentheses are things(values,bit patterns) that we want to send to the function in order for it to do it's work.. In this case because we are using the word void, Which means nothing we will send nothing to the function.
void foo(void)
{
}
Prior to function being called the Arguemntes are copied to some registers by the calling function. The called function then copies the values from the registers to it's local stack frame. we will soon talk about the stack frame
In many computer architectures, particularly x86-64, there's a specific order and set of registers used for passing arguments to functions.
lets see how the arguements are passed under the hood for this code.
int foo(int a, int b, int c, int d)
{
int y = a + b + c + d;
return y;
}
void main(void)
{
int p = foo(1 ,2, 3,4);
}
Combined are called the FUNCTION SIGNATURE or the FUNCTION PROTOTYPE
The signature or protoype defines the cirumstances of the function, how is it called, what does it take, and what does it return
void foo(void) //Function prototype or signature
{
}
after the function signature/prototyp, we have the function body. The part in between the curly brackets is the function body. When the function is called the code that is in the body is what will be executed on the cpu
void foo(void) //Function prototype or signature
{
//Function Body in between the curly braces
}
A function definition has a signature(aka prototype) and a body, as shown in the previous slide. If you just have the signature/prototype by itself and put a semi colon at the end. It is called a function declaration.
void foo(void); //Function declaration.
C/C++ allow you to complete the compilation step of your code. As long as a function declaration is present prior to any function calls that you make to that function. This is called a FORWARD DECLARATION.
HOWEVER, if you never provide a full function definition the linker step will fail when it tries to resolve the symbols it is looking for in the function call.
When you include a standard header file like math.h You are including forward declarations for any function from that library that you want to use. The linker step will look for the complete function definition when it tried to resolve the function call. if it can't find it, you will get a linker error.
// function declaration
//function defintion
void foo(void) //Function prototype or signature
{
//Function Body in between the curly braces
}
All statements in c end in a semicolon, white space is disregarded. The semi colon tells the compiler this is the end of the statement. It is completely ok to make a statement like this. It is not adviseable but will compile. White space only effects symbol names.
int x,
y
,
p =
3;
C evaluates the following symbol '\n' as the number 10 (0xA). The '\' is an escape character. When the c compiler encounters the escape character in a string or delimted by single quotes, it does not evaluate it as a plain text backslash, it evaluates the \ and the next character as a control sequence. If you want to insert an actual backslash you would type two \\
No you cannot. Typing the return key within a "string" will give you compilation error. You must type the control sequence of the ascii char you want to represent. If you want and end of line you type '\n'. On windows systems you do not need to type "some sentence \r\n" The c compiler will automatically make the adjustement for targeting windows systems. However if you inspect a plain text file on a windows systems the last two bytes of each line will be 00001101 00001010
A CPU is just an engine for manipulating numerical values. Memory is a gigantic store of these numeric values. The processor pulls numeric values from memory into registers, operates on them and puts the resulting numeric value back into memory. That is all a cpu does. Programming is just telling the CPU different ways to manipulate those values to produce some outcome.
In c, when we are programming, we can ask for some SPACE in memory for one of these numeric values. When we declare a variable, we are asking for some space and giving that space a name. ie
int Integer;
Asks for space for a numeric value of type int that we name Integer. Each TYPE will have a certain amount of space associated with it.
The = sign in programming is different than = in math. The = sign in Programming means assignment. It tells the cpu to take that space that we requested and set the value there to what is specified on the RIGHT SIDE of the = in the statement.
Integer = 5;
Integer = 5 + 2;
Integer = Integer + 1;
char | 1 byte |
short | 2 byte |
int | 4 byte |
long | 8 byte |
char Number = 100; uses movb for mov byte
short Number = 100; uses movw for mov word 2 bytes
int Number = 100; uses movl for move long 4 bytes
long Number = 100; use movq for quadword 8 bytes
An 8 bit value, 1 byte can represent 256 different values. That does not necessraily mean those values are 0 to 255. By default if I use the char keyword, I am saying I negative values as well as postivie ones using 8 bits. That means 8 bits can be interpreted as different numeric values.
We can add the additional keyword unsigned to let the compiler know we only want to treat the values as positive numbers. We can also explicity add the keyword signed, which is the default setting of whole number numeric types.
char SmallS; // -128 to 127
char unsigned SmallU; // 0 - 255
void main(void)
{
char unsigned SmallU = 255; // 11111111
printf("%d\n", SmallU);
printf("%d\n", (char) SmallU);
}
$ ./a.out
255
-1
void main(void)
{
char unsigned OverFlow = 254;
OverFlow = OverFlow + 1;
OverFlow = OverFlow + 1;
/* What will be the value of OverFlow */
}
live debug?
An ascii String in C is a series of Bytes, where the value of each byte is a value on the ascii table, From 0 to 127, where the value 0 is considedred The NULL character. In c, A string is enclosed in "quotes". The variable name that we use to refer to the string holds the address of the first bytes of the string. All strings in C are null terminated, meaning they end in a byte with the value of 0.