martedì 18 dicembre 2012

Assembly programming using gcc (part 2)

In the previous post we saw the inline assembly programming features of gcc. It is now time to have a look at how to interface C programs with assembly code file (.S file containing only assembly instructions, assembled and linked like another C file).
To do this we need to understand how the C compiler realize the two well known mechanisms: parameter passing by value and parameter passing by reference.
High-level languages have standard ways to pass data known as calling conventions. For high-level code to interface with assembly language, the assembly language code must use the same conventions as the high-level language. These conventions allow one to create subprograms that are re-entrant. A re-entrant subprogram may be called at any point of a program safely (even inside the subprogram itself). The major concern of these conventions is the description of the rules governing the use of the system stack. Before to call the function the caller program must "push" the parameters  onto the stack in case of parameter passing by value or the parameter's address in case of passage by reference.
Let examine together the following C code example:

void f(char a, char b, char c)
{
   char buffer1[6];
   int d;
}
void main() {
  function(1,2,3);
}

If we want to see how the C compiler implementthe function's parameter passing mechanisms  in assembly language,  there are two possibilities:

  1. Feed gcc with this code and the -S option, i.e. gcc -S example.c;
  2. Disassemble the executable produced by the compiler:

Because in Eclipse is so straightforward to see the disassembled code, just look at the dissambly windows of the debugger perspective :), I've hard-copied this qindow in the figure on the left where we can see the assembly translation in AT&T  syntax of the previous C code.
Here we can see that the caller program before to invoke the function f , pushes onto the stack the values of the three parameters:

20 f(1,2,3);
004013b4: movl $0x3,0x8(%esp)
004013bc: movl $0x2,0x4(%esp)
004013c4: movl $0x1,(%esp)
004013cb: call 0x40138c <f>


Note that gcc, to speed up the things, prefers to use the movl instructions instead of the push ones to implement the push mechanism.


Anyway, what it really does is:
push 3;
push 2;
push 1;
call f;
So, the stack before the call of the function f, together with the memory layout of the executing process, is as depicted in the following figure:

Note that, depending on the implementation, the stack will either grow downward (towards lower memory addresses), or upward. In the case of the Intel, Motorola, SPARC and MIPS processors the stack grows downward. The stack pointer (SP) is also implementation dependent. It may point to the last address on the stack, or to the next free available address after the stack. In the case of Intel processors it points to the last address on the stack.
When the call instruction is executed to invoke the f function, the microprocessor save the 
At the beginning of the f function, the EBP register is saved onto the stack and is used to save the current address of the top of the stack (ESP). Why? Because the C calling convention requires the value of EBX to be unmodified by the function call. If this is not done, it is very likely that the program will not work correctly. So the next two assembly instruction will surely be the standar  prologue of our assembly routines skeleton linkable with C programs.
f:0040138c: push %ebp
0040138d: mov %esp,%ebp
Note that it is mandatory, at the end of the procedure, to restore the original value of EBP and deallocate the local variables restoring the initial stack pointer ESP. This is done with the following two assembly statements that will be the standard epilogue of our function skeleton: 
mov %ebp,%esp; deallocate locals
pop %ebp; restore original EBP value
Goinge back to our disassembly listing, we can see that the next instruction written by the compiler is:
0040138f:   sub $0x1c,%esp
Here the compiler is making room in the stack for the function f local variables. This is done decreasing the stack pointer ESP of 0x1c (decimal 28). Remember that the stack grows toward low memory addresses. But. Hey, wait a moment! Why does the compiler reserve 28 bytes to make room for the two variables buffer1 and d that needs only: size(buffer1)+size(d)=6+4=10 bytes?
The answer is composed by several pieces:

  • First of all, we must remember that memory can only be addressed in multiples of the word size. A word in our case is 4 bytes, or 32 bits. So our 6 byte buffer needs two words to be stored, that is 8 bytes instead of 6!
  • Then, we have to reserve room for d, that is one word or 4 bytes.
  • Then, the compiler want to reserve rooms to make a copy of the function parameters. There are three char parameters, but for each char variables we have to use 1 word. So we have 12 bytes for the parameters.
  • Finally, we must take into account that we pushed onto the stack the 4 bytes EBP register and this account for 4 bytes more .

Summing up all the previous quantities, we have 8+4+12+4=28 bytes. In this way, the compiler create a safe 28 bytes long stack frame to isolate the current contest of the function from the eventual new frame that , eventually, could be necessary to be implemented, for example, in case of calls to other function inside f o recursive calls of f. Puff... Puff...Puff.
This was a little boring! Wasn't it?
Anyway, lets try to summarize what we learned so far. The skeleton of our assembly routine callable from C program should be:
subprogram_label:
  push %ebp
  mov %esp,%ebp
  sub SZ,%esp ; SZ=# bytes needed by local variables
  ; subprogram code
  mov %ebp,%esp
  push %ebp
  ret

