Tutorials/C Programming Tutorial

From ThorstensHome
(Redirected from C Programming Tutorial)
Jump to: navigation, search

C and C++ are cool programming languages, but some things are hard to learn or remember. This page is to make learning this language easier to you. The compiling examples are based on Linux.

Contents

Introduction

First program

Here is a "hello world" C/C++ program:

main.cpp

#include <iostream>

int main()
{
  std::cout << "hello world" << std::endl;
}

Build and run it:

g++ -o hello main.cpp && ./hello

Explanation

std::cout is the command to write something "on your screen" (to be more exact, to stdout). std:: is put in front because it is the namespace standard. If you use the command using namespace std; you can omit this. g++ is the gnu c++ compiler. -o hello means it outputs the file "hello".

Hello, Thorsten

We are now going to write a program that asks for your name and says "hello, Thorsten" (in case your name is Thorsten). For that, we need a variable to hold the name that the user inputs:

#include <iostream>
using namespace std;
int main()
{
  string name;
  cout << "What is your name? ";
  cin >> name;
  cout << "Hello, " << name << endl;
}

Explanation

string name tells the program that name is a variable to hold a string (not e.g. a number). cin >> name tells the program you want character input (the opposite direction of charater output, we know the command cout. You input your name and it gets stored in the variable name. Then, "Hello" and the variable's content is output.

C, a functional language

C is a functional language, every command delivers a return value:

std::cout << "the return value of outputting hello world is " << std::cout<< "hello world";

writes something like

the return value of outputting hello world is 0x601068hello world

The compiler does its job till every command is resolved, that means, every command has delivered a return code. That is why you can write return codes into your program without getting error messages:

#include <iostream>
using namespace std;
int main()
{
  cout << "the return value of outputting hello world is " << cout << "hello world" << endl;
  1807;
  4711;
}

Explanation

cout << "hello world" << endl; delivers a return value although this is void (in the sense of useless). Typically, a return value could be used to indicate whether the command was successful. This return value can be output using another cout command. Every command (so, all what is written before the ;) is evaluated till it finally is a number. This is why a number does not do anything - it is already evaluated till the end. 1807; does nothing.

Building

Building your c program is more complicated than writing it. Here is an example:

main.cpp

#include <stdio.h> 

int main(int argc, char* argv[])
{
  printf("hello world");
}

To compile it, you may use g++ or gcc with the respective library:

tweedleburg:~/test # g++ main.cpp
tweedleburg:~/test # gcc main.cpp
/tmp/ccGoITbg.o:(.eh_frame+0x12): undefined reference to `__gxx_personality_v0'
collect2: ld returned 1 exit status
tweedleburg:~/test # gcc -lstdc++ main.cpp
tweedleburg:~/test #

gcc needs this library because it determines from the suffix it is a C++ program. Tell gcc that it is a C program and it will not need this library:

tweedleburg:~/test # gcc -xc main.cpp
tweedleburg:~/test #

or copy main.cpp to main.c and use gcc:

tweedleburg:~/test # gcc main.c
tweedleburg:~/test #

library order

The compiler searches for includes first in /usr/local/include then in /usr/include. includes are expected to be named .h or .hpp.

dynamic linking vs static linking

See http://www.linuxintro.org/wiki/Linking.

printf parameters

An integer number divided using the "/" operator by an integer number delivers an integer number, example:

main.cpp

#include <stdio.h>

int main()
{
  printf( "%i", 31 / 60);
}

Compile, link and run it

g++ main.cpp && ./a.out
0

float

Stupid C only reserves 4 bytes for the result of 31/60, because it thinks 31/60 is an int, 0. So the following yields a random number:

main.cpp

#include <stdio.h>

int main()
{
  printf( "%f", 31 / 60);
}

To correct this, you have two possibilities:

  • write one or two numbers as a floats themselves, e.g.: 31.0/60.0
  • cast 31/60 to a float. We will learn casts next.

file operations

main.c

#include <stdio.h>

int main()
{
  FILE *handle;
  handle=fopen("testfile", "wb");
  char x[1];
  fwrite(x,1,1,handle);
}

To build this, do

g++ main.c -o runthis

casts

You can cast the result of 31/60 to be a float like this:

#include <stdio.h>

int main()
{
  printf( "%f", (float)31 / 60);
}

What does it mean

Imagine you have to analyze C or C++ source code and you come across this sign, say "*" or "&" or "++". There are several meanings of each of it. Fear not! Come here and find them all.

++

There is a ++ operator I always have to look up:

#include <iostream>
int main()
{
    int i=5,n=5;
    std::cout << "i=5;i++:" << i++ << std::endl;
    std::cout << "n=5;++n:" << ++n << std::endl;
}

delivers

i=5;i++:5
n=5;++n:6
Remember
i++ delivers i and afterwards increases it. ++i first increases i, then delivers i.

*

A * can mean

  • a declaration of a pointer:
int* i;
  • a dereferenciation
cout << "i has the value " << *i << endl;

void

  • a void pointer is a pointer of an unknown type
  • a void function is a function that does not return anything

&

A & can mean

  • a bit-wise AND
  • a dereferenciation:
int i=5;
cout << "memory byte number 5 is " << &i << endl;
  • assigning a reference:
#include <iostream> 

int main()
{
  int donald=1;
  int &phantomias=donald;
  // now donald and phantomias mean the same memory address
  std::cout << "Donald is at address " << &donald << " Phantomias at " << &phantomias << std::endl;
  // they have the same value
  std::cout << "Donald has the value " << donald << " Phantomias has " << phantomias << std::endl;
  phantomias=5;
  // now donald == 5
  std::cout << "Donald has the value " << donald << " Phantomias has " << phantomias << std::endl;
}
  • declaring call-by-reference
// Copyright (c) by Scott Wheeler, see http://developer.kde.org/~wheeler/cpp-pitfalls.html#references
void foo( int &i )
{
    i++;
}

int main()
{
    int bar = 5;   // bar == 5
    foo( bar );    // bar == 6
    foo( bar );    // bar == 7

    return 0;
}

Pointers

A pointer is if you do not store a value in a variable, but the address in memory where the value is stored. In other words, we point to the memory address where the value is stored. The C compiler has the asterix (*) as signal that a pointer starts:

delete 0;

gives you the error message

error: type ‘int’ argument given to ‘delete’, expected pointer

While

delete (int*) 0;

and

delete (void*) 0;

are legal.

Let's write a short program to demonstrate a pointer is different from its value:

main.cpp:

#include <iostream>

using namespace std;

int foo(int* i)
{
  cout << "You handed over " << *i << endl;
  cout << "We are talking about the memory address " << i << endl;
}

int main()
{
  int n=5;
  foo(&n);
  int *m=new int(23);
  foo(m);
}

Compile and run it like this:

tweedleburg:~ # g++ main.cpp -o cprogram && ./cprogram
You handed over 5
We are talking about the memory address 0x7fffcab8a914
You handed over 23
We are talking about the memory address 0x602010

Now, what happens in this program ?

int *i is a pointer to an int.

cout << i << endl;

shows the address in mem.

cout << *i << endl;

shows the value.

& is the referencing operator, it gives you the memory address of, say, an int. So, if your function foo requires a pointer int* (like *m in this case), you can as well use a referenced int (&n in this case).

You also see that the "new int" is in a different memory segment (the heap, memory address 0x602010) than int n (which is on the stack, memory address 0x7fffcab8a914).

Pointer arithmetics

i++ increases the pointer, so goes to the next int in mem. *i++ is the same as *(i++), so it also increases the pointer. (*i)++ increases the value in the mem by 1.

Pointer naming

char *ch

is a pointer to a char, equivalent to

char* ch

ch can consist of several characters, from the first character at the pointer till the first zero character.

Pointers in memory

You can not do the following code:

char *ch;
*ch="hallo";

because at the second line, the pointer does not exist, as C does not know where the free memory is. You must discover and allocate the free memory with malloc first:

char *ch;
ch=(char*)malloc(255);

for your convenience, you can also do an implicit malloc if the string length is known when creating the variable:

char *ch="hallo"; 

Variables that are known at compile time are laid onto the stack. Think of a counter as an example. Variables that are set up during run time (think of books in bookstores: you do not know how many you need before runtime) lodge in the heap. You cannot increase the size of variables on the stack during runtime, but you can for variables in the heap. You get e.g. a variable on the stack with the command

int i;

Use malloc or new to get a "run-time variable", a variable on the heap. Remember to free the memory in the heap again, else, we speak of a memory leak.

Know that a pointer is always as big as you need to identify one atomic part (byte) in the memory. So, under ia32, a pointer is 32bit big, under ia64, it is 64 bit big. And under C, an int is always as big as a pointer under every architecture. So, you can calculate with pointers as with ints. So, under ia32-C, an int is 32 bit big, under ia64, an int is 64 bit big.

Read from a memory address

To read from a memory address, create a pointer and assign it a memory address as in the following example.

main.cpp

#include <iostream>
#include <sys/mman.h>

using namespace std;

int main()
{
  cout << "Pointer manipulation" << endl;
  int* i;
  cout << "i is at " << i << " value " << *i << endl;
  i=(int*)0x400890;
  cout << "i is at " << i << " value " << *i << endl;
  // We now try to read from memory address 0x400891
  i=(int*)0x400891;
  cout << "i is at " << i << " value " << *i << endl;
}

Compile, link and run

g++ main.cpp && ./a.out

Here are some equivalents:

#include <iostream>
#include <sys/mman.h>

using namespace std;

int main()
{
  cout << "Pointer manipulation" << endl;
  int* i;
  cout << "i is at " << i << " value " << (int) *i << endl;
  // We now try to read from memory address 0x400891
  i=(int*)0x400891;
  cout << "i is at " << i << " value " << (int) *i << endl;
  // The following is equivalent, but represented in another way
  int n=0x400891;
  cout << "n is at " << n << " value " << &n << endl;
  // The following is equivalent, but represented in another way
  printf("n is at %i value %i", n, &n);
}

Function pointers

main.cpp

#include <iostream>

using namespace std;

void printhello()
{
  cout << "hello" << endl;
}

int main()
{
  cout << "printhello is at memory address " << (int*)printhello << endl;
}

Compile, link and run

g++ main.cpp && ./a.out

Output

printhello is at memory address 0x4008dc

Calling function pointers

main.cpp

#include <iostream>

using namespace std;

void printhello()
{
  cout << "hello" << endl;
}

int main()
{
  cout << "printhello is at memory address " << (int*)printhello << endl;
  cout << "calling printhello" << endl;
  ((void(*)()) printhello)();
}

Compile, link and run it using

g++ -o func main.cpp && ./func

In this example,

cout << ((void(*)) printhello) << endl;

outputs a memory address like 0x4008dc

cout << ((void(*)()) printhello) << endl;

outputs a "1".

((void(*)()) 0x4008dc)();

executes the code at 0x4008dc

((void(*)()) 0x4008dc);

does the same (nothing) as if you wrote the code line

1

Memory leaks

Here is how to provoke a memory leak, my program ram_gourmet:

main.cpp

class t

{
public:
t(){};
};

void pollute()
{
  t* polluter=new t();
}

int main()
{
  while (true) pollute();
}

Attention: It rendered my compi useless within 10 seconds.

Compile, link and run this using:

g++ -o ram_gourmet main.cpp && ./ram_gourmet

This program can be developed further to be a cache condenser

Assembler

Want to use assembler code in your C program ? Do it like this:

asm.cpp

int main()
{
  asm("nop");
}

Compile this using

g++ asm.cpp

Next interesting thing:

ls3523:~/test # cat main.cpp
int main()
{
  int i=5;
}
ls3523:~/test # g++ -c main.cpp
ls3523:~/test # objdump -d main.o

main.o:     file format elf32-i386

Disassembly of section .text:

00000000 <main>:
   0:   8d 4c 24 04             lea    0x4(%esp),%ecx
   4:   83 e4 f0                and    $0xfffffff0,%esp
   7:   ff 71 fc                pushl  0xfffffffc(%ecx)
   a:   55                      push   %ebp
   b:   89 e5                   mov    %esp,%ebp
   d:   51                      push   %ecx
   e:   83 ec 10                sub    $0x10,%esp
  11:   c7 45 f8 05 00 00 00    movl   $0x5,0xfffffff8(%ebp)
  18:   b8 00 00 00 00          mov    $0x0,%eax
  1d:   83 c4 10                add    $0x10,%esp
  20:   59                      pop    %ecx
  21:   5d                      pop    %ebp
  22:   8d 61 fc                lea    0xfffffffc(%ecx),%esp
  25:   c3                      ret
ls3523:~/test #    
ls3166:~ # hwinfo --cpu
01: None 00.0: 10103 CPU
  [Created at cpu.421]
  Unique ID: rdCR.j8NaKXDZtZ6
  Hardware Class: cpu
  Arch: IA-64
  Vendor: "GenuineIntel"
  Model: 0.0.5 "Itanium 2"
  Features: branchlong
  Clock: 1495 MHz
  Config Status: cfg=new, avail=yes, need=no, active=unknown

02: None 01.0: 10103 CPU
  [Created at cpu.421]
  Unique ID: wkFv.j8NaKXDZtZ6
  Hardware Class: cpu
  Arch: IA-64
  Vendor: "GenuineIntel"
  Model: 0.0.5 "Itanium 2"
  Features: branchlong
  Clock: 1495 MHz
  Config Status: cfg=new, avail=yes, need=no, active=unknown
ls3166:~ # cat main.cpp
int main()
{
  int i=5;
}
ls3166:~ # g++ -c main.cpp
ls3166:~ # objdump -d main.o

main.o:     file format elf64-ia64-little

Disassembly of section .text:

0000000000000000 <main>:
   0:   02 10 00 18 00 21       [MII]       mov r2=r12
   6:   e0 28 00 00 48 00                   mov r14=5;;
   c:   00 00 04 00                         nop.i 0x0
  10:   02 00 38 04 90 11       [MII]       st4 [r2]=r14
  16:   e0 00 00 00 42 00                   mov r14=r0;;
  1c:   01 70 00 84                         mov r8=r14
  20:   11 60 00 04 00 21       [MIB]       mov r12=r2
  26:   00 00 00 02 00 80                   nop.i 0x0
  2c:   08 00 84 00                         br.ret.sptk.many b0;;
ls3166:~ #

UniCode

See Unicode.

Factories

Factories are here to instantiate an object whose type you do not know at compile time - is this right?

Interesting problems

Problem 1

See http://websvn.kde.org/trunk/playground/utils/krep/

this works:

 // move the cursor to where it was
   QTextCursor* t=&mkrep->ktextedit_2->textCursor();
   t->setPosition(cursorpos);

but the following not:

   mkrep->ktextedit_2->textCursor().setPosition(cursorpos);


Problem 2

If you do not return a value, the program crashes:

testcase.pro

#-------------------------------------------------
#
# Project created by QtCreator 2010-01-22T11:08:42
#
#-------------------------------------------------

TARGET = testcase
TEMPLATE = app


SOURCES += main.cpp\
        mainwindow.cpp

HEADERS  += mainwindow.h

FORMS    += mainwindow.ui

main.cpp

#include <QtGui/QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
    class MainWindow;
}

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    QString crash() {}; // crashes because it returns nothing

protected:
    void changeEvent(QEvent *e);

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QString qs;
    qs=crash();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

mainwindow.ui

<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow" >
  <property name="geometry" >
   <rect>
    <x>0</x>
    <y>0</y>
    <width>600</width>
    <height>400</height>
   </rect>
  </property>
  <property name="windowTitle" >
   <string>MainWindow</string>
  </property>
  <widget class="QMenuBar" name="menuBar" />
  <widget class="QToolBar" name="mainToolBar" />
  <widget class="QWidget" name="centralWidget" />
  <widget class="QStatusBar" name="statusBar" />
 </widget>
 <layoutDefault spacing="6" margin="11" />
 <pixmapfunction></pixmapfunction>
 <resources/>
 <connections/>
</ui>

This also crashes if crash() is defined in the .cpp file. This also crashes if you write crash() instead of qs=crash(). This does not crash if you exchange QString crash() by int crash().

Your programs

Suggested Readings