Operating Systems
[22c:112]

Tutorial 3: A raw introduction to the C language you need
One of the most important, but often hard to learn, characteristic of the C language are the pointers. In C what we usually denote as a variable can in fact be: a simple variable, a structure, or a pointer.
Simple variables
A simple variable can be an integer (int), a long integer (long), a signed character(char), or an unsigned character. Refer to your C book for more explainations about types. The typical declaration of an integer variable is
int a; // this is a comment
the variable a is a simple integer variable. This mean that an operation like
a = 50;
b = 20;
will write the value 50 into the memory space reserved for the variable a, and 20 in the memory space reserved for variable b. Another operation like
c = a + b + 30;
will sum the current value of a to the current value of b to 30, and will write the result in the memory space reserved for variable c.
Pointers
Declaring a pointer is easy. For example
int *b;
will declare the variable b as a pointer to an integer. Since we can know the memory address assigned to a variable, inserting the & symbol before it's name, we can do something like
d = &a;
and so b will contain the memory address in which a is contained. In a similar way is possible to refeer to the memory address pointed by the variable, inserting the symbol * before the variable name. This will allow us to do something like
*d = *d + 30;
the computer will read the number saved inside b, will take it as a pointer and will go to read the value situated in that memory address. Then it will add 30 to that value, and will save the result of the operation in the same memory address. As consequence, if now we execute
printf("a=%d\n",a);
the output will be
a=80
Keep in mind that a declaration like
char *k[50];
has a special meanings. It's not a pointer to an array of chars, but an array of pointers to chars. That is, k[1] is a pointer to chars, k[2] is a pointer to chars, and so on...
Structures
A structure is very similar to a Java class, but without methods. You use structures when you need to represent more complex data, and there are no predefined types that can help you. For example, you can decide to represent a student in a structure that contain its name, lastname, phone number, student id, and so on. Such structure would have been declared in this way
struct student {
char name[20];
int studentid;
}
After the declaration of the structure, you can declare in your function how many structure you need, as if it would have been a normal type. For example
struct student g;
will declare g as a structure student. You can access to the fields of g using the dot symbol between the name of the variable and the name of the field like
g.id = 123;
If you cannot declare at the beginning of your software all the structure you will need things are a little bit more complicated. In the following section you will learn how to reserve memory for a pointer, so for now feel free to skip what follow, go to learn how a malloc() works, and come back later. If you cannot declare all the structure you need at the beginning of your software, you will have to do it "on-the-fly" during your software. Supposing that we have an empty pointer to a student structure declared as
struct student *p;
and that now we want to write in the memory that structure, we need to do few things. First, we need to allocate the necessary memory. This can be done manually, calculating how many bytes our structure need (DON'T DO THAT!!!) or automatically using the sizeof() function. We will reserve memory with something like
p = (struct student*) malloc(sizeof(struct student));
sounds complicate, uh? Start from the right: sizeof() calculated the dimension of a typical student structure and gave that number to the malloc() function. Then the operating system found in memory a continuous space with the right dimension, where to store our structure and returned it to the malloc() that returned its address. The address is a generic pointer, so we "casted" it to a struct student pointer. Then we saved the result in the variable p. Now all the generic rules to access to the fields of a structure are still valid, but instead of using the "dot" symbol to indicate the fields of the structure, you need to use -> as in
s->id = 246;
Remind also to free() that memory before exiting your software.
Strings and pointers
The hard part is that you can easily assign numbers to variables, but you can't do it so easily with strings as you probably are use to do in Java. For example, something like
g.name = "Alessio";
will produce errors during the compilation. This is because in C strings are collections of characters. In fact, one way to see a string in C is an array of character. That's the reason because if you declare
char name[50];
you can refer to the variable name as a pointer to a string of 50 char. You better remember this, because is very useful for scanning trought strings: printing name[1] or name+1 will have the same effect. All the strings should be declared as pointer to chars
char *s;
now s is a pointer to chars. You cannot directly write something in s, because for now it's nothing, just an empty pointer. It doesn't point anywhere. To reserve memory space where to store something you need to use the function malloc(). This function accept one parameter: an integer that represent the size of the memory space you need. Yes, you need to know it in advance. No arbitrary length strings in C, I'm sorry. The use of malloc() is simple
s = malloc(50);
Now s contains the address of a memory space of 50bytes that has been reserved for you by the operating system. The memory is not clean, you can find garbage in it, so you better clean it using the function memset() in this way
memset(g,0,50);
This will write fifty 0s in the memory, starting from the address pointed by g, in fact cleaning the memory. Even if it's not mandatory, every time you use the malloc() function you should cast it. The word "cast" in programming slang mean to force the type of a variable. The function malloc() return a pointer to an area of the main memory, but it don't know for what will that memory be used. So, it's good to force its output
s = (char*) malloc(50);
In this way, the malloc() function will return a pointer, that the operating system will recognize as a pointer to chars. Good programmers always do that, others doesn't. Another thing that you have to keep in mind is to free all the memory you allocated before exiting the program. If you forget, that little piece of memory you previously allocated and that contain your data, will be blocked until the machine will be rebooted. To free the memory you can use the function free() simply calling it with the name of the variable you want to free as
free(s);
Now s will point to nothing, or better, to NULL that is the word used in C to define "nothing". Even if now it may seem right, you still cannot write directly in that variable. To work with string, you have to use the str* class of C function: strcpy, strcat, strcmp, strdup, scanf. Type
man 3 strings
in a shell to learn more about these functions.
Example #1: Calculator
This is a simple calculator. It cycle and accept numbers from the user input until one of them is 0. When a 0 occurs it terminate and print the sum of the numbers.
#include <stdio.h> int main (void){ int tmp; // declare integer variable tmp int sum; // declare integer variable sum sum = 0; // reset sum value while (tmp!=0) { // while tmp is not 0 printf("> "); // print the prompt scanf("%d", &tmp); // wait for user input, followed by ENTER sum = sum + tmp; // sum the typed value to variable sum } printf("The sum is %d\n",sum); // print out the result return 0; }
Example #2: The greeter
This is another simple program, but is a good example. A pointer to chars is declared (name) and some memory (50bytes) is reserver, cleaned, and assigned to it. Then a question is displayed, and the answer of the user is waited. The answer is stored in the memory previously reserved and pointed by name. A simple greeting is then displayed, the reserved memory is free, and the software return.
#include <stdio.h> int main (void){ char *name; // declare pointer to chars name name = (char*) malloc(50); // reserve 50bytes in the memory memset(name,0,50); // clean the memory printf("What is your name? "); // print the prompt fgets(name,50,stdin); // wait for user input and save it printf("Goodmorning %s",name); // print the message free(name); // free the reserved memory for var "name" return 0; }
Example #3: Family members
Another simple program that introduce you to more complex definitions. First, there is a multiple declaration of chars pointer. Then, you can see a declaration with direct assignment. 100 bytes are assigned to the variable a and cleaned. Then a prompt is displayed, and the user is suppose to write a list of name separated by a comma. The while cycle will now scan the string looking for commas, and will print every piece of it on different line. Finally, when no more commas are in the strings, the software free the used memory, and return.
#include <stdio.h> #include <string.h> int main (void){ char *a, *b, *tmp; // declare pointer to chars int i=0; // declare integer i a = (char*) malloc(100); // reserve 50bytes in the memory b = a; memset(a,0,100); // clean the memory printf("> "); // print the prompt scanf("%s",a); // wait for user input and save it while (a!=NULL) { // cycle until a not equal to NULL tmp = strsep(&a,","); // analyze string looking for commas printf("[%d]: %s\n",i,tmp); // print block i++; // increase i variable } free(b); // free used memory return 0; }
Example #4: Command line arguments
The purpose of this software is to display the command line arguments that you pass to the software when you launch it. First thing to notice is the different declaration of the function main() that now has some parameters. The name of these parameters is not fixed, and you can choose the ones you want. Usually people use argc and argv. I decided to use prm_cnt and prm because I don't want you to think that there is only one way to do things in C. Modified the declaration, you can access to the passed parameters easily using the pointers in the array prm. The variable prm[0] is a pointer to a string containing the name of the file, prm[1] is a pointer to the first parameter, prm[2] to the second, and so on. The variable prm_cnt contain the number of given parameters plus one. As you can see this piece of code do nothing of interesting. It just print out all the parameters on separate lines. Then it check if there are more than 2 parameters and in that case, check if the first and the second parameters are equal. If so, it print a simple message.
#include <stdio.h> #include <string.h> int main (int prm_cnt, char *prm[]) { int i; // declaration of integer variable i printf("Parameters: %d\n",prm_cnt); // print how many parameters are given for (i=0; i<prm_cnt; i++) { // for each one of them printf("[%d] %s\n",i,prm[i]); // print out the parameter } if (prm_cnt>2) { // if parameters are more than 2 if (strcmp(prm[1],prm[2])==0) { // if parameters 1 and 2 are equal printf("[1] = [2]!\n"); // then print this sentence } } return 0; }
Example #5: A stupid shell
This is not the solution of your homework, neither something that is similar to a system shell. This is only something that will help you to understand how the exec function family work (man 3 execvp). After declaring a pointer to chars, and reserving some memory for user input with clean it. Then we cycle with the while until the command is not "quit". The software print the prompt and wait for user input. When user press enter the execlp function try to execute the command in the real shell, without parameters (the NULL is suppose to be a string, or an array, of parameters). If it work, the stupid shell end and the other software will be executed, if it doesn't work, the shell return the prompt. The function execlp(), and all the other functions of its family transfer the control from the current program to another, when called. So, when the other program terminate, also the "calling" software should terminate. If the other program terminate with error, the execlp() function return, and so doesn't allow the calling program to terminate. Since cmd is not equal to quit, the prompt is displayed again.
#include <stdio.h> #include <unistd.h> int main (void) { char *cmd; // declare a pointer to chars cmd = (char*) malloc(100); // allocate 100 bytes for the pointer memset(cmd,0,100); // clean the allocated memory while (strcmp(cmd,"quit")!=0) { // while cmd is not equal to quit printf("MyShell> "); // print "MyShell> " as a prompt scanf("%s",cmd); // wait for user input and save it in cmd execlp(cmd,NULL); // execute the command on the system } return 0; }