Note that the prologue and epilogue of a subprogram can be simplified by using two special instructions that are designed specifically for this purpose. The ENTER instruction performs the prologue code and the LEAVE performs the epilogue. The ENTER instruction takes two immediate operands. For the C calling convention, the second operand is always 0. The first operand is the number bytes needed by local variables. The LEAVE instruction has no operands.

To understand how to access to the passed parameters we can look at how the compiler did it in our simple program:
mov 0x8(%ebp),%ecx   ; move the value of a in ecx
mov 0xc(%ebp),%edx   ; move the value of b in edx
mov 0x10(%ebp),%eax  ; move the value of a in eax

It means that in our stack frame we have:

Location   : data 
EBP + 0x10 : content of c
EBP + 0xc  : content of b
EBP + 0x8  : content of a
EBP + 4    : Return address
EBP        : saved EBP

In case of parameters passed by reference, the caller program pushes the address of the variable instead of its content and the called routine access the address of the variable and can read and, eventually, modifies the content of the passed variables.

So far we saw how to deal with a void function, but what does it happen when we need to write a non void function? 

The C calling conventions specify how this is done. Return values are passed via registers. All integral
types (char, int, enum, etc.) are returned in the EAX register. If they are smaller than 32-bits, they are extended to 32-bits when stored in EAX. (How they are extended depends on if they are signed or unsigned types.) 64-bit values are returned in the EDX:EAX register pair. Pointer values are also stored in EAX. Floating point values are stored in the ST0 register of the math co-processor.

The C calling conventions specify also that after the subprogram is over, the parameters that were pushed on the stack must be removed by the caller program.  Other conventions are different. For example, the Pascal calling convention specifies that the subprogram must remove the parameters before returning to the caller program.



giovedì 13 dicembre 2012

Assembly programming using gcc (part 1)


First of all, why in the hell do we need to know about assembly programming when we have compiled high level language?
First of all, because it is fun :)! Then, instead of why, we should ask when do we need to use assembly instead of high level languages?
In fact, assembly language is used primarily for direct hardware manipulation, access to specialized processor instructions, or to address critical performance issues. Typical uses are device drivers, low-level embedded systems, and real-time systems. We'll not address all these issues but we'll focus only on how to program pieces of code in assembly  on a x86 hardware using the gcc tool chain.
How is it done? There are two ways:
  1. Inline assembly: Assembly instructions are written directly in the C code file
  2. Separate assembly code file: .S file containing only assembly instructions, assembled and linked like another C file.

X86 Assembly and the gcc tool chain

GCC uses AT&T assembly syntax. AT&T syntax uses the opposite order for source and destination operands. For example, to move the constant 1 into the eax register we have:
AT&T] movl $1, %eax                     Intel] mov eax, 1
Note that:
1) Register operands are preceded by the "%" character, including sections.
2) Immediate operands are preceded by the "$" character.
3) The size of memory operands are specified using the last character of the opcode. These are "b" (8-bit), "w" (16-bit), and "l" (32-bit).

Inline assembly

In C syntax, the inline term is used to instruct the compiler to insert the code of a function into the code of its caller at the point where the actual call is made. Such functions are called "inline functions". The benefit of inlining is that it reduces function-call overhead. In particular, inline assembly is a way to tell the compiler to insert, in a determinate position, a fragment of assembly code without trying to compile it.
asm() is the key  statement to inject assembly code in a C program. let see its syntax:

 asm ( assembler template 
           : output operands                  /* optional */
           : input operands                   /* optional */
           : list of clobbered registers      /* optional */
           );
where:
  • assembler template is a list of quoted assembly statements terminated with the '\n';
  • input and output operand are what you can guess they are;
  • clobbered registers are a list of register that have been somehow modified by the inlined assembly code.
