Outils pour utilisateurs

Outils du site


programmation_c_gcc

debug

core dump

memory leak

cas 1

cas 2

Lien: http://www.devx.com/tips/Tip/20915

Essayer “mtrace”.

$ man mtrace
#include <mcheck.h>

void foo( void )
{
  // FIXME
  // lot of malloc/realloc/free usage
}  

void int main( void )
{
  setenv("MALLOC_TRACE","/tmp/mtrace.txt",1);

  mtrace();

  foo();
}
$ gcc -g -o foo foo.c

./foo

$ mtrace foo /tmp/mtrace.txt | less

backtrace

Divers

flock

Accès partagé, coopératif, à un fichier.

logs

Pour écrire en fin de fichier (des logs par exemple)

Soit un fichier de log ouvert comme cela:

if ( -1 == (fd=open(filename,O_WRONLY|O_CREAT,0644)) ) {
  perror("open");
}
else {
  // ... suite plus bas...
}

Donc:

fd descripteur du fichier ouvert en ecriture seule

On bloque ainsi, à partir de la fin du fichier:

// bloquer l'acces au fichier
struct flock lock;

memset( &lock,0,sizeof(lock) );

lock.l_type = F_WRLCK;
lock.l_whence = SEEK_END;
lock.l_start = 0;
lock.l_len = 0;

if ( -1==fcntl(fd,F_SETLKW,&lock) ) perror("lock log");
else {
  // ... suite plus bas...
}

Voila, c'est bloqué, à partir de la fin du fichier (connu a cette instant) et au dela.

On imagine qu'on se positionne a la fin du fichier et qu'on ecrit dedans…

Pour retirer le verrou… il faut debloquer tout le fichier !

memset( &lock,0,sizeof(lock) );

lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;

if ( -1==fcntl(fd,F_SETLKW,&lock) ) perror("lock log");

J'ai calé quelques instants sur cette “fonctionalité”.

D'autant plus qu'il n'y a pas de remonté d'erreur si le “F_UNLCK” est sans effet.

uuid

Creer des identifiants unique de sessions…

Installer les “lib” et “includes”:

# aptitude install uuid-dev

Composer uuid_test.c :

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <uuid/uuid.h>

int main( int argc, char *argv[] )
{
  uuid_t myid;
  char buf[36+1];
  
  // generer uuid (16 chars)  
  uuid_generate(myid);

  // le rendre human readable
  uuid_unparse(myid,buf); 

  printf("uuid=%s\n",buf);

  return EXIT_SUCCESS;
}

Compiler:

$ gcc -o uuid uuid_test.c -luuid

Executer:

$ ./uuid
uuid=33045d19-1469-4c2b-a151-a7b927621af4

Existe aussi en ligne de commande:

$ uuidgen
a0fdf4f1-9fb5-4c0e-8f23-f330b3af65fc

utf8

definition

UTF-8 (UCS transformation format 8 bits) est un format de codage de caractères défini pour les caractères Unicode (UCS). Chaque caractère est codé sur une suite d'un à quatre octets. UTF-8 a été conçu pour être compatible avec certains logiciels originellement prévus pour traiter des caractères d'un seul octet. L'UTF-8 c'est bien, mais ce n'est pas de l'ASCII sur 1 octet.

Source: http://fr.wikipedia.org/wiki/UTF-8

Le bon côté:

  • ce n'est qu'un “format de codage de caractères”. (un Hack)
  • toutes les fonctions historiques du C fonctionnent avec les chaines “UTF-8”
  • il y a bien un simple octet '\0' en fin de chaine
  • tous les codes < 0x80 sont codés sur 1 octet. Exemple: 'A' = 0x41

Le côté obscur:

  • Un caractère UTF-8 peut être codé sur 1 à 6 octets
  • Le codage du caractère respecte la norme Unicode (ou plutot ISO 10646)

Donc, quand l'UTF-8 est décodé, le code est sur 32 bits (wchar_t) et c'est de l'Unicode.

En C

Rapidement

Ok:

char *s1 = "Hello World";      // UTF-8 string
wchar_t *s2 = L"Hello World";  // wide string
char a1 = 'A';                 // 0x41
:!: L“string” est une “wide string” , c'est a dire un tableau de “wide char”
:!: “string” (sans L ) est une chaine classique definie par sont environnement (UTF-8)

Pas Ok:

char a1 = 'À';                 // Car en UTF-8, c'est codé sur 2 octets: "0xC3 0x80", et le compilo va raler, a juste titre.

Pas Ok aussi:

int a1 = 'À';                  // Toujours le même problème. les apostrophes ne s'appliquent qu'aux caractères codables sur 1 octet!

Par contre:

int a1 = L'À';                 // C'est mieux

Mais il est préférable d'utiliser directement les “wide char”:

