C Arrays are collections sets of data elements. They are defined with a data type, an array name and an amount of locations to store data.
int A[5];
This will create an integer array named A
that can hold 5 integers.
A location can be populated by defining the location within the array.
A[0] = 6;
Arrays are created in the stack, but are accessed through the main function. Arrays can be explicitly populated by defining the data after the array declaration.
int main()
{
int A[5];
int B[5] = {2,4,6,8,10};
};
To print the contents of an array, you can use a for loop.
for (i=0; i<5; i++)
{
printf("%d", B[i]);
};
A structure is a collection of similar items. Its kind of like an object in an OOP language. It essentially groups the items. They can also be used to create data types. The parts of a structure are called properties.
struct Rectangle
{
int length;
int breadth;
};
The size of the structure is the total amount of memory consumed by its properties. The structure itself is just a definition, ergo it does not consume any space.
int main()
{
struct Rectangle r = {10,5};
};
In this case, the Rectangle structure contains 4 bytes (2 bytes per property) so the structure r will take up 4 bytes of memory regardless if it is initialized. Much like arrays, structures are created in the stack.
Once a structure is created, you can change the values within the structure using the dot operator.
r.length = 15;
r.breadth = 10;
printf("Area of Rectangle is %d", r.length * r.breadth);
Complex numbers are defined on two elements. The equation for a complex number is
where . The structure can be defined as follows:
struct Complex
{
int real;
int imaginary;
};
In a school, a student can have many types of information stored under their name, like many columns for one row in a database. They can have a term number, name, major, etc.
struct Student
{
int term;
char name[25];
char major[10];
char address[50];
};
If you add up the space required by each property, you will find that the structure takes up 77 bytes (assuming term
takes up 2 bytes).
A deck of playing cards can be defined by 3 properties: face value, suite, and color.
struct Card
{
int face[13];
int suite[4];
int color[2];
};
An array of structures can be created by defining the structure as an array.
int main()
{
struct Card deck[52];
};
It can then be initialized using child arrays. Though it would be more efficient to create a for loop.
deck[52] = {{1,0,0},{2,0,0},{3,0,0}...}
To print a bit of information on a card, call the array with the appropriate index and use the dot operator to specify the appropriate property.
printf("%d", deck[0].face);
A pointer is an address variable that is meant for storing the address of data, but not the data itself. They are used for indirectly accessing data. The main memory is divided into 3 parts: The main function, the stack, and the heap. The program can directly access the main function and the stack, but a pointer is required to access the heap. Additionally, if the program needs to access an external file or a physical device, a pointer will be required.
A data variable is declared as int a=10;
An address variable (pointer) is declared as int *p;
Lets assume that the variable a
is stored at the memory address location: [200/201]
. a
is two bytes large so it occupies the location 200 and 201. The pointer will be initialized with the following:
p = &a;
p
is a (2 bytes sized) variable that is stored inside the stack. Lets assume that it is stored at [210/211]
. &a
will have the value of 200 because it is pointing to the address location of a
, so p
= 200.
If you were to print the value of the pointer, you would get the address that it is pointing to. If you add the star operator *
however, it will assume you are trying to return the data on that address.
printf("%d", p); //returns 200
printf("%d", *p); //returns 10
Whenever you declare a variable (regardless if it is a pointer), it will be inside the stack. To initialize the data of a variable in the heap you will need to use the malloc
function. This requires the <stdlib.h>
library.
#include <stdlib.h>
int main()
{
int *p;
p = (int *)malloc(5*sizeof(int));
};
This will reserve space in the heap that is 5 integers large. The sizeof
function calculates the size of an integer and multiplies that by 5 to get the exact amount of space for 5 integers. The pointer is then initialized with the address of that space. Take note of the integer casting before the malloc
function.
Assume you have a Rectangle structure defined as the following:
struct Rectangle
{
int length;
int breadth;
};
int main()
{
struct Rectangle r = {10,5};
};
In this example, the Rectangle r
is initialized with the values 10 and 5. This structure is stored in the stack.
Now create a Rectangle as a pointer:
int main()
{
struct Rectangle r = {10,5};
struct Rectangle *p=&r;
};
Unlike a normal Rectangle which would use 4 bytes of space, a pointer will only use 2 bytes. This is assuming that the compiler considers an integer as 2 bytes. A pointer is only as large as an integer.
In this example, the pointer is referencing the memory location of Rectangle r, so to change a value through the pointer, you will need to either use the star operator or the arrow operator.
(*p).length = 20;
p->length = 20;
On line 1, parentheses are required around the pointer because the dot operator is executed first in the order of operations. Either line is valid C syntax, but the arrow operator is more heavily used for pointers.
To initialize the structure as a dynamic object in the heap, use the malloc
function. In order to initialize the pointer p
to this function, you have to typecast it as the Rectangle structure. This is because by default, a pointer as a void
data type.
int main()
{
struct Rectangle *p;
p = (struct Rectangle *)malloc(sizeof(struct Rectangle));
};
Now data can be added to the structure using the arrow operator.
p->length = 10;
p->breadth = 5;
A function is a piece of code that performs a specific task. Functions group together instructions to perform a task. Functions can also be referred to as Modules or Procedures. They are used to combine one or more repetitive instructions in the main function into a single definable instruction. The functions are defined outside the main function, but are called inside. Calling a function executes all of the instructions inside the function.
Programing without functions is defined as Monolithic Programming, where as programming with functions is defined as Modular Programming or Procedural Programming.
int add(int a, int b)
{
int c;
c = a + b;
return c;
};
int main()
{
int x,y,z;
x = 10;
y = 5;
Z = add(x,y);
printf("sum is %d", z);
};
In this example, a function called add
takes two parameters: a
and b. The function does 3 things:
c
.c
with the product of parameters a
and b
.c
as a result of calling the function.Inside the main function, the variables x
, y
, and z
are created. x
and y
are initialized with values and z
is initialized with the function add
having the attributes of x
and y
. The function will take inputs a
and b
and then output the result into z
.
In C, Function Parameters (the input values in a function definition) are called Formal Parameters.
Function Attributes (the input values in a function call) are called Actual Parameters.
Call By Value
void swap(int x, int y)
{
int temp;
temp = x;
x = y;
y = temp;
};
int main()
{
int a,b;
a = 10;
b = 20;
swap(a,b);
printf("%d %d", a,b); //outputs 10 20 (not swapped)
};
The concept of “Call By Value” means that the values passed in the actual parameters are “copied” to the formal parameters in the function. a
is copied to x
and b
is copied to y
. The formal parameters are modified, but the actual parameters remain the same, so the print statement will still output 10 and 20 in that order.
Call By Address
In “Call By Address”, the addresses of actual parameters are passed to formal parameters and formal parameters must be pointers. Any changes modified by the external function will also reflect the changes in the main function.
void swap(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
};
int main()
{
int a,b;
a = 10;
b = 20;
swap(&a,&b);
printf("%d %d", a,b); //outputs 20 10 (swapped)
};
The swap function call now uses the addresses of a
and b
instead of their value. This allows the creation of x
and y
as pointers in the swap definition. All of the changes made in the swap function will need to modify the data at those addresses (not the addresses themselves), so stars must be used before each pointer.
Passing an Array
void fun(int A[], int n)
{
int i;
for (i=0; i<n; i++)
printf("%d", A[i]);
};
int main()
{
int A[5] = {2,4,6,8,10}
fun(A,5);
};
To keep array passing functions modular, there should be at least 2 formal parameters: one for the array and one for the size of the array. That way the function will not assume the size of the array.
One thing to note is that in C, arrays can only be passed by address, not by value. The brackets next to A
in the formal parameters of fun
tell the compiler that A
is a pointer of type array
. An alternative way to write this is:
void fun(int *A, int n)
In this case, A
is considered a pointer of type void
so it can accept any integer or array. In general practice, it is better to use brackets if you know you will only pass an array as an actual parameter.
Returning an Array
int [] fun(int n)
{
int *p;
p = (int *)malloc(n*sizeof(int));
return(p);
};
int main()
{
int *A;
A = fun(5);
};
The square brackets before the function definition indicate that the function will be returning an array. In this example, the pointer A
is created and initialized with the function fun
passing 5. Inside the fun
function, a pointer p
is created and initialized with a 5 spaced array inside the heap. This array is then returned to the caller A
, so A
is now a 5 spaced array inside the heap.
It is worth noting that just like formal parameters, a void-pointer return type of a function can also return an array.
int * fun(int n)
In practice however, if the function is expected to only return an array, brackets should be used.
Structure as a Parameter (CBV)
struct Rectangle
{
int length;
int breadth;
};
int area(struct Rectangle r)
{
return r1.lenght*r1.breadth;
};
int main()
{
struct Rectangle r = {10,5};
printf("%d", area(r));
};
In this example, a Rectangle structure is created with properties length and breadth. The main function creates a Rectangle r
of this structure with the values 10 and 5. A print statement is made calling the area function with the Rectangle r
passed as its actual parameter. The area function does not need to have a structure return type to return a structure. Instead, the formal parameter needs to be of the same structure. The function then returns the product of the Rectangle’s length and breadth.
If the area function needs to change the values of the Rectangle, this Call By Value technique will not suffice, instead a Call By Reference technique is required.
Structure as a Parameter (CBR)
struct Rectangle
{
int length;
int breadth;
};
void changeLength(struct Rectangle *p, int l)
{
p->length = l;
};
int main()
{
struct Rectangle r = {10,5};
changeLength(&r,20);
};
In this example, a function called changeLength
is created. The goal of the function is to change the length of a Rectangle passed to it. This is done through pointers. Since the changeLength
function call is only passing the address of the Rectangle r
, the actual function can reference that address to change its data. This can only be done with pointers, otherwise, changes made in the changeLength
function would not affect the Rectangle in the main function.
Structure with an Array as a Parameter
If you are passing an array to a function, it must be passed by address. However, if the array is part of a structure, it can be passed by value.
struct Test
{
int A[5];
int n;
};
void fun(struct Test t1)
{
t1.A[0] = 10;
t1.A[1] = 9;
};
int main()
{
struct Test t = {{2,4,6,8,10}, 5};
fun(t);
};
Keep in mind however, that the changes in the array inside the fun function will not affect the array in the main function. The structure is passed by value, so the scope of the changes is limited to t1
only.