Let see an example. Imagine you would write an extra super fast C function that perform the addition of two integers. Here is the code:
int MyAdd(int x, int y) { // add x and y and return the result
int z;
  asm( "addl %%ebx, %%eax;\n"
      : "=a" (z)
      : "a" (x), "b" (y) );
  return z;
}
The assembly statement addl %%ebx, %%eax; add the content of the ebx register with the content of the eax register and put the result in eax.
The first colon is where the output operand is to be described. "=a" (z) means that the output is the register eax and that the content of this register must be written in the z variable. The '=' sign is used t indicate the output read only operand. 'a' is called a constrain. Other constrain are:
r : any register
b : %ebx, %bx, %bl
c : %ecx, %cx, %cl
d : %edx, %dx, %dl
S : %esi, %si
D : %edi, %di
The second colon is the input operands section of the asm statement. Here are listed all the input operands with the same syntax used for the output operand. More than one operand can be specified separating each one with commas.
In the next example, a personalized integer division function,  we'll see another method to use C variables inside the inline assembly.   here are two more methods to address program variables from the inline assembly code.

int MyDiv(int arg1, int arg2, int quo, int rem) { 
  asm ( "movl $0x0, %%edx;"
        "movl %2, %%eax;" 
        "movl %3, %%ebx;" 
        "idivl %%ebx;" 
        : "=a" (quo), "=d" (rem) : "g" (arg1), "g" (arg2) );
}

The meaning of this function is straightforward. First put 0 in the edx register (the remainder), then load the two operand  arg1 and arg2 in eax and ebx respectively. Finally, idivl perform the integer division and put the quotient and the remainder in the eax and edx registers respectively. It easy to recognize the use of %n to represent the n-th variable of the input and output section of the asm statement.

And, if we don't like AT&T syntax? Don't worry gcc came to our rescue with the directive ".intel_syntax noprefix;". To use intel syntax you can add this directive at the beginning of the inline assembly code. Do not forget to put things as they were before adding at the end the directive ".att_syntax noprefix " to switch back to the AT&T syntax. Otherwise, the assembler will try to assemble the rest of the program using Intel syntax while gcc will continue to produce AT&T code.

Good inline assembly programming :).

martedì 4 dicembre 2012

Are Const expressions like 1+3 computed at compile time or at execution time?

The answer is:" Const expressions are computed at compile time".
Let see together a practical demonstartion.
If we consider the following code:

1
2
3
int main() {
int a=1+3;
}

And compile it using  gcc with -S option we obtain:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 .file "ottimo.c"
 .def ___main; .scl 2; .type 32; .endef
 .text
.globl _main
 .def _main; .scl 2; .type 32; .endef
_main:
 pushl %ebp
 movl %esp, %ebp
 subl $8, %esp
 andl $-16, %esp
 movl $0, %eax
 addl $15, %eax
 addl $15, %eax
 shrl $4, %eax
 sall $4, %eax
 movl %eax, -8(%ebp)
 movl -8(%ebp), %eax
 call __alloca
 call ___main
 movl $4, -4(%ebp)
 leave
 ret


As we can see, the compiler, even without the selection of optimization options, optimize the code and translate a= 1+3 as movl $4, -4(%ebp), that is a =4.

Q.E.D. :)

Global, local and static variables: which is the faster?

This is the first post of the Questions&Answers series. With the tag QeA I'll collect all the relevant answer I'm posting in international forum on C/C++. Hope you'll enjoy it!

The answer is: "Static variable are faster then global and local variables." Moreover, there is no difference between global and local variables, because both are allocated in the stack and must be popped from the stack every time you use them. Let see together a practical demonstration of it!

If we consider the following simple source code:

1
2
3
4
5
6
7
8
9
10
11
12
static int a;
int fTest();
int main() {
  int b;
  a=a+11;
  b=b+22;
}
int fTest(){
  int c;
  c=c+33;
}

compiling it using gcc with option -S give us the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 .file "variabili.c"
 .def ___main; .scl 2; .type 32; .endef
 .text
.globl _main
 .def _main; .scl 2; .type 32; .endef
_main:
 pushl %ebp
 movl %esp, %ebp
 subl $8, %esp
 andl $-16, %esp
 movl $0, %eax
 addl $15, %eax
 addl $15, %eax
 shrl $4, %eax
 sall $4, %eax
 movl %eax, -8(%ebp)
 movl -8(%ebp), %eax
 call __alloca
 call ___main
 addl $11, _a
 leal -4(%ebp), %eax
 addl $22, (%eax)
 leave
 ret
.globl _fTest
 .def _fTest; .scl 2; .type 32; .endef
_fTest:
 pushl %ebp
 movl %esp, %ebp
 subl $4, %esp
 leal -4(%ebp), %eax
 addl $33, (%eax)
 leave
 ret
.lcomm _a,16

Note that we used int constants 11, 22 and 33 to easily identify the fragment of assembler code of interest. 
We can observe that:
  1. For what concern the global variable b and the local variable c, they are both popped from the stack and then the addition is performed;
  2. For what concern the static int variable a the compiler use the addl instruction with the absolute address of the space reserved to the variable a with the .lcomm _a,16 assembly directive.
