C programming

C is the programming language used for the kernel and C++ the object oriented alternative to C. Both are supported by gcc.

The hello wold program

Example 16.1. hello.c

#include <stdio.h>
main()
{
  printf("Hello World!\n");
}


To compile it (-o hello produces hello as output file instead of the default a.out)

gcc -o hello hello.c

Check that the file hello as the executable permission and run it

./hello

Hello world split in two files

If programs are getting bigger, they get split in multiple file. This has different advantages. A file can be reused somewhere else. Not every thing has to be recompiled just the file edited. Here the hello world program split in two files:

Example 16.2. hello.c splitted

main()
{
  printhello();
}


Example 16.3. hello c function

#include <stdio.h>
void printhello(void)
{
  printf("Hello World!\n");
}

Now gcc has to be called differently:

gcc -c hello.c

gcc -c printhello.c

The result is not a running program. The two object files hello.o and printhello.o are produced. The-c option tells gcc to do so and not producing absolute addresses. To get a running program the object files have to be put together using a linker. Instead of calling the linker ld directlygcc can be called.

Note

Gccstands for Gnu Compiler Collection and not Gnu C Compiler. Gcc can be used for different things: C compiler, assembler, linker, ...

The following command links the two object files into the program hello:

gcc hello.o printhello.o -o hello

Now it can be run with

./hello

Interacting with the parent process

To access the command line parameters and the environmental variables of the parent process declare main as follows:

int main(int argc, char *argv[], char *envp[]) 
{
 return(0);
}
  1. argc contains how many parameters are passed. There is always one, the command line that has called the C program.

  2. argv[] is an pointer to an array containing all command line parameters available

  3. envp[] is a pointer to an array containing all environmental parameters as strings formatted NAME=value, luckily you do not have to search trough them, there is a function that gets the values assigned to the names. Example how to get the hostname:

#include <stdlib.h> 
char* host;
host = getenv("HOSTNAME");
  1. The return function exits and gives back a number to the parent process.

Integrated development Environments (IDE)

IDE's seem the way to go and the first results might be quickly be achieved, however when in trouble, the command line options and concepts have to be understood even when working with a IDE. Additionally IDE's offer so much functionality, as automatic documentation, publishing and archiving code, version control, gui support for various libraries, debugging, code analyze, multiple language support, and many or too many more. So often if you open an IDE you do not get the overview and struggle with the project files.

  1. emerge codeblocks for C++ using it works without autotools and make file. It is a ready to go environment that works as desired. Libraries have to be added under Project > Build Options > Linker Settings where the library name without lib or l has to be added. Command line arguments can be set under Project > Set programs arguments. Codeblocks is also available for Windows.

  2. anjuta is and IDE for gnome

  3. Kdevelop is a very big Integrated Development Environment (IDE) to create your own programs in various programming languages and endless options and features. For small programs the size of the program is smaller than the size of the makefile that Kdevelop has created for you and you spend more time troubleshooting Kdevelop than writing your own program. Maybe the situation looks different when you are a professional programmer or you work in a team. For a hobby programmer, Kdevelop is probably an overshoot.

  4. eclipse has is a huge IDE originally developed for java, but has all kind of plugins available for any kind of programming languages.

Or why not keep it simple and use just an editor with syntax highlighting, gcc and make your makefile (or Makefile) using an editor. Do not forget to add -g to your makefile to have the info required to debug in your program. Then compile it in a console by calling make. This way has the obvious advantage that you know what is going on and you can concentrate yourself to the creation of your own program.

working with eclipse the CDT has to be installed, this is best done via marketplace. Marketplace should be available under help (if not install marketplace). Then the CDT has to be found, the Marketplace Yoxos has it. There appear many packages that are related to CDT with big icons, so it takes a while to find the small icon of the CDT.

Everything to be seen in eclipse is a Perspective, so open the C/C++ perspective. When done with it open the Debug Perspective to test your code.

Makefiles and make

If there is just one c file as the hello.c then make and makefile are not useful, since a single gcc call will do everything. However when there are many different files it is obviously desired to not compile every file when just one got changed. However when one got changed, then the onces depending on the changed one need to compiled as well. The main goal of the makefile is to write down the dependency and have the program make to do all necessary but not more. By the way, make is not restricted to C programming, it can call other command lines not containing gcc commands.

Note

To know what has changed make looks at the modification dates of the files, therefore make sure your clock is running well!

Syntax of the makefiles

Example makefile created by a text editor and put in the directory where c and h files reside.

