Here are some “hints and kinks” for the advanced C-Programmer. You might encounter some task from university courses. It is not a good idea just to copy. Your lecturer might ask some additional questions.
On this page:
- Recursion
- Calling a function by reference
- Some remarks on strings in C
- Base64 Encoder
- Base64 Decoder
- Fibonacci Series
- Swapping 2 variables without using a temporary variable
Recursion
“Recursion” means that a function calls itself for a certain amount of repetitions. This is an elegant way for solving some special problems. Whenever you might consider that a function could be used repeatedly you should take that into account. But, never to be forgotten, a “halt” criteria must be implemented. Otherwise the loop will run infinitely and stack pointer will crash sooner or later.
Here is some simple example. The function “faculty()” calculates the faculty (!) of a given number. Example: 3! = 1 * 2 * 3 = 6; By the way: 0! = 1.
The “bail out” criteria here is that the value handed over to the next recursion always is diminished by 1 each time the function is called. As soon as it reaches 0 the recursed functions are quit one after another until control is handed over again to main().
long faculty(long x)
{
if((x - 1) > 0)
{
return x * faculty(x - 1);
}
else
{
return 1;
}
}
The function calls itself by multiplying the parameter x (just handed over) with x – 1. Thus the “loop” (not a real “loop” by the way) runs from the start (x) down to 0. After this the program returns to main(). If 0 is handed over, return value 1 is returned instantly.
Calling a function by reference
A function can not only be called by its name but also by its address in memory. Here is an example. The function power2() calculates the square value of a parameter handed over and returns that square value.
In this example we want to compute the integral calculus of a given function within defined boundaries by iterative means. The calling function main() hands over the function’s memory address to the next function integral(). This one uses the reference to call the power() function calculating the area under the function graph of the given function within the provided boundaries.
#include <stdio.h> #include <math.h> double power2(double x) { return x*x; } double integral(double (*fpower)(double),double b0,double b1,double step) { double c = b0; double r = 0; while(c < b1) { r += fpower(c) * step; c += step; } return r; } int main() { double r = integral(&power2, 0.0, 4.0, 0.001); printf("%lf\n", r); return 0; }
Some remarks on strings
Strings in C are somehow “special” when you’re coming from another programming language like BASIC, PASCAL or so. Strings in C are just some memory cells sequenced and terminated by a 0 (zero) content. The string “ABC” for example looks like this in the memory a processor handles:
65 | 66 | 67 | 0 |
Declaring a string
There are several ways to declare a string. The result will be the same, the handling will differ to a certain degree. First you can declare a string using 2 different methods:
Method “A” looks like:
char s[100];
And “B” is:
char *s;
For the latter method you have not declared a string but a pointer, where this string starts in memory. The expression *s is an address. What you have NOT is done is to reserve memory for this string. It can be achieved by allocating memory
#include <stdlib.h> ... s = (char*)malloc(100);
This memory allocation will reserve 100 Bytes starting at the memory address *s points to. The expression (char*) as a so-called “cast” operator is used to define the data type correctly. Some compilers need it, others don’t.
Defining a string
There are also several ways to assign a value (i. e. a content) to a string. First is by initialization:
char s[] = "This is a string";
which does the same like
char s[25] = "This is a string";
[] leaves the counting of the bytes and memory allocation to the compiler, [25] to the software developer.
Not a good idea is doing this:
char s[5] = "This is a string";
The memory space reserved will not be suffice to store the whole string, other variables might be overwritten.
Alternative:
char s[4] = {'A', 'B', 'C', 0};
“‘A'” is the ASCII code for 65, which, as you might guess, stands for the letter. So you can either type “65” or “‘A'”.
The value of “0” sometimes is written “\0” to make clear that it stands for the number and not for the character “0” (which has ASCII code 48 dec. resp. 0x30);
Defining a string on runtime
After the string variables have been initialized (or not), on runtime handling strings requires a different technique. xstr = “Hello world” does not work in C as xstr is not a string but the memory address of the first byte of the string. You have to use string functions. They are declared in <string.h> and defined in the respective library.
There is function called strcpy(str_dest, str_source). Check this out:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *src = "Hello!"; char *dest; dest = malloc(16); strcpy(dest, src); printf("%s", dest); return 0; }
strcpy() is a very simple function. Using a new name to not interfere with the “built in” C function we can imagine this as
void strcopy(char *d, char *s) { while(*s) { *d++ = *s++; } }
Please note that the function has no return value. The memory address of string d (for “destination”) is handed over and changing the string’s bytes takes place according to this start address of the string in memory. The copying runs as long as 0 (zero) value is not encountered. The “++” operator increases respective memory address by 1 each time the loop is repeated. Another alternative for implementing this function could be:
void strcopy(char *d, char *s) { int c = 0; do { d[c] = s[c]; } while(s[c++]); }
which does the same but on a different approach.
For a compact string bytes setting you can use direct access as well:
s[0] = 'A';s[1] = 'B'; s[2] = 'C'; s[3] = 0;
It should always be terminated by a zero value because you never know if the sting has been initialized correctly.
I usually initialize strings by loop when they don’t have an init value
#define MAXLEN 100 int main(void) { char *s; int i; s = malloc(MAXLEN); for(i = 0; i < MAXLEN; i++) { s[i] = 0; } }
This will prevent unwanted surprise…
Base64 Encoder function
Base64 is a method of encoding byte-sized binary (0..255) to a 6-bit pattern using only letters (A..Z, a..z), numbers (0..9) and 2 extra characters (+,/). The purpose is to be able to transfer binary data via SMTP message protocol which only supports 7-bit data width only. The relation between original data and Base64-codes data is:
(From Wikipedia)
Each3 bytes of input data are transformed into 4 bytes of Base64-data.
A C-function to do this can look like this:
#include <stdio.h> #include <stdlib.h> void encode_base64(char*, char*); void encode_base64(char *dest, char *src) { char *b64 ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int t0 = 0, t1 = 0, cnt = 0, l = 0; long x; while(src[l++]); while(t0 < (l - 1)) { x = (src[t0] << 16) + (src[t0 + 1] << 8) + src[t0 + 2]; for(t1 = 3; t1 >= 0; t1--) { dest[cnt++] = b64[(x & 0x3F << (t1 * 6)) >> (t1 * 6)]; } t0 += 3; } }
Two string pointers are used. One for the destination and one for the source. The function call is:
encode(p, "ABC..XYZ017639302919?=)(/&§");
where p is a pointer to an array of bytes for the destination.
Base64 Decoder function
And this is the corresponding decoder function:
void decode_base64(char *dest, char *src) { char *b64 ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int t0 = 0, t1, t2, cnt = 0, l = 0; long x; while(src[l++]); while(t0 < (l - 1)) { x = 0; for(t1 = 0; t1 <= 4; t1++) { for(t2 = 0; t2 < 63; t2++) { if(b64[t2] == src[t0 + t1]) { x += (long) t2 << ((3 - t1) * 6); } } } for(t2 = 2; t2 >= 0; t2--) { dest[cnt++] = (x & (0xFF << (t2 << 3))) >> (t2 << 3); } t0 += 4; } }
Fibonacci Series
The Fibonacci series is a consecutive series of numbers with the mathematical algorithm f.n = f.n-1 + f.n-2 (n>=3). Element #0 and #1 are 1 each, sometimes preceded by 0.
The resulting series starts as 0, 1, 1, 2, 3, 5, 8, 13,… etc.
Explanation: 0+1=1, 1+1=2, 1+2=3, 2+3=5, 3+5=8, 5+8=13.
This C code generates a respective series up to a limit fn. Parameter fn must be <100, no error check included!
long fibonacci(int fn) { int t1 = 2; int n[100]; n[0] = 0; n[1] = 1; while(t1 <= fn) { n[t1] = n[t1 - 1] + n[t1 - 2]; printf("%d ", n[t1]); t1++; } }
Swapping 2 variables without using a temporary variable
In coding interviews this is a popular question: “How can you swap 2 variables without using a temporay variable?”
The problem can be solved by “encoding” one variable into the other and recoding it later. This done by using the XOR link:
A XOR B in C programming language is: a ^ b. So, try this:
int main()
{
int a=10, b=6;
printf("a, b: %d %d\n", a, b);
a = a ^ b; //a=1010b, b=0110b -> a=1100b
b = b ^ a; //b=0110b, a=1100b -> b=1010b
a = a ^ b; //a=1100b, b=1010b -> a=0110b
printf("a, b: %d %d\n", a, b);
return 0;
}