In conclusion, it seems that static variable perform better than global and local variables.
Q.E.D. :)

lunedì 3 dicembre 2012

Curiosità online sul C/C++ - N.1

Non riuscite a capire il tipo di una dichiarazione di variabile o di funzione oppure un casting estremo?
Sarebbe bello se qualcuno ve la leggesse in un linguaggio umano?
Oppure, sapete descrivere a parole il tipo del vostro puntatore di puntatore ma non riuscite a tradurlo in codice?
Per rispondere a tutte queste domande, fatevi aiutare da cdecl.org il traduttore simultaneo dal  Grammelot C all'inglese e viceversa. Sul sito, in alto a destra, trovate anche il link al sorgente del programma C che fa da traduttore.

venerdì 30 novembre 2012

Algoritmi in pillole: bubble sort


Animazione dell'algoritmo di Bubble sort da wikipedia
Animazione dell'algoritmo di ordinamento bubble sort da wikipedia
L'algoritmo controlla ogni coppia di elementi adiacenti  e li scambia se non sono ordinati. L'algoritmo procede così fino a quando non ci sono più scambi da fare. 

pseudocodice:
"per i che varia da 0 a N-1
   per j che varia da N-1 a i+1
      se l'elemento j è minore dell'elemento j-1
        allora scambia l'elemento j con l'elemento j-1"
Codice:
void bubblesort(int v[], int n) {
  int i=0,j,temp;
  int scambio = FALSE;
  do {
    scambio=FALSE;
    for (j=n-1;j>i;j--)
      if (v[j]<v[j-1]) {
        temp=v[j];
        v[j]=v[j-1];
        v[j-1]=temp;
        scambio=TRUE;
      }
      i=i+1;
    } while(i<n-1 && scambio);
}
La complessità computazionale asintotica è O(n^2).
Caso peggiore: array ordinato in senso inverso - T. esec. prop. a n^2
Caso migliore: array già ordinato                     - T. esec. prop. a n

giovedì 29 novembre 2012

Simple linked list o liste concatenate semplici (parte III): stampiamo le liste

Terzo post sull'argomento liste semplici collegate o linked lists. Spero non siate stanchi:)
Forse, se vedessimo insieme qualche funzione di servizio si potrebbe provare a superare la stanchezza?
Io ci provo.
Nei due precedenti post, qui e qui, abbiamo creato e modificato la nostra lista. Ma, come facciamo a vedere quello che c'è dentro la lista? Ovviamente con una funzione che ci stampa a schermo la lista.
Vediamo come:

mercoledì 28 novembre 2012

Algoritmi in pillole C: ordinamento con insertion sort


Animazione dell'algoritmo Insertion sort da Wikipedia
Se l'ordine desiderato è quello crescente,  l'algoritmo parte dall'ipotesi che l'array da ordinare sia composto da un primo elemento ordinato e una seconda parte "disordinata". Esamina quindi il primo elemento disordinato confrontandolo con tutti gli elementi "ordinati". Ad ogni confronto, se l'elemento disordinato è minore di quello ordinato si "inserisce", da qui il nome dell'algoritmo, l'elemento disordinato al posto di quello ordinato, scambiandoli di posto. Al termine di questa fase si avrà una parte ordinata composta da due elementi e una parte disordinata composta da N-2 elementi. L'algoritmo continua ad esaminare la parte disordinata fino alla fine dell'array.

Pseudocodice:
"per i che varia da 1 a N
   per j che varia da i a 0
     se l'elemento j è minore dell'elemento i allora
       scambia l'elemento i con l'elemento j"

Codice:
void insertSort(int * ar, int  n)
{ int i, j, temp;
for (i = 1; i < n; t++){ j = i; while( (j > 0) && (ar[j] < ar[j - 1] )) { temp = ar[j]; ar[j] = ar[j - 1]; ar[j - 1] = temp; j--; } } }
La complessità computazionale asintotica è O(n^2).
Caso peggiore: array ordinato in senso inverso - T. esec. prop. a n^2
Caso migliore: array già ordinato                      - T. esec. prop. a n

lunedì 26 novembre 2012

Piccolo tutorial sul linguaggio C - Gli operatori aritmetici e logici

Operatori aritmetici

Nel nostro termostato avremo bisogno di fare calcoli! Vediamo cosa ci offre il C a questo scopo. Il linguaggio C comprende i consueti operatori:
Operatori binari
+ Somma;
- Sottrazione;
/ Divisione;
* Moltiplicazione;
% Resto (mod);

