Merge pull request #19 from PaulSchulz/main

Some more changes for readabilty.
This commit is contained in:
ToshioCP 2022-03-05 10:32:17 +09:00 committed by GitHub
commit 1d4741bdb9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 332 additions and 314 deletions

View file

@ -2,15 +2,18 @@ Up: [Readme.md](../Readme.md), Prev: [Section 5](sec5.md), Next: [Section 7](se
# String and memory management # String and memory management
GtkTextView and GtkTextBuffer have functions that have string parameters or return a string. GtkTextView and GtkTextBuffer have functions that use string parameters or return a string.
The knowledge of strings and memory management is very important for us to understand the functions above. The knowledge of strings and memory management is useful to understand how to use these functions.
## String and memory ## String and memory
String is an array of characters that is terminated with '\0'. A String is an array of characters that is terminated with '\0'.
String is not a C type such as char, int, float or double. Strings are not a C type such as char, int, float or double,
But the pointer to a character array behaves like a string type of other languages. but exist as a pointer to a character array. They behaves like a string type
So, the pointer is often called string. which you may be familiar from other languages.
So, this pointer is often called 'a string'.
In the following, `a` and `b` defined as character arrays, and are strings.
~~~C ~~~C
char a[10], *b; char a[10], *b;
@ -27,46 +30,50 @@ b = a;
/* *(++b) is 'e' */ /* *(++b) is 'e' */
~~~ ~~~
The array `a` has `char` elements and the size is ten. The array `a` has `char` elements and the size of ten.
The first six elements are 'H', 'e', 'l', 'l', 'o' and '\0'. The first six elements are 'H', 'e', 'l', 'l', 'o' and '\0'.
This array represents a string "Hello". This array represents the string "Hello".
The first five elements are character codes that correspond to the characters. The first five elements are character codes that correspond to the characters.
The sixth element is '\0' that is the same as zero. The sixth element is '\0', which is the same as zero,
And it indicates that the string ends there. and indicates that the string ends there.
The size of the array is 10, so 4 bytes aren't used, but it's OK. The size of the array is 10, so 4 bytes aren't used, but that's OK,
They are just ignored. they are just ignored.
The variable 'b' is a pointer to a character. The variable 'b' is a pointer to a character.
Because `a` is assigned to `b`, `a` and `b` point the same character ('H'). Because `b` is assigned to be `a`, `a` and `b` point the same character ('H').
The variable `a` is defined as an array and it can't be changed. The variable `a` is defined as an array and it can't be changed.
It always point the top address of the array. It always point the top address of the array.
On the other hand, pointers are mutable, so `b` can change itself. On the other hand, 'b' is a pointer, which is mutable, so `b` can be change.
It is possible to write `++b` (`b` is increased by one). It is then possible to write statements like `++b`, which means take the value in b (n address),
increase it by one, and store that back in `b`.
If a pointer is NULL, it points nothing. If a pointer is NULL, it points to nothing.
So, the pointer is not a string. So, the pointer is not a string.
Programs with string will include bugs if you aren't careful about NULL pointer. A NULL string on the other hand will be a pointer which points to a location
that contains `\0`, which is a string of length 0 (or "").
Programs that use strings will include bugs if you aren't careful when using NULL pointers.
Another annoying problem is memory that a string is allocated. Another annoying problem is the memory that a string is allocated.
There are four cases. There are four cases:
- The string is read only. - The string is read only;
- The string is in static memory area. - The string is in static memory area;
- The string is in stack. - The string is in stack; and
- The string is in memory allocated from the heap area. - The string is in memory allocated from the heap area.
## Read only string ## Read only string
A string literal in C program is surrounded by double quotes. A string literal in a C program is surrounded by double quotes and written as the following:
~~~C ~~~C
char *s; char *s;
s = "Hello" s = "Hello"
~~~ ~~~
"Hello" is a string literal. "Hello" is a string literal, and is stored in program memory.
A string literal is read only. A string literal is read only.
In the program above, `s` points the string literal. In the program above, `s` points the string literal.
So, the following program is illegal. So, the following program is illegal.
~~~C ~~~C
@ -76,18 +83,22 @@ So, the following program is illegal.
The result is undefined. The result is undefined.
Probably a bad thing will happen, for example, a segmentation fault. Probably a bad thing will happen, for example, a segmentation fault.
NOTE: The memory of the literal string is allocated when the program is
compiled. It is possible to view all the literal strings defined in your program
by using the `string` command.
## Strings defined as arrays ## Strings defined as arrays
If a string is defined as an array, it's in static memory area or stack. If a string is defined as an array, it's in either stored in the static memory area or stack.
It depends on the class of the array. This depends on the class of the array.
If the array's class is `static`, then it's placed in static memory area. If the array's class is `static`, then it's placed in static memory area.
It keeps its value and remains for the life of the program. This allocation and memory address is fixed for the life of the program.
This area is writable. This area can be changed and is writable.
If the array's class is `auto`, then it's placed in stack. If the array's class is `auto`, then it's placed in stack.
If the array is defined in a function, its default class is `auto`. If the array is defined inside a function, its default class is `auto`.
The stack area will disappear when the function returns to the caller. The stack area will disappear when the function exits and returns to the caller.
stack is writable. Arrays defined on the stack are writable.
~~~C ~~~C
@ -105,30 +116,30 @@ print_strings (void) {
} }
~~~ ~~~
The array `a` is defined externally to functions. The array `a` is defined externally to a function and is global in its scope.
Such variables are placed in static memory area even if the `static` class is left out. Such variables are placed in static memory area even if the `static` class is left out.
First, the compiler calculates the number of the elements in the right hand side. The compiler calculates the number of the elements in the right hand side (six),
It is six. and then creates code that allocates six bytes in the static memory area and copies the data to this memory.
The compiler allocates six bytes memory in the static memory area and copies the data to the memory.
The array `b` is defined inside the function. The array `b` is defined inside the function
So, its class is `auto`. so its class is `auto`.
The compiler calculates the number of the elements in the string literal. The compiler calculates the number of the elements in the string literal.
It is six because the string is zero terminated. It has six elements as the zero termination character is also included.
The compiler allocates six bytes memory in the stack and copies the data to the memory. The compiler creates code which allocates six bytes memory in the stack and copies the data to the memory.
Both `a` and `b` are writable. Both `a` and `b` are writable.
The memory is managed by the executable program. The memory is managed by the executable program.
You don't need to program to allocate or free the memory for `a` and `b`. You don't need your program to allocate or free the memory for `a` and `b`.
The array `a` remains for the life of the program. The array `a` is created then the program is first run and remains for the life of the program.
The array `b` disappears when the function returns to the caller. The array `b` is created on the stack then the function is called, disappears when the function returns.
## Strings in the heap area ## Strings in the heap area
You can get memory from the heap area and put back the memory to the heap area. You can also get, use and release memory from the heap area.
The standard C library provides `malloc` to get memory and `free` to put back memory. The standard C library provides `malloc` to get memory and `free` to put back memory.
Similarly, GLib provides `g_new` and `g_free`. GLib provides the functions `g_new` and `g_free` to do the same thing, with support for
some additional Glib functionality.
~~~C ~~~C
g_new (struct_type, n_struct) g_new (struct_type, n_struct)
@ -165,7 +176,7 @@ If `mem` is NULL, `g_free` does nothing.
`gpointer` is a type of general pointer. `gpointer` is a type of general pointer.
It is the same as `void *`. It is the same as `void *`.
This pointer can be casted to any pointer type. This pointer can be casted to any pointer type.
Conversely, any pointer type can be casted to gpointer. Conversely, any pointer type can be casted to `gpointer`.
~~~C ~~~C
g_free (s); g_free (s);
@ -175,10 +186,10 @@ g_free (t);
/* Frees the memory allocated to t. */ /* Frees the memory allocated to t. */
~~~ ~~~
If the argument doesn't point allocated memory, it will cause an error, for example, segmentation fault. If the argument doesn't point allocated memory it will cause an error, specifically, a segmentation fault.
Some Glib functions allocate memory. Some Glib functions allocate memory.
For example, `g_strdup` allocates memory and copy a string given as an argument. For example, `g_strdup` allocates memory and copies a string given as an argument.
~~~C ~~~C
char *s; char *s;
@ -196,8 +207,11 @@ The following is extracted from the reference.
> The returned string should be freed with `g_free()` when no longer needed. > The returned string should be freed with `g_free()` when no longer needed.
The reference usually describes if the returned value needs to be freed. The function reference will describe if the returned value needs to be freed.
If you forget to free the allocated memory, it causes memory leak. If you forget to free the allocated memory it will remain allocated. Repeated use will cause
more memory to be allocated to the program, which will grow over time. This is called a memory leak,
and the only way to address this bug is to close the program (and restart it),
which will automatically release all of the programs memory back to the system.
Some GLib functions return a string which mustn't be freed by the caller. Some GLib functions return a string which mustn't be freed by the caller.
@ -207,10 +221,10 @@ g_quark_to_string (GQuark quark);
~~~ ~~~
This function returns `const char*` type. This function returns `const char*` type.
The qualifier `const` means immutable. The qualifier `const` means that the returned value is immutable.
Therefore, the characters pointed by the returned value aren't be allowed to change or free. The characters pointed by the returned value aren't be allowed to be changed or freed.
If a variable is qualified with `const`, the variable can't be assigned except initialization. If a variable is qualified with `const`, the variable can't be assigned except during initialization.
~~~C ~~~C
const int x = 10; /* initialization is OK. */ const int x = 10; /* initialization is OK. */
@ -218,5 +232,4 @@ const int x = 10; /* initialization is OK. */
x = 20; /* This is illegal because x is qualified with const */ x = 20; /* This is illegal because x is qualified with const */
~~~ ~~~
Up: [Readme.md](../Readme.md), Prev: [Section 5](sec5.md), Next: [Section 7](sec7.md) Up: [Readme.md](../Readme.md), Prev: [Section 5](sec5.md), Next: [Section 7](sec7.md)

