Functions, Pointers, and I/O

Functions

//Function Declaration/Prototype
int sum(int, int);
//or 
int sum(int a, int b);


//Function Definition: formal parameters
int sum(int x, int y){
	return x+y;
}


//Function Call: actual parameters (aka arguments)
sum(2, 3);


//Fun fact: The number of arguments that a function takes is called the "arity" of the function.
  • Return type if declaration missing is not assumed to be int always. If definition appears above call then return type is inferred from definition and if it appears after function call, then int is assumed and it leads to type mismatch compiler-error.

  • Empty parameter list in C mean that the parameter list is not specified and function can be called with any number and type of parameters.

  • Every function has a return type. If a function doesnโ€™t return any value, then void is used as return type.

void modify(struct *a){

	return;
}
  • We can specify no return type, in this case return type is assumed depending upon the compiler.
sum(int a, int b){			//valid, but not recommended
	return a+b;
}
  • In C, functions can return any type except arrays and functions. We can get around this limitation by returning pointer to array or pointer to function.

Parameter Passing Modes

Link: https://www.geeksforgeeks.org/parameter-passing-techniques-in-c-cpp/

  • Everything in C is pass by value but we send address of local variables of callee function so that we can change them from called function.
  • Pass by value creates a deep copy of the variable passed.
  • Arrays are always passed using pointers in C. (since its costly to create copy of array, so we send its address and store it a new callee local pointer variable)

Static Functions

Access to static functions is restricted to the file where they are declared i.e. external linkage is restricted for functions declared as static.

static int sum(int a, int b){
	return a+b;
}

Variadic Functions

Implemented using <stdarg.h>.

#include<stdio.h>
#include<stdarg.h>

int sum(int arg_count, ...) {	//first parameter must be count of arguments and ellipses (...)
	int sum = 0;

	va_list args;		//list type

	va_start(args, arg_count);		//must be called before accessing va_arg

	for (int i = 0; i < arg_count; ++i)
	{
		sum += va_arg(args, int);		//access. EVERYTIME its called, it fetches next element (be careful in min/max functions)
	}

	va_end(args);		//must be called at the ends

	return sum;
}

int main()
{
	printf("%d", sum(4, 1, 2, 3, 4));		//first parameter must be count of arguments
}

Callback Functions

Callbacks are functions that takes as parameter another function and call it. Implemented using function pointers.

#include<stdio.h>

void A()
{
	printf("I am function A\n");
}

// callback function
void B(void (*ptr)())
{
	(*ptr) (); 	// callback to A
}

int main()
{
	void (*ptr)() = &A;
	
	// calling function B and passing
	// address of the function A as argument
	B(ptr);

return 0;
}

exit(), abort(), and assert()

  • void exit(int status_code): code is returned to OIS. it can range from 0-255 and code % 256 if code is larger than 255. Deletes temp files, closes stream buffers. _Exit() doesn’t perform any cleanup tasks.

  • void abort(void): raises SIGABRT and quits program if no abort handler is defined. Doesn’t delete temp files and doesn’t close any open buffers.

  • void assert(int expression): sends expression, filename and line number to stderr and then calls abort() if false, otherwise nothing.

assert(x == 2);

Nested Functions

Not supported in C. We can define functions inside other functions, but can access only the ones in the same scope.

#include<stdio.h>

int main()
{
	printf("main ");

	int a = 9;
	int fun() {
		printf("%d", a);
	}

	fun();
}

//Output: main 9
#include <stdio.h>
int main(void)
{
	printf("Main");
	int fun()
	{
		printf("fun");

		// defining view() function inside fun()
		int view()
		{
			printf("view");
		}
	}
	view();			//can't call because definition is not in same scope, compiler-error
}

Pointers

Variables that store memory address of other variables.

  • type of pointer depends upon target data type whose address they store
  • type can also be void i.e void*
  • sizeof on them is compiler-dependent, also it doesn’t depend on the type whose address they store
//decalaring a pointer
int *a;
int* a;
int * a;

//assigning address to a pointer
int b;
int* a = &b;

int* c;		//uninitialized pointers are dangerous as they have garbage values

Operations on Pointers

/* <--- on pointers ---> */
-
++, --


/* <--- on integer constants and pointers ---> */
+, +=
-, -=
  • Minus(-) on pointers is useful to calculate memory between two elements in array which can then be divided by size of each element to get total number of elements between those two addresses

  • Dereferencing (*): Getting value at the address the pointer stores.

int b = 9;
int *a = &b;

printf("%d %d", a, *a);

//Output: 6553288 9

Double Pointer

int a = 8;
int* b = &a;
int **c = &b;
      c 		        b	           a
  _________		    _________	      ___	
 | 0xaddrB |  -->  | 0xaddrA |  -->  | 8 |
 |_________|       |_________|       |___|

Dangling, Void, Null, and Wild Pointers

int* a;		// wild pointer