Operatori unari (prefissi e suffissi)

++ incremento di 1
-- decremento di 1
In un precedente post, avevamo già visto quali possono essere i pericoli derivanti dall'uso non attento dei semplici operatori aritmetici binari in presenza di variabili e costanti di tipi diversi dal tipo del risultato. Per riassumere, il segno di diviso tra due interi è interpretato come divisione tra interi. Ovvero, 1/3 è uguale a 0! Anche gli operatori unari di incremento/decremento possono presentare dei comportamenti, a prima vista, bizzarri se non se ne conosce bene il funzionamento.  Commentiamo insieme un esempio:
i=1;     //istr. 1
j=ì++;  //istr. 2
k=++ì; //istr. 3
Secondo voi, dopo il precedente blocco di istruzioni, quanto vale j? Chi ha detto 2?Nessuno  dice 1? La risposta esatta è 1! Infatti, l'operatore suffisso viene applicato alla fine della valutazione dell'espressione nella quale si trova. Se il gioco è chiaro, la prossima domanda dovrebbe essere facile: quanto vale k? Bravi! Avete risposto bene. Il valore assunto da k è proprio 3, perché dopo l'istruzione 2 i vale 2 e quando viene valutata l'istruzione 3, i viene prima incrementato di 1, quindi vale 3, e poi usato nell'espressione a destra del segno uguale.

Operatori relazionali

Abbiamo già detto che il linguaggio C non prevede un tipo booleano primitivo? Se non lo abbiamo detto, lo diciamo qui :). In C, 0 (zero) corrisponde al valore booleano False, qualunque altro valore è True.
Gli operatori relazionali operano su valori interi o decimali e restituiscono il valore 0 oppure 1.
  • == Uguale a
  • != Non uguale a, Diverso da
  • > Maggiore di
  • < Minore di
  • >= Maggiore o uguale a
  • <= Minore o uguale a
Attenzione: per testare l'uguaglianza si usa l'operatore == e non l'operatore =, che è invece l'operatore di assegnamento.
Commentiamo insieme un esempio per capire cosa può succedere se usiamo l'operatore sbagliato.
Immaginano di voler disporre di una variabile di nome manuale che ci dica se è stat impostata la modalità manuale del termostato, vi ricordo che le specifiche del termostato sono in questo post e che la modalità manuale si seleziona premendo il tasto 'a'. Quindi, da qualche parte nel nostro programmino potremmo scrivere:
manuale = (comandotastiera='a'); // istr. 1
...
if (manuale) fai_qualcosa();          // istr. 2
Purtroppo, invece di scrivere comandotastiera=='a' abbiamos scritto comandotastiera='a', con il risultato di assegnare alla variabile manuale il risultato dell'espressione comandotastiera='a' che è esattamente 'a', ovvero il codice numerico del carattere 'a' che è pari a 97. Poiché 97 è maggiore di 0, il test sulla variabile manuale è sempre verificato e quindi il nostro termostato sarebbe sempre in modalità manuale acceso.
Si noti che l'istruzione 1, benché non faccia ciò che volevamo che facesse, è perfettamente legittima e quindi il compilatore non segnala alcun errore!
Visto cosa può succedere scrivendo = invece di ==?

Operatori logici

Lo abbiamo già detto: 0 è False, qualunque altro valore è vero!
Gli operatori logici sono
  • ! not, negazione Booleana (unario)
  • && and Booleano 
  • || or Booleano 

Operatori logici bit a bit

  • ~ Negazione bit a bit (unario) – cambia tutti gli 0 in 1 e gli 1 in 0 
  • & esegue l'And dei bit dell'operando di sinistra con i bit dell'operando di destra
  • | esegue l'Ordei bit dell'operando di sinistra con i bit dell'operando di destra
  • ^ esegue l'Or esclusivo dei bit dell'operando di sinistra con i bit dell'operando di destra
  • >> Shifta a destra di un numero di bit pari all'operando a destra (=divisione per potenze di 2)
  • <<  Shifta a sinistradi un numero di bit pari all'operando a destra (=moltiplicazione per potenze di 2)
Anche qui, attenzione a non confondere gli operatori logici con quelli bit a bit, il compilatore non vi aiuterà nel caso di errori!

Operatori di assegnamento 