View file

@ -6,21 +6,22 @@ Up: [Readme.md](../Readme.md), Prev: [Section 6](sec6.md), Next: [Section 8](se
### G\_APPLICATION\_HANDLES\_OPEN flag ### G\_APPLICATION\_HANDLES\_OPEN flag
GtkTextView, GtkTextBuffer and GtkScrolledWindow have given us a minimum editor in the previous section. The GtkTextView, GtkTextBuffer and GtkScrolledWindow widgets have given us a minimum editor
Next, we will add a function to read a file and remake the program into a file viewer. in the previous section.
There are many ways to implement the function. We will now add a function to read a file and rework the program into a file viewer.
Because this is a tutorial for beginners, we'll take the easiest one. There are many ways to implement the function and
because this is a tutorial for beginners, we'll take the easiest one.
When the program starts, we give a filename as an argument. When the program starts, we will give the filename to open as an argument.
$ ./a.out filename $ ./a.out filename
Then it opens the file and inserts its contents into the GtkTextBuffer. It will open the file and insert its contents into the GtkTextBuffer.
At the beginning of the implementation, we need to know how GtkApplication (or GApplication) recognizes arguments. To do this, we need to know how GtkApplication (or GApplication) recognizes arguments.
It is described in the [GIO API Reference, Application](https://docs.gtk.org/gio/class.Application.html). This is described in the [GIO API Reference, Application](https://docs.gtk.org/gio/class.Application.html).
When GtkApplication is created, a flag (its type is GApplicationFlags) is given as an argument. When GtkApplication is created, a flag (with the type GApplicationFlags) is provided as an argument.
~~~C ~~~C
GtkApplication * GtkApplication *
@ -28,9 +29,9 @@ gtk_application_new (const gchar *application_id, GApplicationFlags flags);
~~~ ~~~
This tutorial explains only two flags, `G_APPLICATION_FLAGS_NONE` and `G_APPLICATION_HANDLES_OPEN`. This tutorial explains only two flags, `G_APPLICATION_FLAGS_NONE` and `G_APPLICATION_HANDLES_OPEN`.
If you want to handle command line arguments, `G_APPLICATION_HANDLES_COMMAND_LINE` flag is what you need. If you want to handle command line arguments, the `G_APPLICATION_HANDLES_COMMAND_LINE` flag is what you need.
How to program it is written in [GIO API Reference, g\_application\_run](https://docs.gtk.org/gio/method.Application.run.html). How to use the new application method is described in [GIO API Reference, g\_application\_run](https://docs.gtk.org/gio/method.Application.run.html),
And the flag is described in the [GIO API Reference, ApplicationFlags](https://docs.gtk.org/gio/flags.ApplicationFlags.html). and the flag is described in the [GIO API Reference, ApplicationFlags](https://docs.gtk.org/gio/flags.ApplicationFlags.html).
~~~ ~~~
GApplicationFlags' Members GApplicationFlags' Members
@ -41,18 +42,15 @@ G_APPLICATION_HANDLES_OPEN This application handles opening files (in the prima
... ... ... ... ... ...
~~~ ~~~
There are ten flags. There are ten flags in total, but we only need two of them so far.
But we only need two of them so far. We've already used `G_APPLICATION_FLAGS_NONE`, as
We've already used `G_APPLICATION_FLAGS_NONE`. it is the simplest option, and no arguments are allowed.
It is the simplest option. If you provide arguments when running the application, an error will occur.
No argument is allowed.
If you give arguments and run the application, then error occurs.
`G_APPLICATION_HANDLES_OPEN` is the second simplest option. The flag `G_APPLICATION_HANDLES_OPEN` is the second simplest option.
It allows arguments but only files. It allows arguments but only files.
The application assumes all the arguments are filenames. The application assumes all the arguments are filenames and we will use this flag when creating
our GtkApplication.
Now we use this flag when creating GtkApplication.
~~~C ~~~C
app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN); app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
@ -60,12 +58,12 @@ app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPE
### open signal ### open signal
When the application starts, two signals are possible. Now, when the application starts, two signals can be emitted.
- activate signal --- This signal is emitted when there's no argument. - activate signal --- This signal is emitted when there's no argument.
- open signal --- This signal is emitted when there is at least one argument. - open signal --- This signal is emitted when there is at least one argument.
The handler of "open" signal is defined as follows. The handler of the "open" signal is defined as follows.
~~~C ~~~C
void user_function (GApplication *application, void user_function (GApplication *application,
@ -83,22 +81,22 @@ The parameters are:
- `hint` --- a hint provided by the calling instance (usually it can be ignored) - `hint` --- a hint provided by the calling instance (usually it can be ignored)
- `user_data` --- user data set when the signal handler was connected. - `user_data` --- user data set when the signal handler was connected.
The way how to read a file (GFile) will be described in the next subsection. How to read a specified file (GFile) will be described next.
## Making a file viewer ## Making a file viewer
### What is a file viewer? ### What is a file viewer?
A file viewer is a program that shows a text file given as an argument. A file viewer is a program that displays the text file that is given as an argument.
It works as follows. Our file viewer will work as follows.
- If it is given arguments, it recognizes the first argument as a filename and opens it. - When arguments are given, it treats the first argument as a filename and opens it.
- If opening the file succeeds, it reads the contents of the file and inserts it to GtkTextBuffer and shows the window. - If opening the file succeeds, it reads the contents of the file and inserts it to GtkTextBuffer and then shows the window.
- If it fails to open the file, shows an error message and quits. - If it fails to open the file, it will show an error message and quit.
- If there's no argument, it shows an error message and quits. - If there's no argument, it will shows an error message and quit.
- If there are two or more arguments, the second one and after are ignored. - If there are two or more arguments, the second one and any others are ignored.
The program is as follows. The program which does this is shown below.
~~~C ~~~C
1 #include <gtk/gtk.h> 1 #include <gtk/gtk.h>
@ -159,7 +157,6 @@ The program is as follows.
56 g_object_unref (app); 56 g_object_unref (app);
57 return stat; 57 return stat;
58 } 58 }
59
~~~ ~~~
Save it as `tfv3.c`. Save it as `tfv3.c`.
@ -170,25 +167,25 @@ Then compile and run it.
![File viewer](../image/screenshot_tfv3.png) ![File viewer](../image/screenshot_tfv3.png)
Now I want to explain the program `tfv3.c`. Let's explain how the program `tfv3.c` works.
First, the function `main` changes in only two lines from the previous version. First, the function `main` has only two changes from the previous version.
- `G_APPLICATION_FLAGS_NONE` is replaced by `G_APPLICATION_HANDLES_OPEN`. - `G_APPLICATION_FLAGS_NONE` is replaced by `G_APPLICATION_HANDLES_OPEN`; and
- `g_signal_connect (app, "open", G_CALLBACK (on_open), NULL)` is added. - `g_signal_connect (app, "open", G_CALLBACK (on_open), NULL)` is added.
Next, the handler `app_activate` is now very simple. Next, the handler `app_activate` is added and is very simple.
It just outputs the error message. It just outputs the error message and
The application quits immediately because no window is created. the application quits immediately because no window is created.
The point is the handler `app_open`. The main functionality is the in the handler `app_open`. It
- It creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them. - Creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them together;
- Sets wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView. - Sets wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView;
- Sets GtkTextView to non-editable because the program isn't an editor but only a viewer. - Sets GtkTextView to non-editable because the program isn't an editor but only a viewer;
- Reads the file and inserts the text into GtkTextBuffer (this will be explained in detail later). - Reads the file and inserts the text into GtkTextBuffer (this will be explained in detail later); and
- If the file is not opened then outputs an error message and destroys the window. It makes the application quit. - If the file is not opened then outputs an error message and destroys the window. This makes the application quit.
The file reading part of the program is shown again below. The following is the important file reading part of the program and is shown again below.
~~~C ~~~C
if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) { if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) {
@ -208,29 +205,32 @@ if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) {
} }
~~~ ~~~
The function `g_file_load_contents` loads the file contents into a buffer, which is automatically allocated, and sets `contents` to point the buffer. The function `g_file_load_contents` loads the file contents into a buffer,
And the length of the buffer is set to `length`. which is automatically allocated and sets `contents` to point that buffer.
It returns `TRUE` if the file's contents are successfully loaded. `FALSE` if an error happens. The length of the buffer is set to `length`.
It returns `TRUE` if the file's contents are successfully loaded and `FALSE` if an error occurs.
If the function succeeds, it inserts the contents into GtkTextBuffer, frees the buffer memories pointed by `contents`, sets the title of the window, If this function succeeds, it inserts the contents into GtkTextBuffer,
frees the memories pointed by `filename` and shows the window. frees the buffer pointed by `contents`, sets the title of the window,
If it fails, it outputs an error message and destroys the window. frees the memories pointed by `filename` and then shows the window.
If it fails, it outputs an error message and destroys the window, causing the program to quit.
## GtkNotebook ## GtkNotebook
GtkNotebook is a container widget that contains multiple children with tabs in it. GtkNotebook is a container widget that uses tabs and contains multiple children.
The child that is displayed depends on which tab has been selected.
![GtkNotebook](../image/screenshot_gtk_notebook.png) ![GtkNotebook](../image/screenshot_gtk_notebook.png)
Look at the screenshots above. Looking at the screenshots above,
The left one is a window at the startup. the left one is the window at the startup.
It shows the file `pr1.c`. It shows the file `pr1.c` and the filename is in the left tab.
The filename is in the left tab. After clicking on the right tab, the contents of the file `tfv1.c` are shown instead.
After clicking on the right tab, the contents of `tfv1.c` appears. This is shown in the right screenshot.
It is shown in the right of the screenshot.
GtkNotebook widget is between GtkApplicationWindow and GtkScrolledWindow. The GtkNotebook widget is inserted as a child of GtkApplicationWindow and contains a GtkScrolledWindow
Now I want to show you the program `tfv4.c`. for each file that is being displayed.
The code to do this is given in `tfv4.c` and is:
~~~C ~~~C
1 #include <gtk/gtk.h> 1 #include <gtk/gtk.h>
@ -305,26 +305,24 @@ Now I want to show you the program `tfv4.c`.
70 g_object_unref (app); 70 g_object_unref (app);
71 return stat; 71 return stat;
72 } 72 }
73
~~~ ~~~
Most of the change is in the function `app_open`. Most of the changes are in the function `app_open`.
The numbers at the left of the following items are line numbers in the source code. The numbers at the left of the following items are line numbers in the source code.
- 11-13: Variables `nb`, `lab` and `nbp` are defined and points GtkNotebook, GtkLabel and GtkNotebookPage respectively. - 11-13: Variables `nb`, `lab` and `nbp` are defined and will point to a new GtkNotebook, GtkLabel and GtkNotebookPage widget respectively.
- 23: The window's title is set to "file viewer". - 23: The window's title is set to "file viewer".
- 25: The size of the window is set to maximum because a big window is appropriate for file viewers. - 25: The size of the window is set to maximum because a big window is appropriate for file viewers.
- 27-28 GtkNotebook is created and inserted to the GtkApplicationWindow as a child. - 27-28 GtkNotebook is created and inserted to the GtkApplicationWindow as a child.
- 30-59 For-loop. Each loop corresponds to an argument. And files[i] is GFile object with respect to the i-th argument. - 30-59 For-loop. Each loop corresponds to a filename argument, and `files[i]` is GFile object containing the i-th argument.
- 32-37 GtkScrollledWindow, GtkTextView are created and GtkTextBuffer is get from the GtkTextView. - 32-37 GtkScrollledWindow, GtkTextView are created and GtkTextBuffer found from the new GtkTextView.
GtkTextView is connected to GtkScrolledWindow as a child. GtkTextView is connected to GtkScrolledWindow as a child.
They corresponds to each file, so they are created inside the for-loop. Each file gets their own copy of these widgets, so they are created inside the for-loop.
- 39-40 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`. - 39-40 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`.
- 41-43: Gets the filename and creates GtkLabel with the filename. - 41-43: Gets the filename and creates GtkLabel with the filename and then frees `filename`.
Frees `filename`.
- 44-45: If `filename` is NULL, creates GtkLabel with the empty string. - 44-45: If `filename` is NULL, creates GtkLabel with the empty string.
- 46: Appends GtkScrolledWindow and GtkLabel to GtkNotebook. - 46: Appends GtkScrolledWindow as a page, with the tab GtkLabel, to GtkNotebook.
At the same time, a GtkNoteBookPage widget is created automatically. At this time a GtkNoteBookPage widget is created automatically.
The GtkScrolledWindow widget is connected to the GtkNotebookPage. The GtkScrolledWindow widget is connected to the GtkNotebookPage.
Therefore, the structure is like this: Therefore, the structure is like this:
@ -332,18 +330,16 @@ Therefore, the structure is like this:
GtkNotebook -- GtkNotebookPage -- GtkScrolledWindow GtkNotebook -- GtkNotebookPage -- GtkScrolledWindow
~~~ ~~~
- 47: Gets GtkNotebookPage widget and sets `nbp` to point the GtkNotebookPage. - 47: Gets GtkNotebookPage widget and sets `nbp` to point to this GtkNotebookPage.
- 48: GtkNotebookPage has a property "tab-expand". - 48: GtkNotebookPage has a property set called "tab-expand".
If it is set to TRUE then the tab expands horizontally as long as possible. If it is set to TRUE then the tab expands horizontally as long as possible.
If it is FALSE, then the width of the tab is determined by the size of the label. If it is FALSE, then the width of the tab is determined by the size of the label.
`g_object_set` is a general function to set properties in any objects. `g_object_set` is a general function to set properties in objects.
See [GObject API Reference, g\_object\_set](https://docs.gtk.org/gobject/method.Object.set.html). See [GObject API Reference, g\_object\_set](https://docs.gtk.org/gobject/method.Object.set.html).
- 49-51: If it fails to read the file, "No such file" message is outputted. - 49-51: If the file cannot be read, "No such file" message is displayed and the `filename` buffer is freed.
Frees `filename`. - 52-53: If `filename` is NULL, the "No valid file is given" message is outputted.
- 52-53: If `filename` is NULL, "No valid file is given" message is outputted.
- 55-58: If at least one file was read, then the number of GtkNotebookPage is greater than zero. - 55-58: If at least one file was read, then the number of GtkNotebookPage is greater than zero.
If it's true, it shows the window. If it's true, it shows the window.
If it's false, it destroys the window. If it's false, it destroys the window, which causes the program to quit.
Up: [Readme.md](../Readme.md), Prev: [Section 6](sec6.md), Next: [Section 8](sec8.md) Up: [Readme.md](../Readme.md), Prev: [Section 6](sec6.md), Next: [Section 8](sec8.md)

View file

@ -1,14 +1,17 @@
# String and memory management # String and memory management
GtkTextView and GtkTextBuffer have functions that have string parameters or return a string. GtkTextView and GtkTextBuffer have functions that use string parameters or return a string.
The knowledge of strings and memory management is very important for us to understand the functions above. The knowledge of strings and memory management is useful to understand how to use these functions.
## String and memory ## String and memory
String is an array of characters that is terminated with '\0'. A String is an array of characters that is terminated with '\0'.
String is not a C type such as char, int, float or double. Strings are not a C type such as char, int, float or double,
But the pointer to a character array behaves like a string type of other languages. but exist as a pointer to a character array. They behaves like a string type
So, the pointer is often called string. which you may be familiar from other languages.
So, this pointer is often called 'a string'.
In the following, `a` and `b` defined as character arrays, and are strings.
~~~C ~~~C
char a[10], *b; char a[10], *b;
@ -25,46 +28,50 @@ b = a;
/* *(++b) is 'e' */ /* *(++b) is 'e' */
~~~ ~~~
The array `a` has `char` elements and the size is ten. The array `a` has `char` elements and the size of ten.
The first six elements are 'H', 'e', 'l', 'l', 'o' and '\0'. The first six elements are 'H', 'e', 'l', 'l', 'o' and '\0'.
This array represents a string "Hello". This array represents the string "Hello".
The first five elements are character codes that correspond to the characters. The first five elements are character codes that correspond to the characters.
The sixth element is '\0' that is the same as zero. The sixth element is '\0', which is the same as zero,
And it indicates that the string ends there. and indicates that the string ends there.
The size of the array is 10, so 4 bytes aren't used, but it's OK. The size of the array is 10, so 4 bytes aren't used, but that's OK,
They are just ignored. they are just ignored.
The variable 'b' is a pointer to a character. The variable 'b' is a pointer to a character.
Because `a` is assigned to `b`, `a` and `b` point the same character ('H'). Because `b` is assigned to be `a`, `a` and `b` point the same character ('H').
The variable `a` is defined as an array and it can't be changed. The variable `a` is defined as an array and it can't be changed.
It always point the top address of the array. It always point the top address of the array.
On the other hand, pointers are mutable, so `b` can change itself. On the other hand, 'b' is a pointer, which is mutable, so `b` can be change.
It is possible to write `++b` (`b` is increased by one). It is then possible to write statements like `++b`, which means take the value in b (n address),
increase it by one, and store that back in `b`.
If a pointer is NULL, it points nothing. If a pointer is NULL, it points to nothing.
So, the pointer is not a string. So, the pointer is not a string.
Programs with string will include bugs if you aren't careful about NULL pointer. A NULL string on the other hand will be a pointer which points to a location
that contains `\0`, which is a string of length 0 (or "").
Programs that use strings will include bugs if you aren't careful when using NULL pointers.
Another annoying problem is memory that a string is allocated. Another annoying problem is the memory that a string is allocated.
There are four cases. There are four cases:
- The string is read only. - The string is read only;
- The string is in static memory area. - The string is in static memory area;
- The string is in stack. - The string is in stack; and
- The string is in memory allocated from the heap area. - The string is in memory allocated from the heap area.
## Read only string ## Read only string
A string literal in C program is surrounded by double quotes. A string literal in a C program is surrounded by double quotes and written as the following:
~~~C ~~~C
char *s; char *s;
s = "Hello" s = "Hello"
~~~ ~~~
"Hello" is a string literal. "Hello" is a string literal, and is stored in program memory.
A string literal is read only. A string literal is read only.
In the program above, `s` points the string literal. In the program above, `s` points the string literal.
So, the following program is illegal. So, the following program is illegal.
~~~C ~~~C
@ -74,18 +81,22 @@ So, the following program is illegal.
The result is undefined. The result is undefined.
Probably a bad thing will happen, for example, a segmentation fault. Probably a bad thing will happen, for example, a segmentation fault.
NOTE: The memory of the literal string is allocated when the program is
compiled. It is possible to view all the literal strings defined in your program
by using the `string` command.
## Strings defined as arrays ## Strings defined as arrays
If a string is defined as an array, it's in static memory area or stack. If a string is defined as an array, it's in either stored in the static memory area or stack.
It depends on the class of the array. This depends on the class of the array.
If the array's class is `static`, then it's placed in static memory area. If the array's class is `static`, then it's placed in static memory area.
It keeps its value and remains for the life of the program. This allocation and memory address is fixed for the life of the program.
This area is writable. This area can be changed and is writable.
If the array's class is `auto`, then it's placed in stack. If the array's class is `auto`, then it's placed in stack.
If the array is defined in a function, its default class is `auto`. If the array is defined inside a function, its default class is `auto`.
The stack area will disappear when the function returns to the caller. The stack area will disappear when the function exits and returns to the caller.
stack is writable. Arrays defined on the stack are writable.
~~~C ~~~C
@ -103,30 +114,30 @@ print_strings (void) {
} }
~~~ ~~~
The array `a` is defined externally to functions. The array `a` is defined externally to a function and is global in its scope.
Such variables are placed in static memory area even if the `static` class is left out. Such variables are placed in static memory area even if the `static` class is left out.
First, the compiler calculates the number of the elements in the right hand side. The compiler calculates the number of the elements in the right hand side (six),
It is six. and then creates code that allocates six bytes in the static memory area and copies the data to this memory.
The compiler allocates six bytes memory in the static memory area and copies the data to the memory.
The array `b` is defined inside the function. The array `b` is defined inside the function
So, its class is `auto`. so its class is `auto`.
The compiler calculates the number of the elements in the string literal. The compiler calculates the number of the elements in the string literal.
It is six because the string is zero terminated. It has six elements as the zero termination character is also included.
The compiler allocates six bytes memory in the stack and copies the data to the memory. The compiler creates code which allocates six bytes memory in the stack and copies the data to the memory.
Both `a` and `b` are writable. Both `a` and `b` are writable.
The memory is managed by the executable program. The memory is managed by the executable program.
You don't need to program to allocate or free the memory for `a` and `b`. You don't need your program to allocate or free the memory for `a` and `b`.
The array `a` remains for the life of the program. The array `a` is created then the program is first run and remains for the life of the program.
The array `b` disappears when the function returns to the caller. The array `b` is created on the stack then the function is called, disappears when the function returns.
## Strings in the heap area ## Strings in the heap area
You can get memory from the heap area and put back the memory to the heap area. You can also get, use and release memory from the heap area.
The standard C library provides `malloc` to get memory and `free` to put back memory. The standard C library provides `malloc` to get memory and `free` to put back memory.
Similarly, GLib provides `g_new` and `g_free`. GLib provides the functions `g_new` and `g_free` to do the same thing, with support for
some additional Glib functionality.
~~~C ~~~C
g_new (struct_type, n_struct) g_new (struct_type, n_struct)
@ -163,7 +174,7 @@ If `mem` is NULL, `g_free` does nothing.
`gpointer` is a type of general pointer. `gpointer` is a type of general pointer.
It is the same as `void *`. It is the same as `void *`.
This pointer can be casted to any pointer type. This pointer can be casted to any pointer type.
Conversely, any pointer type can be casted to gpointer. Conversely, any pointer type can be casted to `gpointer`.
~~~C ~~~C
g_free (s); g_free (s);
@ -173,10 +184,10 @@ g_free (t);
/* Frees the memory allocated to t. */ /* Frees the memory allocated to t. */
~~~ ~~~
If the argument doesn't point allocated memory, it will cause an error, for example, segmentation fault. If the argument doesn't point allocated memory it will cause an error, specifically, a segmentation fault.
Some Glib functions allocate memory. Some Glib functions allocate memory.
For example, `g_strdup` allocates memory and copy a string given as an argument. For example, `g_strdup` allocates memory and copies a string given as an argument.
~~~C ~~~C
char *s; char *s;
@ -194,8 +205,11 @@ The following is extracted from the reference.
> The returned string should be freed with `g_free()` when no longer needed. > The returned string should be freed with `g_free()` when no longer needed.
The reference usually describes if the returned value needs to be freed. The function reference will describe if the returned value needs to be freed.
If you forget to free the allocated memory, it causes memory leak. If you forget to free the allocated memory it will remain allocated. Repeated use will cause
more memory to be allocated to the program, which will grow over time. This is called a memory leak,
and the only way to address this bug is to close the program (and restart it),
which will automatically release all of the programs memory back to the system.
Some GLib functions return a string which mustn't be freed by the caller. Some GLib functions return a string which mustn't be freed by the caller.
@ -205,14 +219,13 @@ g_quark_to_string (GQuark quark);
~~~ ~~~
This function returns `const char*` type. This function returns `const char*` type.
The qualifier `const` means immutable. The qualifier `const` means that the returned value is immutable.
Therefore, the characters pointed by the returned value aren't be allowed to change or free. The characters pointed by the returned value aren't be allowed to be changed or freed.
If a variable is qualified with `const`, the variable can't be assigned except initialization. If a variable is qualified with `const`, the variable can't be assigned except during initialization.
~~~C ~~~C
const int x = 10; /* initialization is OK. */ const int x = 10; /* initialization is OK. */
x = 20; /* This is illegal because x is qualified with const */ x = 20; /* This is illegal because x is qualified with const */
~~~ ~~~

