Update tuto

This commit is contained in:
Miloslav Ciz 2022-04-03 17:01:43 +02:00
parent 6ff9159889
commit 56f93e1f56

View file

@ -795,7 +795,7 @@ As a bonus, let's see a few useful compiler flags:
- `-c`: Compile only (generate object files, do not link).
- `-g`: Include debug symbols, this will be important for [debugging](debugging.md).
## Advanced Data Types and Variables (Structs, Arrays)
## Advanced Data Types and Variables (Structs, Arrays, Strings)
Until now we've encountered simple data types such as `int`, `char` or `float`. These identify values which can take single atomic values (e.g. numbers or text characters). Such data types are called **[primitive types](primitive_type.md)**.
@ -888,9 +888,144 @@ Arrays can also be multidimensional, but we won't bothered with that right now.
Why are arrays so important? They allow us to work with great number of data, not just a handful of numeric variables. We can create an array of million structs and easily work with all of them thanks to indexing and loops, this would be practically impossible without arrays. Imagine e.g. a game of [chess](chess.md); it would be very silly to have 64 plain variables for each square of the board (`squareA1`, `squareA2`, ..., `squareH8`), it would be extremely difficult to work with such code. With an array we can represent the square as a single array, we can iterate over all the squares easily etc.
string
One more thing to mention about arrays is how they can be passed to functions. A function can have as a parameter an array of fixed or unknown length. There is also one exception with arrays as opposed to other types: **if a function has an array as parameter and the function modifies this array, the array passed to the function (the argument) will be modified as well** (we say that arrays are *passed by reference* while other types are *passed by value*). We know this wasn't the case with other parameters such as `int` -- for these the function makes a local copy that doesn't affect the argument passed to the function. The following example shows what's been said:
```
#include <stdio.h>
// prints an int array of lengt 10
void printArray10(int array[10])
{
for (int i = 0; i < 10; ++i)
printf("%d ",array[i]);
}
// prints an int array of arbitrary lengt
void printArrayN(int array[], int n)
{
for (int i = 0; i < n; ++i)
printf("%d ",array[i]);
}
// fills an array with numbers 0, 1, 2, ...
void fillArrayN(int array[], int n)
{
for (int i = 0; i < n; ++i)
array[i] = i;
}
int main(void)
{
int array10[10];
int array20[20];
fillArrayN(array10,10);
fillArrayN(array20,20);
printArray10(array10);
putchar('\n');
printArrayN(array20,20);
return 0;
}
```
The function `printArray10` has a fixed length array as a parameter (`int array[10]`) while `printArrayN` takes as a parameter an array of unknown length (`int array[]`) plus one additional parameter to specify this length (so that the function knows how many items of the array it should print). The function `printArray10` is important because it shows how a function can modify an array: when we call `fillArrayN(array10,10);` in the `main` function, the array `array10` will be actually modified after when the function finishes (it will be filled with numbers 0, 1, 2, ...). This can't be done with other data types (though there is a trick involving [pointers](pointer.md) which we will learn later).
Now let's finally talk about **text [strings](string.md)**. We've already seen strings (such as `"hello"`), we know we can print them, but what are they really? A string is a data type, and from C's point of view strings are nothing but **arrays of `char`s** (text characters), i.e. sequences of `char`s in memory. **In C every string has to end with a 0 `char`** -- this is NOT `'0'` (whose [ASCII](ascii.md) value is 48) but the direct value 0 (remember that `char`s are really just numbers). The 0 `char` cannot be printed out, it is just a helper value to terminate strings. So to store a string `"hello"` in memory we need an array of length at least 6 -- one for each character plus one for the terminating 0. These types of string are called **zero terminated strings** (or *C strings*).
When we write a string such as `"hello"` in our source, the C compiler creates an array in memory for us and fills it with characters `'h'`, `'e'`, `'l'`, `'l'`, `'o'`, 0. In memory this may look like a sequence of numbers 104, 101, 108, 108 111, 0.
Why do we terminate strings with 0? Because functions that work with strings (such as `puts` or `printf`) don't know what length the string is. We can call `puts("abc");` or `puts("abcdefghijk");` -- the string passed to `puts` has different length in each case, and the function doesn't know this length. But thanks to these strings ending with 0, the function can compute the length, simply by counting characters from the beginning until it finds 0 (or more efficiently it simply prints characters until it finds 0).
The [syntax](syntax.md) that allows us to create strings with double quotes (`"`) is just a helper (*syntactic sugar*); we can create strings just as any other array, and we can work with them the same. Let's see an example:
```
#include <stdio.h>
int main(void)
{
char alphabet[27]; // 26 places for letters + 1 for temrinating 0
for (int i = 0; i < 26; ++i)
alphabet[i] = 'A' + i;
alphabet[26] = 0; // terminate the string
puts(alphabet);
return 0;
}
```
`alphabet` is an array of `char`s, i.e. a string. Its length is 27 because we need 26 places for letters and one extra space for the terminating 0. Here it's important to remind ourselves that we count from 0, so the alphabet can be indexed from 0 to 26, i.e. 26 is the last index we can use, doing `alphabet[27]` would be an error! Next we fill the array with letters (see how we can treat `char`s as numbers and do `'A' + i`). We iterate while `i < 26`, i.e. we will fill all the places in the array up to the index 25 (including) and leave the last place (with index 26) empty for the terminating 0. That we subsequently assign. And finally we print the string with `puts(alphabet)` -- here note that there are no double quotes around `alphabet` because its a variable name. Doing `puts("alphabet")` would cause the program to literally print out `alphabet`. Now the program outputs:
```
ABCDEFGHIJKLMNOPQRSTUVWXYZ
```
In C there is a standard library for working with strings called *string* (`#include <string.h>`), it contains such function as `strlen` for computing string length or `strcmp` for comparing strings.
One final example -- a creature generator -- will show all the three new data types in action:
```
#include <stdio.h>
#include <stdlib.h> // for rand()
typedef struct
{
char name[4]; // 3 letter name + 1 place for 0
int weightKg;
int legCount;
} Creature; // some weird creature
Creature creatures[100]; // global array of Creatures
void printCreature(Creature c)
{
printf("Creature named %s ",c.name); // %s prints a string
printf("(%d kg, ",c.weightKg);
printf("%d legs)\n",c.legCount);
}
int main(void)
{
// generate random creatures:
for (int i = 0; i < 100; ++i)
{
Creature c;
c.name[0] = 'A' + (rand() % 26);
c.name[1] = 'a' + (rand() % 26);
c.name[2] = 'a' + (rand() % 26);
c.name[3] = 0; // terminate the string
c.weightKg = 1 + (rand() % 1000);
c.legCount = 1 + (rand() % 10); // 1 to 10 legs
creatures[i] = c;
}
// print the creatures:
for (int i = 0; i < 100; ++i)
printCreature(creatures[i]);
return 0;
}
```
When run you will see a list of 100 randomly generated creatures which may start e.g. as:
```
Creature named Nwl (916 kg, 4 legs)
Creature named Bmq (650 kg, 2 legs)
Creature named Cda (60 kg, 4 legs)
Creature named Owk (173 kg, 7 legs)
Creature named Hid (430 kg, 3 legs)
...
```
## Macros/Preprocessor