Oltre al classico operatore di assegnamento il linguaggio C prevede le seguenti "scorciatoie":per effettuare operazioni di assegnamento e modifica di una variabile con un solo riferimento alla variabile stessa. La sintassi è la seguente:
var op= espr;
dove op può essere uno qualunque tra gli operatori: + - * / % & | ^ << >>.  Il significato è il seguente: assegna a var il risultato di var op espressione. Vediamo con un esempio a cosa servono. L'istruzione 
i  =  i * 10;
può essere riscritta con l'operatore di assegnamento è equivalente a
ì *=10;
Gli ideatori del C  hanno pensato che questo tipo di operatori avrebbe aumentato la leggibilità dei programmi  introducendo questo modo di rappresentare di modifica ad una variabile basata su se stessa.
Inoltre, l'uso di questi operatori può contribuire alla scrittura di codice più efficiente. Il compilatore quando incontra un operatore di assegnamento può implementare l'operatore con un istruzioni più veloci che nel caso di assegnamento semplice.

sabato 24 novembre 2012

Precedenze degli operatori del linguaggio C


La seguente tavola delle precedenze degli operatori in C dovrebbe essere sempre a portata di mano. Soprattutto quando si sta imparando il linguaggio di programmazione. Ma, anche i più esperti potranno trarre beneficio dall'avere riassunti in una unica tabella tutti gli operatori con le loro precedenze e associatività (= ordine di precedenza che viene seguito quando si trovano più operatori di uguale precedenza nella stessa espressione).


Operatore
Descrizione
Associatività
( )
[ ]
.
->
++ --
Parentesi (chiamata a funzione) (vedi Nota 1)
Parentesi quadre (array)
selezione di membri di oggetti dal nome
come sopra, ma dal puntatore
incrementi/decrementi postfissi (vedi Nota 2)
da sx a dx

++ --
+ -
! ~
(type)
*
&
sizeof
incrementi/decrementi prefissi
più/meno unari
negazione logia/complemento bit a bit
cast (conversione di tipi)
deferenza
indirizzo (di operandi)
dimensioni in byte (dipendente dalla piattaforma)
da dx a sx
*  /  %
moltiplicazione/divisione/modulo
da sx a dx
+  -
Aaddizione/sottrazione
da sx a dx
<<  >>
shift a sinistra/destra bit a bit
da sx a dx
<  <=
>  >=
operatori relazionali minore di/minore o uguale a
operatori relazionali maggiore di/maggiore o uguale a
da sx a dx
==  !=
operatore relazionale uguale a/ non uguale a
da sx a dx
&
AND bit a bit
da sx a dx
^
OR esclusivo bit a bit
da sx a dx
|
OR bit a bit
da sx a dx
&&
AND logico
da sx a dx
| |
OR logico
da sx a dx
? :
condizionale ternario
da dx a sx
=
+=  -=
*=  /=
%=  &=
^=  |=
<<=  >>=
assegnazione
assegnazione con addizione/sottrazione
assegnazione con moltiplicazione/divisione
assegnazione con modulo/AND bit a bit
assegnazione con OR esclusivo bit a bit/Or  bit a bit
assegnazione con shift a sinistra/shift a destra bit abit
da dx a sx
,
virgola (separatore di espressioni)
da sx a dx


Nota 1:Le parentesi sono usate anche per raggruppare sottoespressioni per forzare le precedenze. Nel caso di parentesi nidificate, l'ordine di precedenza è dalla più interna alla più esterna.

Note 2:Gli operatori di incremento postfissi hanno alta precedenza, ma l'incremento7decremnto reale è rimandato fino alla fine della valutazione dell'espressione. Ad esempio nell'esecuzione di a=3*i++, se i valeva 1 prima di questa istruzione,  a varrà 3 e i varrà 2. Cioè i è incrementato al termine dell'esecuzione dell'istruzione.

mercoledì 21 novembre 2012

Simple linked list o liste concatenate semplici (parte II)


Se chiedete a 10 programmatori diversi : "Quali funzioni servono per gestire le liste?", probabilmente otterreste 10 risposte diverse, perché dipende dall'applicazione e dalle strutture dati complesse che si intendono implementare con le liste. Quindi, senza la pretesa di essere esaustivi, analizziamo insieme alcune funzioni di "base" per la creazione, modifica, stampa ed eliminazione di liste. In particolare, in questa piccola serie di post sulle liste collegate semplici, vedremo le seguenti funzioni:


void push(int pl, nodeP *sP, nodeP *eP);
void pop(int *pl,  nodeP *sP, nodeP *eP);
void delnode(int pl, int global, nodeP *sP, nodeP *eP);
void genlist(int N, nodeP* sP, nodeP* eP);
void destroy(nodeP *sp);
void printlistv(nodeP head);
void printlisto(nodeP head);

sabato 17 novembre 2012

Come si misura ill tempo di esecuzione di un blocco di codice C?