View file

@ -4,21 +4,22 @@
### G\_APPLICATION\_HANDLES\_OPEN flag ### G\_APPLICATION\_HANDLES\_OPEN flag
GtkTextView, GtkTextBuffer and GtkScrolledWindow have given us a minimum editor in the previous section. The GtkTextView, GtkTextBuffer and GtkScrolledWindow widgets have given us a minimum editor
Next, we will add a function to read a file and remake the program into a file viewer. in the previous section.
There are many ways to implement the function. We will now add a function to read a file and rework the program into a file viewer.
Because this is a tutorial for beginners, we'll take the easiest one. There are many ways to implement the function and
because this is a tutorial for beginners, we'll take the easiest one.
When the program starts, we give a filename as an argument. When the program starts, we will give the filename to open as an argument.
$ ./a.out filename $ ./a.out filename
Then it opens the file and inserts its contents into the GtkTextBuffer. It will open the file and insert its contents into the GtkTextBuffer.
At the beginning of the implementation, we need to know how GtkApplication (or GApplication) recognizes arguments. To do this, we need to know how GtkApplication (or GApplication) recognizes arguments.
It is described in the [GIO API Reference, Application](https://docs.gtk.org/gio/class.Application.html). This is described in the [GIO API Reference, Application](https://docs.gtk.org/gio/class.Application.html).
When GtkApplication is created, a flag (its type is GApplicationFlags) is given as an argument. When GtkApplication is created, a flag (with the type GApplicationFlags) is provided as an argument.
~~~C ~~~C
GtkApplication * GtkApplication *
@ -26,9 +27,9 @@ gtk_application_new (const gchar *application_id, GApplicationFlags flags);
~~~ ~~~
This tutorial explains only two flags, `G_APPLICATION_FLAGS_NONE` and `G_APPLICATION_HANDLES_OPEN`. This tutorial explains only two flags, `G_APPLICATION_FLAGS_NONE` and `G_APPLICATION_HANDLES_OPEN`.
If you want to handle command line arguments, `G_APPLICATION_HANDLES_COMMAND_LINE` flag is what you need. If you want to handle command line arguments, the `G_APPLICATION_HANDLES_COMMAND_LINE` flag is what you need.
How to program it is written in [GIO API Reference, g\_application\_run](https://docs.gtk.org/gio/method.Application.run.html). How to use the new application method is described in [GIO API Reference, g\_application\_run](https://docs.gtk.org/gio/method.Application.run.html),
And the flag is described in the [GIO API Reference, ApplicationFlags](https://docs.gtk.org/gio/flags.ApplicationFlags.html). and the flag is described in the [GIO API Reference, ApplicationFlags](https://docs.gtk.org/gio/flags.ApplicationFlags.html).
~~~ ~~~
GApplicationFlags' Members GApplicationFlags' Members
@ -39,18 +40,15 @@ G_APPLICATION_HANDLES_OPEN This application handles opening files (in the prima
... ... ... ... ... ...
~~~ ~~~
There are ten flags. There are ten flags in total, but we only need two of them so far.
But we only need two of them so far. We've already used `G_APPLICATION_FLAGS_NONE`, as
We've already used `G_APPLICATION_FLAGS_NONE`. it is the simplest option, and no arguments are allowed.
It is the simplest option. If you provide arguments when running the application, an error will occur.
No argument is allowed.
If you give arguments and run the application, then error occurs.
`G_APPLICATION_HANDLES_OPEN` is the second simplest option. The flag `G_APPLICATION_HANDLES_OPEN` is the second simplest option.
It allows arguments but only files. It allows arguments but only files.
The application assumes all the arguments are filenames. The application assumes all the arguments are filenames and we will use this flag when creating
our GtkApplication.
Now we use this flag when creating GtkApplication.
~~~C ~~~C
app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN); app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
@ -58,12 +56,12 @@ app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPE
### open signal ### open signal
When the application starts, two signals are possible. Now, when the application starts, two signals can be emitted.
- activate signal --- This signal is emitted when there's no argument. - activate signal --- This signal is emitted when there's no argument.
- open signal --- This signal is emitted when there is at least one argument. - open signal --- This signal is emitted when there is at least one argument.
The handler of "open" signal is defined as follows. The handler of the "open" signal is defined as follows.
~~~C ~~~C
void user_function (GApplication *application, void user_function (GApplication *application,
@ -81,22 +79,22 @@ The parameters are:
- `hint` --- a hint provided by the calling instance (usually it can be ignored) - `hint` --- a hint provided by the calling instance (usually it can be ignored)
- `user_data` --- user data set when the signal handler was connected. - `user_data` --- user data set when the signal handler was connected.
The way how to read a file (GFile) will be described in the next subsection. How to read a specified file (GFile) will be described next.
## Making a file viewer ## Making a file viewer
### What is a file viewer? ### What is a file viewer?
A file viewer is a program that shows a text file given as an argument. A file viewer is a program that displays the text file that is given as an argument.
It works as follows. Our file viewer will work as follows.
- If it is given arguments, it recognizes the first argument as a filename and opens it. - When arguments are given, it treats the first argument as a filename and opens it.
- If opening the file succeeds, it reads the contents of the file and inserts it to GtkTextBuffer and shows the window. - If opening the file succeeds, it reads the contents of the file and inserts it to GtkTextBuffer and then shows the window.
- If it fails to open the file, shows an error message and quits. - If it fails to open the file, it will show an error message and quit.
- If there's no argument, it shows an error message and quits. - If there's no argument, it will shows an error message and quit.
- If there are two or more arguments, the second one and after are ignored. - If there are two or more arguments, the second one and any others are ignored.
The program is as follows. The program which does this is shown below.
@@@include @@@include
tfv/tfv3.c tfv/tfv3.c
@ -110,25 +108,25 @@ Then compile and run it.
![File viewer](../image/screenshot_tfv3.png){width=6.3cm height=5.325cm} ![File viewer](../image/screenshot_tfv3.png){width=6.3cm height=5.325cm}
Now I want to explain the program `tfv3.c`. Let's explain how the program `tfv3.c` works.
First, the function `main` changes in only two lines from the previous version. First, the function `main` has only two changes from the previous version.
- `G_APPLICATION_FLAGS_NONE` is replaced by `G_APPLICATION_HANDLES_OPEN`. - `G_APPLICATION_FLAGS_NONE` is replaced by `G_APPLICATION_HANDLES_OPEN`; and
- `g_signal_connect (app, "open", G_CALLBACK (on_open), NULL)` is added. - `g_signal_connect (app, "open", G_CALLBACK (on_open), NULL)` is added.
Next, the handler `app_activate` is now very simple. Next, the handler `app_activate` is added and is very simple.
It just outputs the error message. It just outputs the error message and
The application quits immediately because no window is created. the application quits immediately because no window is created.
The point is the handler `app_open`. The main functionality is the in the handler `app_open`. It
- It creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them. - Creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them together;
- Sets wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView. - Sets wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView;
- Sets GtkTextView to non-editable because the program isn't an editor but only a viewer. - Sets GtkTextView to non-editable because the program isn't an editor but only a viewer;
- Reads the file and inserts the text into GtkTextBuffer (this will be explained in detail later). - Reads the file and inserts the text into GtkTextBuffer (this will be explained in detail later); and
- If the file is not opened then outputs an error message and destroys the window. It makes the application quit. - If the file is not opened then outputs an error message and destroys the window. This makes the application quit.
The file reading part of the program is shown again below. The following is the important file reading part of the program and is shown again below.
~~~C ~~~C
if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) { if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) {
@ -148,51 +146,53 @@ if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) {
} }
~~~ ~~~
The function `g_file_load_contents` loads the file contents into a buffer, which is automatically allocated, and sets `contents` to point the buffer. The function `g_file_load_contents` loads the file contents into a buffer,
And the length of the buffer is set to `length`. which is automatically allocated and sets `contents` to point that buffer.
It returns `TRUE` if the file's contents are successfully loaded. `FALSE` if an error happens. The length of the buffer is set to `length`.
It returns `TRUE` if the file's contents are successfully loaded and `FALSE` if an error occurs.
If the function succeeds, it inserts the contents into GtkTextBuffer, frees the buffer memories pointed by `contents`, sets the title of the window, If this function succeeds, it inserts the contents into GtkTextBuffer,
frees the memories pointed by `filename` and shows the window. frees the buffer pointed by `contents`, sets the title of the window,
If it fails, it outputs an error message and destroys the window. frees the memories pointed by `filename` and then shows the window.
If it fails, it outputs an error message and destroys the window, causing the program to quit.
## GtkNotebook ## GtkNotebook
GtkNotebook is a container widget that contains multiple children with tabs in it. GtkNotebook is a container widget that uses tabs and contains multiple children.
The child that is displayed depends on which tab has been selected.
![GtkNotebook](../image/screenshot_gtk_notebook.png){width=13.2cm height=5.325cm} ![GtkNotebook](../image/screenshot_gtk_notebook.png){width=13.2cm height=5.325cm}
Look at the screenshots above. Looking at the screenshots above,
The left one is a window at the startup. the left one is the window at the startup.
It shows the file `pr1.c`. It shows the file `pr1.c` and the filename is in the left tab.
The filename is in the left tab. After clicking on the right tab, the contents of the file `tfv1.c` are shown instead.
After clicking on the right tab, the contents of `tfv1.c` appears. This is shown in the right screenshot.
It is shown in the right of the screenshot.
GtkNotebook widget is between GtkApplicationWindow and GtkScrolledWindow. The GtkNotebook widget is inserted as a child of GtkApplicationWindow and contains a GtkScrolledWindow
Now I want to show you the program `tfv4.c`. for each file that is being displayed.
The code to do this is given in `tfv4.c` and is:
@@@include @@@include
tfv/tfv4.c tfv/tfv4.c
@@@ @@@
Most of the change is in the function `app_open`. Most of the changes are in the function `app_open`.
The numbers at the left of the following items are line numbers in the source code. The numbers at the left of the following items are line numbers in the source code.
- 11-13: Variables `nb`, `lab` and `nbp` are defined and points GtkNotebook, GtkLabel and GtkNotebookPage respectively. - 11-13: Variables `nb`, `lab` and `nbp` are defined and will point to a new GtkNotebook, GtkLabel and GtkNotebookPage widget respectively.
- 23: The window's title is set to "file viewer". - 23: The window's title is set to "file viewer".
- 25: The size of the window is set to maximum because a big window is appropriate for file viewers. - 25: The size of the window is set to maximum because a big window is appropriate for file viewers.
- 27-28 GtkNotebook is created and inserted to the GtkApplicationWindow as a child. - 27-28 GtkNotebook is created and inserted to the GtkApplicationWindow as a child.
- 30-59 For-loop. Each loop corresponds to an argument. And files[i] is GFile object with respect to the i-th argument. - 30-59 For-loop. Each loop corresponds to a filename argument, and `files[i]` is GFile object containing the i-th argument.
- 32-37 GtkScrollledWindow, GtkTextView are created and GtkTextBuffer is get from the GtkTextView. - 32-37 GtkScrollledWindow, GtkTextView are created and GtkTextBuffer found from the new GtkTextView.
GtkTextView is connected to GtkScrolledWindow as a child. GtkTextView is connected to GtkScrolledWindow as a child.
They corresponds to each file, so they are created inside the for-loop. Each file gets their own copy of these widgets, so they are created inside the for-loop.
- 39-40 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`. - 39-40 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`.
- 41-43: Gets the filename and creates GtkLabel with the filename. - 41-43: Gets the filename and creates GtkLabel with the filename and then frees `filename`.
Frees `filename`.
- 44-45: If `filename` is NULL, creates GtkLabel with the empty string. - 44-45: If `filename` is NULL, creates GtkLabel with the empty string.
- 46: Appends GtkScrolledWindow and GtkLabel to GtkNotebook. - 46: Appends GtkScrolledWindow as a page, with the tab GtkLabel, to GtkNotebook.
At the same time, a GtkNoteBookPage widget is created automatically. At this time a GtkNoteBookPage widget is created automatically.
The GtkScrolledWindow widget is connected to the GtkNotebookPage. The GtkScrolledWindow widget is connected to the GtkNotebookPage.
Therefore, the structure is like this: Therefore, the structure is like this:
@ -200,16 +200,14 @@ Therefore, the structure is like this:
GtkNotebook -- GtkNotebookPage -- GtkScrolledWindow GtkNotebook -- GtkNotebookPage -- GtkScrolledWindow
~~~ ~~~
- 47: Gets GtkNotebookPage widget and sets `nbp` to point the GtkNotebookPage. - 47: Gets GtkNotebookPage widget and sets `nbp` to point to this GtkNotebookPage.
- 48: GtkNotebookPage has a property "tab-expand". - 48: GtkNotebookPage has a property set called "tab-expand".
If it is set to TRUE then the tab expands horizontally as long as possible. If it is set to TRUE then the tab expands horizontally as long as possible.
If it is FALSE, then the width of the tab is determined by the size of the label. If it is FALSE, then the width of the tab is determined by the size of the label.
`g_object_set` is a general function to set properties in any objects. `g_object_set` is a general function to set properties in objects.
See [GObject API Reference, g\_object\_set](https://docs.gtk.org/gobject/method.Object.set.html). See [GObject API Reference, g\_object\_set](https://docs.gtk.org/gobject/method.Object.set.html).
- 49-51: If it fails to read the file, "No such file" message is outputted. - 49-51: If the file cannot be read, "No such file" message is displayed and the `filename` buffer is freed.
Frees `filename`. - 52-53: If `filename` is NULL, the "No valid file is given" message is outputted.
- 52-53: If `filename` is NULL, "No valid file is given" message is outputted.
- 55-58: If at least one file was read, then the number of GtkNotebookPage is greater than zero. - 55-58: If at least one file was read, then the number of GtkNotebookPage is greater than zero.
If it's true, it shows the window. If it's true, it shows the window.
If it's false, it destroys the window. If it's false, it destroys the window, which causes the program to quit.

View file

@ -56,4 +56,3 @@ main (int argc, char **argv) {
g_object_unref (app); g_object_unref (app);
return stat; return stat;
} }

View file

@ -70,4 +70,3 @@ main (int argc, char **argv) {
g_object_unref (app); g_object_unref (app);
return stat; return stat;
} }