wchar_t a1 = L'À';             // Parfait.
:!: sizeof(wchar_t) = 4

En “char”:

char chaine[] = "Hello World";
int chaine_longueur = strlen( chaine );   // strlen(chaine) == sizeof(chaine)-1

En “wide char”:

wchar_t chaine[] = L"Hello World";
int chaine_longueur = wcslen( chaine );   // wcslen(chaine) =  (sizeof(chaine)-1)/sizeof(wchar_t)

C et UTF-8

Concretement, par exemple, en UTF-8, la chaine de caractères “Vidéo” à une longueur de 6 octets ! Et oui, le “é” occupe ici 2 octets.

Dans un environnement en UTF-8, en C, ca donne:

$ vi test.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <locale.h>

int main( int argc, char *argv[] )
{
  const char *chaine = "Vidéo";

  setlocale(LC_CTYPE, "");

  printf("chaîne: %s  %d octets  %d caractères\n",chaine,strlen(chaine),mbstowcs(NULL,chaine,0));

  if ( argc>1 ) {
    char *arg = argv[1];

    printf("   arg: %s  %d octets  %d caractères\n",arg,strlen(arg),mbstowcs(NULL,arg,0));
  }

  return 0;
}
$ gcc -std=c99 -Wall -o test test.c
$ ./test Vidéo
chaîne: Vidéo  6 octets  5 caractères
   arg: Vidéo  6 octets  5 caractères

Sans la ligne setlocale(LC_CTYPE, “”); , la fonction mbstowcs() est incapable de convertir l'UTF-8 (je crois que c'est calé en dur sur “C” ou “ISO 8859-1” ….)

“When a C program executes the setlocale(LC_CTYPE, ”“) function, the library will test the environment variables LC_ALL, LC_CTYPE, and LANG in that order, and the first one of these that has a value will determine which locale data is loaded for the LC_CTYPE category (which controls the multibyte conversion functions).”

Source: http://www.cl.cam.ac.uk/~mgk25/unicode.html#activate

Lien: http://mail.nl.linux.org/linux-utf8/2004-11/msg00009.html

Si besoin, il faut forcer le standard “C99” avec -std=c99 .

Compilons en forcant un source en “ISO-8859-1” :

$ gcc -std=c99 -fexec-charset=ISO-8859-1 -o test test.c

Puis testons:

$ ./test Vidéo
cha�ne: Vid�o  5 octets  -1 caract�res
   arg: Vidéo  6 octets  5 caract�res

D'abord, ca s'affiche pas bien (normal dans mon terminal UTF-8), et puis la fonction mbstowcs retourne une erreur (-1) parce qu'elle ne reconnait pas la chaine comme UTF-8 (?)

Wide Char

En s'inspirant de l'exemple ci-dessus:

...
#include <wchar.h>
...
{
  wchar_t tab_a[] = { L'a', L'à' , L'â', L'ä', L'\0' };

  for( int i=0; tab_a[i]; i++ ) {
    printf("%d: %X %lc\n",i,tab_a[i],tab_a[i]);
  }
}
...

Devrait produire une sortie comme cela:

0: 61 a
1: E0 à
2: E2 â
3: E4 ä

Exemple de conversion

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <locale.h>
#include <wchar.h>

int main( int argc, char *argv[] )
{
  setlocale(LC_CTYPE, "");

  char chaine[] = "Héllô Wörld!";   // chaine multi-byte
  mbstate_t ps;
  size_t n;
  const char *p_chaine;

  // Definir la longueur de la chaine en conversion "wide char"
  memset(&ps,0,sizeof(ps));

  p_chaine = chaine;      // <--- parce que la fonction peut modifier le pointeur en retour...
  n = mbsrtowcs(NULL,&p_chaine,0,&ps);

  if ( (int)n<0 ) exit( EXIT_FAILURE );
  else {
    // Allouer la place necessaire sur la pile (Merci le -std=c99)
    wchar_t ws[n+1];

    printf("chaine a pour longueur: %d\n",n);

    // (re)Faire la conversion "mb" => "wc"
    memset(&ps,0,sizeof(ps));

    p_chaine = chaine;
    if ( n != mbsrtowcs(ws,&p_chaine,n+1,&ps) ) exit( EXIT_FAILURE );

    // Afficher le resultat de la conversion , caractère par caractère
    for( int i=0; ws[i]; i++ ) {
      printf("%2d : %08X %lc\n",i,ws[i],ws[i]);
    }

    // Conversion de "wide char" en "mb"
    const wchar_t *p_ws;
    size_t n_ws;

    memset(&ps,0,sizeof(ps));
    
    p_ws = ws;
    n_ws = wcsrtombs(NULL,&p_ws,0,&ps);

    if ( (int)n_ws<0 ) exit( EXIT_FAILURE );

    char mb[n_ws+1];

    memset(&ps,0,sizeof(ps));

    p_ws = ws;
    if ( n_ws != wcsrtombs(mb,&p_ws,n_ws+1,&ps) ) exit( EXIT_FAILURE );

    printf("chaine multi-byte: %s\n",mb);
  }

  return 0;
}