Misurare le performance di un frammento codice o di una funzione può essere utile per valutare le prestazioni (benchmarcking) del proprio codice o per decidere tra diversi approcci per la soluzione di un certo problema qual'è il più veloce. In un precedente post, abbiamo visto come misurare il tempo di esecuzione di un eseguibile utilizzando il comando Unix Time, disponibile anche su Windows in CigWin. Invece, per misurare la velocità di esecuzione di un frammento di codice all'interno di un programma scritto in C si può usare la funzione clock(), il cui prototipo si trova in <time.h>.
La funzione clock() restituisce il numero di cicli di clock dall'ultima chiamata della funzione. In <time.h> c'è anche una costante chiamata CLOCK_PER_SEC che fornisce il numero di clock per secondo per la piattaforma hardware e software che si sta usando.

mercoledì 14 novembre 2012

Simple linked list o liste concatenate semplici (parte I)


Una lista concatenata semplice, o linked list e per brevità da qui in poi lista, è una struttura dati che consente la memorizzazione di informazioni in elementi, detti anche nodi, non contigui collegati sequenzialmente  da un legame .

Lista concatenata semplice di 3 interi

domenica 11 novembre 2012

Algoritmi in pillole C: ordinamento con selection sort

Selection sort animata da wikipedia
Se l'ordine desiderato è quello crescente,  l'algoritmo "seleziona" il valore più piccolo dell‟array di n elementi e lo scambia con quello al primo posto. Successivamente, "seleziona" il più piccolo tra gli n-1 elementi rimasti e lo scambia con quello al secondo posto, e così via fino a che non rimane un solo elemento, l'ultimo e il più grande. 

pseudocodice:
"per i che varia da 1 a  N   per j che varia da i+1 a N       se l'elemento j è minore dell'elemento i allora          scambia i con j"


Codice:
void selectionsort(int v[], int n){
 int i,j,min,temp;
 for (i=0;i<n-1;i++) {
   min=i;
   for (j=i+1;j<n;j++)
     if (v[j]<v[min]) min=j;
   temp=v[i];
   v[i]=v[min];
   v[min]=temp;
 }
}


La complessità computazionale asintotica è O(n^2).
Caso peggiore: array ordinato in senso inverso - T. esec. prop. a n^2
Caso migliore: array già ordinato                     - T. esec. prop. a n^2


Piccolo tutorial sul linguaggio C - le conversioni di tipo

Le specifiche del progetto del nostro termostato indicano che la funzione che dialoga con il sensore esterno fornisce la temperatura nel seguente modo:

Per ottenere la temperatura ambientale si dovrà usare la funzione dammitemperatura() che restituisce un valore intero compreso tra  -1000  e +1000 per valori di temperatura compresi tra -50 °C e +50 °C. il prototipo della funzione dammitemperatura() è il seguente:
  • int dammitemperatura();

domenica 4 novembre 2012

Piccolo tutorial sul linguaggio C - i puntatori in C


Uno dei meccanismi più potenti e, allo stesso tempo, più insidiosi del C è l'uso dei puntatori. Un puntatore è una variabile, quindi un contenitore di dati, che, invece che contenere il dato di interesse, contiene il valore numerico dell'indirizzo di memoria del dato di interesse. Per fare un po di chiarezza su questo argomento, commentiamo insieme il programma di esempio riportato qui a sinistra. Come abbiamo già visto in un post precedente, utilizziamo anche una rappresentazione grafica stilizzata della memoria fisica del computer riportata nella figura qui sotto.

sabato 3 novembre 2012

Piccolo tutorial sul linguaggio C - le variabili in C



Quando dichiariamo una variabile in C, il compilatore riserva in memoria, in una struttura dati denominata stack, il numero di bytes necessari per memorizzare il tipo di dato della variabile. Ad esempio, nella figura qui sotto, possiamo vedere cosa succede alla memoria fisica del computer quando eseguiamo il programma C riportato nella figura a sinistra.

giovedì 1 novembre 2012

Piccolo tutorial sul linguaggio C - tipi di dato primitivi


Questo è il primo di una breve serie di post dedicati ad un piccolo tutorial sul linguaggio C. Durante questo breve viaggio nel C, come pretesto per introdurre i costrutti del linguaggio stesso, realizzeremo un semplice termostato ambiente in C.




 Le specifiche del progetto sono le seguenti: 

Il mio ritratto al museo della scienza di Londra :)

sabato 20 ottobre 2012

Quale compilatore C produce il codice più "veloce"? The winner is ...