Note

Very important is that the indents are done with the tab character. Make also sure that you editor really inserts tab and not spaces.

<target filename>: <source filename>.c <source filename>.h
    gcc -g <filename>.c  -o <filename>
clean:
    rm -f *.o
# is the character to define the line as comment
  1. The first line starts with a file name, that is used as target. It defines a dependency rule, when one of its source file is newer then the target file, then

  2. the second line is executed (and more when present). Lines containing commands must begin with the tab character.

  3. The third line containing the word clean is a phony target, since no file named clean exists. Calling make clean executes all commands put below the third line.

The makefile for the split hello world file would look like:

hello: hello.o printhello.o
    gcc hello.o printhello.o -o hello
hello.o: hello.c
    gcc -c hello.c
printhello.o:printhello.c
     gcc -c printhello.c
clean:
     rm -f *.o 

To not edit everywhere when the make file is getting used for a new project or if the files get a new name or if you want to set standard compiler options, you can work with variables:

name=hello
include=printhello
$(name): $(name).o $(include).o
    gcc $(name).o $(include).o -o $(name)
$(name).o: $(name).c
    gcc -c $(name).c
$(include).o:$(include).c
     gcc -c $(include).c
clean:
     rm -f *.o 

If some files are in other directories then the -I option can be used to pass the path:

-I/<path to file>

Compilation

To compile, open a shell and go to directory, then type make.

Per default the destination files of gcc are called a.out. The option -o selects an other name.

make clean

Having added the clean section in the makefile as shown above, the command make clean cleans the directory from object files no longer used.

Code optimization

Finally be aware that gcc is normally used to emerge <Gentoo ebuilds> and is therefore set to produce code optimized for speed. The variables defined in /etc/make.conf are passed to gcc, so check those options

CHOST="i686-pc-linux-gnu"
CFLAGS="-O2 -march=athlon-xp -pipe -fomit-frame-pointer"
CXXFLAGS="${CFLAGS}"
MAKEOPTS="-j2"

Those options are OK, but the -fomit-frame-pointer produces long reaction time when you debug your c program. Therefore you might consider to change /etc/make.conf while you are in a longer debug session.

Built runnable binaries can be copied to /usr/local/bin to be used outside of the development environment. Better would be putting just links there to your executable and add a version number to your executable file name. So you are prepared for new versions. However you should not copy executables manually, you should install them following the rules of your Linux distribution to not make a mess. For Gentoo this means you should write an ebuild.

Debugging

I don't think you want to debug in a console, so use a front end for dgb the console debugger. By the way, also the big development environments as Kdevelop are no more than a front end for gdb when it comes to debug.

Make sure you complied it with the option -g that adds information for the debugger into the executable. Executables under Linux are not just raw program code, as used in embedded systems and other operating systems, they are formatted elf files. Elf files have different section and using the -g option the debugging section is added. To observe the hello executable:

readelf --all hello

objdump -h hello

Maybe make refuses to recompile after just editing the makefile, so edit the source file or delete the executable.

gdb

to debug in the console start gdb by gdb<my program>

Then type some commands as:

Table 16.1. gdb

run run the program until it exits, finds a break point or error
print <variable name> show contents of a variable
quit quit gdb
break <function name> set a breakpoint to a function
next single step
nexti single step
help show help


Nemiver

Is what it should to be a modern easy to go and intuitive gui debugger

Insight

Insight is an other front-end for gdb. It uses tk.tcl and wants it in a certain location, otherwise it fails.

ddd

An other front-end for gdb is ddd. It is rather ugly and not intuitive. To debug an executable it has to be called in command line ddd<name of executable>. Even it is ugly it works well.

Execution from the text console

To execute a program type ./<filename> ./ is your current directory so bash finds the executable.

Libraries

Using Libraries

To use Libraries include

#include <math.h> 

to the source code, but this might not be enough.

/usr/lib contains the mathematic library libm. To use it with gcc you must tell gcc by adding -lm that you want it. -l stands for including libraries and m is the short name of libm, where lib gets wiped off. So the gcc command line in the makefile becomes:

gcc -g <source>.c -lm -o <source>

This has to be done for all libraries. The only exception is glibc the global C library.

For the libncurses add: -lcurses

For libraries not in /usr/lib the option -L can be used to tell gcc where they are.

-L /usr/X11R6/lib

Instead of finding out where the libraries are and use this hard coded pkg-config can be used. It will be available after emerge pkgconfig. To see what it does call it as:

