Table des matières
debug
core dump
memory leak
cas 1
Essayer: valgrind
Lien: http://www.cyberciti.biz/faq/valgrind-check-for-memory-leaks-in-c-programs/
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
Lien: http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes
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
Lien:
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…)
- Ca devrait être de l'UTF-8 !
- 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