Nel  precedente post ci eravamo chiesti quale compilatore C gratuito per Windows producesse il codice più veloce e abbiamo stabilito il nostro protocollo di test. Oggi vediamo i risultati. Per misurare il tempo di esecuzione del codice compilato abbiamo usato il comando unix Time reso disponibile da CygWin. Lo script usato è il seguente:




And, the winner is ...

Dev-C++. Congratulazioni!

I risultati della prova sono riportati nella figura qui a destra. Dev-cpp è quello che produce codice più veloce. La versione di Dev-Cpp installata usa gcc MingW versione 4.6.2 mentre la versione di gcc Mingw testata è la 4.6.1 (ultima stabile sul sito ufficiale Mingw).
Tutti i compilatori testati sono stati invocati senza nessuna opzione di ottimizzazione del codice.

sabato 13 ottobre 2012

Quale compilatore C gratuito per windows produce il codice più veloce? (parte 1)

E poi, a che cosa potrebbe mai servire saperlo? Beh, intanto la curiosità è umana :). Poi, ci sono casi in cui si ha bisogno di produrre codici veloci per applicazioni realtime o per applicazioni batch su grandi moli di dati. Quest'ultima circostanza potrebbe sembrare alquanto fumosa per chi non ha mai visto programmi che macinano grosse moli di dati per produrre, ad esempio, stampe quali quelli usati per
stampare le bollette delle utenze o le famigerate cartelle esattoriali. Detto in altro modo, ridurre il tempo di elaborazione della singola unità di informazione trattata di qualche punto percentuale fa la
differenza quando si elaborano milioni di tali unità di informazione. Chi invece ha esperienza di real time o big batch sa di che cosa stiamo parlando. L'idea l'ho avuta alcuni giorni fa quando ho lettoquesto interessante post di juhan sul blog OK, panico circa il confronto sulla velocità di esecuzione di un prefissato algoritmo "macina numeri" in diversi linguaggi di programmazione. Io ho pensato di mettere alla prova 5 compilatori C/C++ per windows disponibili gratuitamente.
I compilatori che useremo sono quelli in uso su c-lessons-online.org. Il codice usato è quello del post che mi ha ispirato con qualche piccolissima modifica lo riporto qui di seguito:
Nel prossimo post compileremo a parità di opzioni standard il codice con i 5 compilatori individuati e commenteremo i risultati insieme.

#include <stdio.h>
#include <math.h>
static double f[14];static double fsin(double a) {
   
return a - pow(a, 3.0) / f[3] + pow(a, 5.0) / f[5]
            - pow(a, 7.0) / f[7] + pow(a, 9.0) / f[9]
            - pow(a, 11.0) / f[11] + pow(a, 13.0) / f[13];
}
static double fcos(double a) {
   
return 1.0 - pow(a, 2.0) / f[2] + pow(a, 4.0) / f[4]
              - pow(a, 6.0) / f[6] + pow(a, 8.0) / f[8]
              - pow(a, 10.0) / f[10] + pow(a, 12.0) / f[12];
}
static double myln(double x) {
   
if (x == 0.0) {
       
return -1.0e20;
   }
else {
       
double r = (x - 1.0) / (x + 1.0);
       
return 2.0 * (r + pow(r, 3.0) / 3.0
           + pow(r, 5.0) / 5.0
           + pow(r, 7.0) / 7.0
           + pow(r, 9.0) / 9.0);
   }
}
static double ln10 = 2.2548000538926538; //
static double mylog(double x) {
   
return x / ln10;
}
int main(int argc, char **argv) {
   
int i, j;
   f[0] = 1.0;
   
for (i = 1; i < 14; i++)
       f[i] = i * f[i - 1];

   ln10 = myln(10.0);
   
int deg = 60 * 60;
   
int nsteps = 180 * deg;
   
double step = M_PI / nsteps;
   
double ssum;
   
double csum;
   
double a, s, c, t = 0.0;
   
double ls, lc;

   
for (j = 1; j < 11; j++) {
       ssum = 0.0;
       csum = 0.0;
       
for (i = 0; i <= nsteps; i++) {
           a = i * step;
           s = fsin(a);
           ls = mylog(myln(s));
           ssum += s;
           c = fcos(a);
           lc = mylog(myln(c));
           csum += c;

           
if ((i % (10 * deg)) == 0) {
               
if (c != 0.0)
                   t = s / c;

               printf(
"%3d %11.8f %11.8f %11.8f %15.8e\n",
                      (i / deg), a, s, c, t);
               printf(
" %15.8e %15.8e\n", ls, lc);
           }
       }
   }
   printf(
"%15.8e\n", ssum);
   printf(
"%15.8e\n", csum);
}