pkg-config --libs --cflags modbus and when you have libmodbus installed you get: -lmodbus

After this test you will understand what the following does:

gcc random-test-slave.c -o random-test-slave `pkg-config --libs --cflags modbus`

Creating Libraries

If code is used again and again, then it is time to put it into a library and make it easily available. The code is compiled as usual with the -c option so the linker will not be involved. The following shows how the split hello world program makes use of a library:

gcc -c printhello.c

The output is a object file printhello.o. A number of such files can be added into a library archive <library>.a

ar r<library>.a <code1>.o <code2>.o

For the printhello.o the command looks as:

ar r libhello.a printhello.o

After running ar, ranlib can be used to add an index of the contents to the file to speed up its use.

ranlib libhello.a

Using the following commands the content of a library can be observed

ar t libhello.a

nm -s libhello.a

After having the library it can be used as:

gcc -o hello hello.c libhello.a

or

gcc -o<program> <program>.c /<path to>/<library>. a

or as standard libraries in /usr/lib but telling with -L where the library is

gcc -o<program> <program>.c -L/<path to library> -l<library-lib>

or for the hello world

gcc -o hello hello.c -L/<path> -lhello

Note, that using this commands the library code is inserted to the program, therefore it is called statically linked libraries. With each program using the library code, a copy of the library code is loaded into the memory.

Dynamic linked libraries

Statically linked libraries can reside multiple times in the memory. More ideal would be loading the library into memory with the first program demanding it and then share it with all demanding programs. This is the concept of the dynamically linked libraries. Those files end with .so (shared object) suffixes in their names.

Since the library and program are separate files there is a potential for a version conflict. Additionally dynamically linked libraries must be known by the operating system, this is an additional potential to run in troubles.

Version numbers are added to the files. To have a certain flexibility when the version numbers do not match exactly, there is a major and a minor version number. The shared object files appear therefore in multiple forms:

realname

/usr/lib/libhello.so.1.0

Holds the code and is therefore the shared object file

 

soname

/usr/lib/libhello.so.1

Is a link to realname and allows an evolution

 

linkername

/usr/lib/libhello.so

Name that the linker uses

 

When the program gets loaded by the run-time linker, /lib/ld.so or /lib/ld-linux.so, Linux needs to know where the library is, this information comes out of the linker cache /etc/ld.so.conf that is a binary file and ldconfig updates it. The directories where shared objects are to be expected are listed in: /etc/ld.so.conf. If it will not be found in the linker cache (where just the most recent are) then it will scan certain directories.

The addresses where the code in dynamically linked libraries are placed can not be fixed. Therefore Position Independent Code (PIC) is required that is created with the gcc option -fPIC. To build a shared object compile the code as:

gcc -fPIC -c printhello.c

Then link it to a shared object

ld -shared -soname libhello.so.1 -o libhello.so.1.0 -lc printhello.o

then as root, copy libhello.so.1.0 to /usr/lib, /lib or /usr/local/lib>

create necessary links and cache

ldconfig -v -n

Now an application to test the shared object is necessary. Assuming the shared object has been put in /usr/lib the following command applies:

gcc -o hello hello.c -L/usr/lib -lhello

And now it can be tested:

./hello

To observe what the system knows about it and to get information when trouble shooting, the following commands can be used:

To see dependency of the test program hello:

ldd hello

linux-gate.so.1 => (0xffffe000)

libhello.so.1 => /usr/lib/libhello.so.1 (0xb7f73000)

libc.so.6 => /lib/libc.so.6 (0xb7e4b000)

/lib/ld-linux.so.2 (0xb7f99000)

To see other stuff about the program (as 32 bit or 64 bit):

file hello

hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), not stripped

To see whats inside the shared object:

nm libhello.so.1.0

00001f4c a _DYNAMIC

00001ff4 a _GLOBAL_OFFSET_TABLE_

00002004 A __bss_start

00000276 t __i686.get_pc_thunk.bx

00002004 A _edata

00002004 A _end

00000250 T printhello

U puts@@GLIBC_2.0

Or even more

objdump -x libhello.so.1.0

Inside the shared object there two functions can be added that will be called each time the library is accessed. The following is a sample code that prints those events to the screen:

Example 16.4. Dynamic library

void _init()
{
  printf("Inside _init()\n"); 
}

void _fini()
{
  printf("Inside _fini()\n"); 
}

Clang

There is not just gcc. there is clang http://clang.llvm.org/ a C language front-end for the llvm compiler infrastructure https://llvm.org/


Linurs startpage