Compilation:

$ gcc -Wall -std=c99 -o test test.c

Résultat:

$ ./test
chaine a pour longueur: 12
 0 : 00000048 H
 1 : 000000E9 é
 2 : 0000006C l
 3 : 0000006C l
 4 : 000000F4 ô
 5 : 00000020
 6 : 00000057 W
 7 : 000000F6 ö
 8 : 00000072 r
 9 : 0000006C l
10 : 00000064 d
11 : 00000021 !
chaine multi-byte: Héllô Wörld!

error: converting to execution character set

Lien: http://www.debianadmin.com/flip-convert-text-file-line-endings-between-unix-and-dos-formats.html

V'la que gcc refuse de compiler un code simple…

Les problèmes:

$ file boursorama-split.c
boursorama-split.c: ISO-8859 C program text, with CRLF line terminators

(Rappel: je suis censé être en UTF…)

  1. Ca devrait être de l'UTF-8 !
  2. Le format de fin de ligne est DOS ! ( et vim me parle de conversion DOS)

Transformer en UTF-8 (qui est mon environnement):

$ mv boursorama-split.c old-boursorama-split.c
$ iconv -f ISO_8859-1 old-boursorama-split.c > boursorama-split.c

Vérifions:

$ file boursorama-split.c
boursorama-split.c: UTF-8 Unicode C program text, with CRLF line terminators

Transformer les “CRLF” en “LF”, qui est le format “unix”:

# aptitude update
# aptitude install flip

Et puis:

$ flip -u boursorama-split.c
boursorama-split.c: binary file, not converted

Dans ce cas, on force même les “binary” (bizarre quand même):

$ flip -b -u boursorama-split.c

Vérifions:

$ file boursorama-split.c
boursorama-split.c: UTF-8 Unicode C program text

Voila.

DOS

Lien: http://gedcom-parse.sourceforge.net/doc/encoding.html

La conversion d'un fichier DOS vers Linux peut fonctionner comme cela:

$ iconv -f CP437 old-minitel-powa.h >minitel-powa.h

Conversion par lot:

$ cd dosfile
$ find * -exec iconv -f CP437 {} -o ../{} \;

En perl

base

Par defaut, le compilateur Perl ignore (volontairement pour des questions de compatibilité ascendante) l'environnement UTF-8.

Soit l'exemple:

#!/usr/bin/perl -w

use strict;
use warnings;
{
  my $mot = 'Vidéo';

  print "Voilà le mot: ", $mot, " dont la longueur est ", length( $mot ) , "\n";
}

Sous shell (environnement UTF-8):

$ ./utfhello.pl
Voilà le mot: Vidéo dont la longueur est 6

Alors qu'en fait, “Vidéo” a une longueur de 5 caractères ! bien sur!
Mais à bien une longueur de 6 octets !

Dans ce cas de scrypte écrit avec des caractères UTF-8, il faut ajouter:

use utf8;

( A contrario, si cela pose un problème: no utf8; )

Ainsi:

$ ./utfhello.pl
Voil� le mot: Vid�o dont la longueur est 5

Ok pour la longueur, mais ça affiche n'importe quoi !

Ceci parce que “PerlIO” qui gère l'affichage des caractères, ignore l'UTF-8 … grrrr…

2 solutions:
Soit: Il faut utiliser l'option -C pour le forcer à afficher les caractères en UTF-8.

$ perl -C utfhello.pl

Soit: Il faut ajouter la ligne suivante (au début du scripte):

binmode(STDOUT, ":utf8");

Et voila:

$ ./utfhello.pl
Voilà le mot: Vidéo dont la longueur est 5

De même, lorsqu'on connait l'encodage d'un fichier , à lire ou a créer, il peut être nécessaire de le préciser ainsi:

# en lecture
open( my $f , '<:utf8', $filename );
# en ecriture
open( my $f , '>:utf8', $filename );

et pour un fichier déjà ouvert:

binmode( $handle , ":utf8" );

encode

Exemple pour jongler entre “utf8” et le mode par defaut.

my $m = "Vidéo";

utf8::encode($m);

print "Aprés encode: $m de longueur ", length( $m ) , "\n";

utf8::decode($m);

print "Aprés decode: $m de longueur ", length( $m ) , "\n";

Sortie écran:

Aprés encode: Vidéo de longueur 6
Aprés decode: Vidéo de longueur 5
programmation_c_gcc.txt · Dernière modification : 2010/07/13 16:16 de thierry