int* a = NULL; 	// null pointer

NULL: NULL macro is defined as ((void *)0) in header files of most of the C compiler implementations. But C standard is saying that 0 is also a null pointer constant. It means that the following is also perfectly legal as per standard:

int * ptr = 0;

void Pointers: No dereferencing (*) is allowed and no pointer arithmetic is allowed.

void *a = NULL;

Dangling Pointers: If the variable to which pointer points to goes out of scope or memory block is deallocated, it is undefined behaviour.

const Pointers

//Difference between these?
const int *p, 
int *const p, 
const int *const p


//Up qualification and Down qualification
int a = 8;
const int b = 9;

int *x = &b;		 // down: const made as int [WARNING]
const int *y = &a;	 // up: int made as const

Function Pointers

They point to executable code’s start address and not some memory location unlike normal pointers, but function like normal pointers in all aspects.

int sum(int a, int b){
	return a+b;
}

int main(){

	int (*f)(int, int) = sum;		// since definition already present, or &sum 

	f(2, 3);		// 5
}

Pointers and Arrays

  • arrays are implemented using pointers, but array type is different than pointer type.
arr[3]
//becomes
*(arr + 3)

arr[2][3]
//becomes
*(*(arr + 3) + 4)

5[arr] 
//becomes
*(arr + 5)
  • array names convert to const pointers internally and represent address of the first element in array
int arr[] = {0};

arr == &arr[0]	  // true

Pointer Pitfalls

  • declaration along with other type
int *a, b;		// a is pointer, b is int
  • no implicit conversion of void*
void* p = NULL;
int a = 8;
p = &a;				// error
  • pointers can only be initialized with 0 (among decimal values)
int *a = 0;		//valid
int *b = 9;  	//invalid
  • in function pointers, we can use function name in assignment to pointer only if definition is done prior to such assignment.
#include <stdio.h>

int main(){

	int (*f)(int, int) = sum;		// error, use &sum

	printf("%d", f(2, 3));	
}

int sum(int a, int b){
	return a+b;
}

Input-Ouput (I/O)

Formatted I/O

printf(): Returns the total number of characters printed.

printf("%d", printf("Hi"));		// Hi2

char * a = "hi";
char* b = "yoi";
printf("%d", printf("%s%s", a, b));		// hiyoi5	

scanf(): Returns total number of inputs scanned succesfully.

char *a, *b;
printf("%d", scanf("%s %s", &a, &b));		// 2

Format Specifiers

%[flags][width][.precision][length]specifier

Ex: %-3.2lld, %-.3Lf

Reference

Cheat Sheet

%n specifier: stores and writes value equal to characters printed until it is encountered.

#include<stdio.h>
int main()
{
  int c;
  printf("john%nwick ", &c);
  printf("%d", c);
  return 0;
}

//Output: johnwick 4

* in printf(): We can insert custom width and length values using *.

char *s = "Hello World";
int n = 6;
printf("%.*s", n, s);

// Output: Hello
  • In scanf(), %d and %i differ while they’re the same for printf(). %d assumes input is given in base-10 and forces conversion and %i can take hex and octal inputs also.
scanf("%d", &a);		// 12
scanf("%d", &b);		// 012
scanf("%d", &c);		//0x12
printf("a = %i, b = %i, c = %i", a, b, c);		// 12, 12, 0


scanf("%d", &a);		// 12
scanf("%i", &b);		// 012
scanf("%i", &c);		//0x12
printf("a = %i, b = %i, c = %i", a, b, c);		// 12, 10, 18

Scansets

Reference

%[scanset]specifier
scanf("%[A-Z]s", &str);		// reads till first non A-Z character is encountered

scanf("%[^c]s", &str);		// reads till 'c' is encountered in an input

* as supression chaacter in scanf(): Reads specified characters but don’t asssign them.

scanf("%*d%c", &a);		// ignore integers till a character is encountered

scanf("%*s%d", &b);		// ignore string till an integer is encountered, be careful, qwerty1234 is read whole as string and scanf() ignores it whole and waits for an integer input

I/O Function List

Link

I/O Pitfalls

  • puts() puts \n by default after printing.
char s[100];
gets(s);			// world
puts("hello");
puts(s);

//  hello
//	world
  • scanf() stops reading at spaces and not suitable to read sentences. Use gets() instead.

  • scanf() leaves /n in buffer and others successive functions like gets() can read it errorneously.

scanf("%s", &a);
gets(b);

/* Input: hello
		  world

   Output: hello
		   \n
*/ 


// Fix: make scanf read a new line
scanf("%s\n", &s);
  • constant added with a string literal as argument to printf():
printf(5 + "HelloWorld");	  // World

printf("%c ", 5["HelloWorld"]);		// W

printf("%c ", "HelloWorld"[5]);		// W
  • fflush(stdin) after scanf() statement also clears the input buffer in certain compilers, it is not recommended to use it as it is undefined behavior by language standard.