humani nil a me alienum puto

Aller au contenu | Aller au menu | Aller à la recherche

Tag - programmation

Fil des billets - Fil des commentaires

mercredi 9 septembre 2009

doteuh nette, ça pète !

Ça fait longtemps que je n'ai pas craché sur la techno de m$ .Net. En allant sur une page de vogue, voici ce qui est arrivé :



Erreur du serveur dans l'application '/'.

Une exception de type 'System.OutOfMemoryException' a été levée.

Description : Une exception non gérée s'est produite au moment de l'exécution de la demande Web actuelle. Contrôlez la trace de la pile pour plus d'informations sur l'erreur et son origine dans le code.

Détails de l'exception: System.OutOfMemoryException: Une exception de type 'System.OutOfMemoryException' a été levée.

Erreur source:

Ligne 31 :         else
Ligne 32 : {
Ligne 33 : dom.Load(xmlFilePath);
Ligne 34 : CacheDependency xmlFileCacheDependency = new CacheDependency(xmlFilePath);
Ligne 35 : Cache.Add(cacheKey, dom, xmlFileCacheDependency, DateTime.Now.AddHours(1), Cache.NoSlidingExpiration, CacheItemPriority.AboveNormal, null);

Fichier source : f:\Sites\Vogue\wwwroot\_marketResources\UserControls\F6\BannerGenerator.ascx.cs    Ligne : 33

Trace de la pile:

[OutOfMemoryException: Une exception de type 'System.OutOfMemoryException' a été levée.]
System.Xml.XmlNamedNodeMap.get_Nodes() +26
System.Xml.XmlNamedNodeMap.AddNodeForLoad(XmlNode node, XmlDocument doc) +41
System.Xml.XmlAttributeCollection.Append(XmlAttribute node) +41
System.Xml.XmlLoader.LoadNode(Boolean skipOverWhitespace) +190
System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc) +20
System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace) +129
System.Xml.XmlDocument.Load(XmlReader reader) +108
System.Xml.XmlDocument.Load(String filename) +87
_marketResources_UserControls_Adv_BannerGenerator.Page_Load(Object sender, EventArgs e) in f:\Sites\Vogue\wwwroot\_marketResources\UserControls\F6\BannerGenerator.ascx.cs:33
System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +14
System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +35
System.Web.UI.Control.OnLoad(EventArgs e) +99
System.Web.UI.Control.LoadRecursive() +50
System.Web.UI.Control.LoadRecursive() +141
System.Web.UI.Control.LoadRecursive() +141
System.Web.UI.Control.LoadRecursive() +141
System.Web.UI.Control.LoadRecursive() +141
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +627


Informations sur la version : Version Microsoft .NET Framework :2.0.50727.4016; Version ASP.NET :2.0.50727.4016

C'est tout de même assez fabuleux. Jolie présentation, en tout cas...

mercredi 26 novembre 2008

oh, ce qu'ils n'ont pas osé faire !

RealBasic : via une google ad, je découvre cet IDE+compilateur crossplatform win/linux/mac (toutes versions de mac), qui... réinvente VB6  >_<". Et là normalement, il faut troller à mort, mais voilà, après un coup d'oeil, c'est simplement épatant (j'aime particulièrement le debug pas à pas où l'on peut visualiser le contenu d'une image !). Et je suis bien embêté pour me moquer, maintenant, parce que chiotte, c'est quand même du basic avec des Dim et autres saloperies. N'empêche, c'est manifestement carrément meilleur que le Visual Basic de m$ (même à l'exécution : pas besoin d'une dll de réinterprétation, la plaie absolue de VB pour filer ses programmes à quelqu'un d'autre), et c'est par des texans (ouch !) qui vous traduisent le site ouèbe dans votre langue sans qu'on ait rien à lui dire.

Purée, y'a des gens qui en 2008 codent un compilateur propriétaire complet from scratch, avec une IDE complète (qui n'est pas un plugin eclipse), avec des features puissamment clickodromesques (pareil que sous VB, on construit sa fenêtre en WYSIWYG), pour... du Basic. Va me falloir un certain temps pour m'en remettre...

samedi 13 septembre 2008

électronicien raté ?

Les informaticiens embarqués sont souvent épatant pour les informaticiens-tout-court : ils font des choses toutes bizarres qui paraissent bien merveilleuses, pour activer des choses très bas niveau, très sytème. Pour preuve cet extrait de Makefile que l'on trouve sur le Hors Série de Linux Mag' spécial électronique, à la page 16, dans un article écrit par Denis Bodor, le rédac en chef du magazine lu (et compris, c'est surtout ça le problème) par les ingénieurs-geeks les plus confirmés :

avr-objcopy -j .text -j .data -O ihex main.elf main.hex

Il est vrai d'objcopy n'est jamais employé dans le génie logiciel, la manipulation des sections et des symboles étant laissés au bon vouloir d'un lieur dont on ignore souvent même l'existence. L'article en question qui traite de communication avec les périphériques série RS232 via un AVR est épatant : mais que l'on ne s'y trompe pas, cela vient clairement d'un électronicien.

Il y a un tas d'électronicien ayant parfois reçu une légère formation informatique qui sont impressionnant en terme de programmation système. Cependant, ils ont toujours cette dimension propre à la physique (la bidouille), mais manquent de celle caractéristique des mathématiques : l'algorithme. Aussi manier des masques de bits et gérer les interruptions n'a aucun secret pour eux. Mais j'ai déjà vu un collègue peiner deux jours sur un code parce qu'il ne connaissait pas malloc. Sans arriver à cet extrême, nous pouvons voir ceci sur la même page, qui ne manquera pas d'étonner :

while (1) {
  rxbuff=ReceiveByte();

  if (rxbuff) {
    TransmitByte(rxbuff);
    switch (rxbuff) {
      case '0':             set_pwm(0);        break;
      case '1':             set_pwm(111);      break;
      case '2':             set_pwm(222);      break;
      case '3':             set_pwm(333);      break;
      case '4':             set_pwm(444);      break;
      case '5':             set_pwm(555);      break;
      case '6':             set_pwm(666);      break;
      case '7':             set_pwm(777);      break;
      case '8':             set_pwm(888);      break;
      case '9':             set_pwm(999);      break;
    }
  }
}

On reste un peu pantois. Et pas seulement parce qu'il n'y a pas de default  :). On pourrait penser à mettre directement une multiplication par 111 :

if (rxbuff < 10)   // il est évidemment non signé
  set_pwm(rxbuff * 111);

Ca coûterait au maximum sept additions, et éviterait d'avoir jusqu'à neuf branch dans le code. Cette solution serait à comparer avec celle basée sur un tableau :

int16_t pwmcorr[10] = { 0, 111, 222, 333, 444, 555, 666, 777, 888, 999 };
// ...
if (rxbuff < 10)
  set_pwm(pwmcorr[rxbuff]);

Solution évitant la multiplication, mais faisant appel à un déplacement mémoire (et forcément à une addition) ; cependant, même avec une MMU, ça ne devrait pas être trop dramatique. Dans tous les cas, on est en droit de douter qu'un switch-case sur neuf niveau aussi stupide, même avec l'aide d'un compilateur intelligent (mieux vaut un switch-case qu'un tableau de pointeurs sur fonctions : dans le premier cas, le compilateur peut comprendre et optimiser, dans le second il ne peut rien voir venir ; et c'est sans compter le debug...).

Quelque part, pour l'informaticien embarqué qui a comme moi une formation première d'informaticien, c'est rassurant : alors qu'il entre dans un monde industriel où il a déjà la réputation d'électronicien raté, car c'est uniquement l'individu transfuge qui jusque là s'occupait de ce travail, il se sent souvent impuissant face à la couche à peine supérieure à l'électronique, celle de la tambouille très bas niveau, lui qui est à peine bien content d'avoir échappé aux weberies et autres presse-boutons. Il sait alors que sa valeur ajoutée sera l'informatique pure, le beau code qui fait plaisir à lire. Et que par exemple, Linux embarqué correspond parfaitement à ce genre de secteur et d'applicatifs où sa valeur ajoutée sera la plus appréciée.

jeudi 11 septembre 2008

tuons les wébeux et les résoteux

Oui, c'est de l'eugénisme, mais vous savez...

Pour les wébeux, le dernier exemple se trouve par-là. Regardez l'immonde user agent de Chrome :

Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13

Et pour les résoteux, voici un extrait de Makefile de SNMP :

INSTALL = $(LIBTOOL) --mode=install /usr/bin/install -c
UNINSTALL = $(LIBTOOL) --mode=uninstall rm -f
INSTALL_DATA = $(LIBTOOL) --mode=install ${INSTALL} -m 64

Non, vraiment, l'informatique se portera vraiment mieux sans tous ces gens...

lundi 1 septembre 2008

le itoa du bourrin

Cherchant tout à l'heure une implémentation rapide d'un "itoa", c'est-à-dire d'une fonction C transformant un entier en chaîne de caractère, entier en l'occurrence toujours positif (la précision est d'importance). Et là, je tombe sur celle que j'ai codée dans KonQueR, un grand moment (je réadapte un peu au passage) :

char *itoa(uint n)
{
  char  s[11] = {0,}; int i = sizeof(s) - 1;

  do {
    s[--i] = '0' + n % 10;
  } while ((n /= 10) > 0 && i != 0);
  return strdup(s + i);
}

En fait, le "11" était un "5", tout simplement parce que le chiffre en entrée ne dépasse pas 9999 (oui, c'est sioux, c'est pour ça aussi le "i != 0" dans le while, on ne sait jamais) ; avec 11 on peut arriver à 4294967295, soit MAX_INT, à réadapter si long long en entrée par exemple (il suffit donc de compter les chiffres de 2^64 - 1 en base 10, et d'ajouter 1 pour le \0). On peut réadapter pas trop difficilement à une autre base (il y a la vieille astuce du "0123456789abcdef" pour la base 16, etc), ou pour les nombres négatifs (tester et stocker "-1" ou "0", rendre positif, et à la fin le rajouter à s[i + sign] = '-' et strdup(s + i + sign) ).

Je me rappelle de mon premier itoa, ou plutôt du fait qu'il était long comme mon bras (long, pas large). Cinq lignes de code, c'est pas mal en revanche  :)  (je suis déjà tombé sur du code de crassouillé qui mettait l'équivalent du "s" en static et le retournait : mieux vaut faire des free que de se retrouver avec des bouts de chaînes qui se réécrasent de temps à autre !).

lundi 19 mai 2008

les merveilles de la libc

Trois extraits de man pour la route :

1. strfmon

L'appel
              strfmon(buf, sizeof(buf), "[%^=*#6n] [%=*#6i]",
                      1234.567, 1234.567);
       affiche
              [ fl **1234,57] [ NLG  **1 234,57]
       Dans la localisation hollandaise (fl étant "florijnen" et NLG "Netherlands Guilders").  Le caractère de groupement  est  horrible  car  il  prend
       autant  de place qu'un chiffre, alors qu'il ne devrait occuper qu'un demi-espace, ce qui peut induire en erreur.  Etonnament, le "fl" est précédé
       et suivi par un espace, et "NLG" est précédé par un espace et suivi par deux.  Il s'agit peut-être d'un bogue dans le  fichier  de  localisation.
       Les localisations italienne, australienne, suisse, et portugaise donnent
              [ L. **1235] [ ITL  **1.235]
              [ $**1234.57] [ AUD **1,234.57]
              [Fr. **1234,57] [CHF  **1.234,57]
              [ **1234$57Esc] [ **1.234$57PTE ]

2. strfry :

NOM
       strfry - Créer une anagramme d'une chaîne.

SYNOPSIS
       #include <string.h>

       char *strfry (char *string);

DESCRIPTION
       La  fonction   strfry()  crée  une anagramme du contenu de la chaîne string en utilisant la fonction rand(3) pour échanger aléatoirement des car-
       actères.

3. memfrob :

NOM
       memfrob - Crypter une zone de mémoire.

SYNOPSIS
       #include <string.h>

       void *memfrob (void *s, size_t n);

DESCRIPTION
       La  fonction  memfrob()  crypte  les  n  premiers octets de la zone de mémoire s en effectuant un OU-exclusif entre chaque octet et le nombre 42.
       L'effet inverse est obtenu en rappelant à nouveau memfrob() sur la zone cryptée.

       Notez que cette fonction n'est pas vraiment un cryptage, car la constante du OU exclusif est fixée. Elle est simplement  utile  pour  cacher  des
       chaînes de texte clair.

C'qu'on se marre. A noter qu'à chaque pauvre utilisation de dynamique de la libc, ces fonctions à la con sont aussi chargées en mémoire. edit: une fois suffit, qu'on en ait besoin ou pas, mais qu'une seule fois (cf commentaires) Tout à coup ça fait moins rire.

Puisque j'y suis, avez-vous vu ces illustrations hyper drôles à propos de la dernière faille abominable (ah, les mêmes dessins, ça a eu du succès :) ) sur openSSL sous Debian (&co, dont ubuntu) de ces derniers jours ?

jeudi 27 mars 2008

le single user sur openBSD (et ses facéties)

Pour un truc hyper sécurisé, des fois, ça me laisse songeur... Regardez le code par ici de init. BSD n'est pas SystemV, donc ça ne marche pas comme un Linux de base : il ne fait que lancer le script /etc/rc (codé en dur) ; il n'y a pas d'inittab. De fait, la présence de login via getty est déduite en fonction du fichier /etc/ttys, qui par défaut en met un sur tty00, la première console (on remarquera que getty est dans /usr/libexec, encore une des plaisanteries pas drôle de cet OS mal fichu ; à remarquer que c'est pareil sur MacOS, d'après ce que j'ai vu).

Sauf que si l'on regarde dans le code, la lecture de ce fichier se fait via la fonction "read_ttys()", qui elle-même appelle "start_session_db()" ; si cela échoue, c'est "single_user()" qui est appelé (par pointeur de fonction), sinon plus bas, c'est "multi_user()" (idem). multi_user, c'est le truc standard, avec un login/mot de passe demandé pour se logguer. Tout ceci se passe évidemment après avoir lu et entièrement exécuté le fichier /etc/rc (fonction "runcom()" ; à remarquer que c'est le même script qui est exécuté lorsque l'on arrête ou que l'on reboote la machine, pratique non ? Heureusement, un argument "shutdown" est passé dans ce cas, fonction "nice_death()"), tout ce qui doit être démarré pour avoir un système viable (un serveur apache, par exemple) est donc démarré. Notamment, on a depuis longtemps dépassé la fonction "main()" qui avec son while sur getopt en tête, vérifie que l'on n'a pas explicitement demandé un single mode dans le sens unixien/linuxien du terme, c'est-à-dire le fameux démarrage en mode dégradé directement sur shell (remarquons d'ailleurs que de plus en plus de distribution Linux semblent désactiver cette fonction du kernel par défaut : sinon avec un accès physique à un portable, il suffit d'éditer la ligne de conf avant démarrage, sous grub, et ajouter "single" à la liste des arguments, avant de changer le mot de passe root et redémarrer, par exemple) ; sous bsd, ça s'appelle en mettant un argument "-s" au noyau (stocké usuellement dans /bsd, soit à la racine, tout est simpliste dans ce monde).

Résumons : le single user est comme sous Linux, et tout aussi dangereux ; il n'est pas désactivable, sauf à compiler le kernel sans support de la ligne de commande (la gestion de la compilation de bsd est d'ailleurs des plus folkloriques ; à noter que la notion de modules n'est pas encore au goût du jour, et que désactiver le support d'une carte pcmcia dont on n'a que faire peut avoir des conséquences fâcheuses inattendues -- j'ai fini par la remettre, j'étais prévenu -- ah, si vous trouvez la faq austère, sachez qu'elle est en réalité fort complexe, si si, et que c'est pour ça qu'elle n'est pas aux standards w3c). C'est-à-dire que s'il est déclenché, on se loggue sans mot de passe en root, et l'on peut faire la fête sur le système. Parlons donc de cette fonction "start_session_db()"...

Il semble que bien des choses reposent sur db, la base de donnée antédiluvienne, notamment la gestion des mots de passe (pas de shadow mais un pwd.db). Et apparemment pour la gestion des ttys aussi. Or, db a besoin de son répertoire /var/db. Que se passe-t-il si le disque est plein, ou si /var est read-only ou du moins non inscriptible, par exemple parce que le file system de sa partoche (enfin, slice, je passe sur les détails...), est corrompu et automatiquement monté en ro ? (ce qui peut avoir des conséquences tragi-comiques : ayant un fichier sur lequel j'ai créé par virtual node -- le loopback du pauvre sur bsd -- un système bsd complet, et notamment une partition primaire bootable, le fait de vouloir la monter alors qu'un qemu fermé un peu trop vite m'avait un peu pourri le file system -- qui n'est pas journalisé, sous bsd --, a fait détecter au système que je voulais remonter une partition système avec bootloader et certainement noyau alors que celle-ci n'était pas nettoyé : refus catégorique et affichage de warning sur toutes les consoles ! Ou comment une mesure de sécurité légèrement pensé mène à du grand n'importe quoi) Ou encore que /var ou /var/db n'existe plus ? (imaginons qu'une coupure de courant fasse rebooter violement la machine, et que certains dossiers soient perdus ou fichier de création dynamique de répertoires corrompu)

Eh bien tout simplement, la fonction échoue, et on se retrouve, alors que login et getty peuvent se lancer tout à fait correctement, avec le prompt "Enter pathname of shell or RETURN for sh: ", ce qui en appuyant sur Entrée donne directement sur un shell root, sans passer par l'étape habituelle de la demande de mot de passe.

"Ah bein bravo", comme on dit...

(en l'occurrence, cette enquête a été mené parce que /var était par erreur un répertoire sur un file system / RO par mesure de sécurité, au lieu d'être un lien vers /tmp/var, lui-même monté en RAMdisk, ou "mfs" sous bsd -- d'ailleurs le "mount_mfs" reste un processus lancé, c'est-à-dire en toute rigueur une gestion en userland via allez-retour kernel/processus pour de la mémoire que l'on veut la plus rapide possible -- contrairement à un FUSE linux qui de toute façon a toujours été un gros hack, bien inférieur à ce que ferait un hurd --, et en plus non déboulonnable ensuite, du moins un kill -9 n'y fait rien... Magnifique design, pour la peine)

vendredi 4 janvier 2008

C++ bashing

Pour bien terminer la semaine, un peu de C++ bashing. Ce langage est décidément une belle merde. Souvenez-vous il y a deux ans, je découvrais que le bidule ne savait pas faire la différence au niveau de la surcharge de méthodes entre une signature avec bool et une autre avec int. C'était là. Un grand moment.

Il semble que depuis l'accent (du moins dans g++) ait été mis sur le typage qui était trop laxiste. Par exemple, les enum, sur lesquels il est impossible de faire la moindre opération (même pas d'incrémentation, de telle sorte que leur intérêt retrouvé est tout à coup grandement reréduit). Mais aussi sur les fameux appels de méthode surchargées...

Voyons ce que me dit cette quadruple buse :

erreur: call of overloaded ‘get_data(int, int&)’ is ambiguous
 note: candidats sont: void* values_pool::get_data(long unsigned int, short unsigned int)
 note:                 void* values_pool::get_data(const char*, short unsigned int)

Il n'arrive pas à faire la différence entre un const char * et un long unsigned int. Pas de la blague. Ouais ouais. Même pas peur. Mais que lui ai-je donc dit pour attirer ainsi ses foudres, et son désarrois ?

pool->get_data(0ul, i);

Ouais, 0, il sait pas ce que c'est. Avec 0u, il trouve que c'est unsigned, mais pas assez précis, il hésite toujours (il le prend pour un int, mais est-ce un long int, où un char* ? Mais qu'il est con !). Avec 0l, il trouve que c'est un long, mais il hésite encore (pas sûr que ça soit non signé... ça pourrait donc être un pointeur, logique non ?). Avec 0ul, ça passe enfin. Hosanna !

Mais vous savez pas le plus drôle ? Ce truc-là ne fait aucune erreur, et passe par le bon tuyau :

pool->get_data(42, s_1);

Bah ouais, c'est fort, seul 0 n'est pas typé ! Mais apparemment pas du tout du tout. Je suppute une hypothèse hautement probable à cette faille spacio-temporelle : comme des joyeux codeurs descendus plus du cochon que du singe n'utilisent toujours pas NULL, ils mettent 0, en dur (et grouuuuiiikk !). Faudrait pas faire de warning, nan nan ! (warning: you piggy !) Donc on fait chier les bons codeurs, avec des merdes pareilles, où l'on en est réduit à précis des "0ul" à la place de "0" tout court. Foooormidable !

(et pour info, il existe d'anciennes archis où le pointeur NULL ne vaut pas 0...)

jeudi 13 décembre 2007

Does someone understand code flow here?

        /*
         * This is subtle. Instead of calling do_follow_link() we do the
         * thing by hands. The reason is that this way we have zero link_count
         * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.
         * After that we have the parent and last component, i.e.
         * we are in the same situation as after the first path_walk().
         * Well, almost - if the last component is normal we get its copy
         * stored in nd->last.name and we will have to putname() it when we
         * are done. Procfs-like symlinks just set LAST_BIND.
         */
        nd->flags |= LOOKUP_PARENT;
        error = security_inode_follow_link(path.dentry, nd);
        if (error)
                goto exit_dput;
        error = __do_follow_link(&path, nd);
        if (error) {
                /* Does someone understand code flow here? Or it is only
                 * me so stupid? Anathema to whoever designed this non-sense
                 * with "intent.open".
                 */
                release_open_intent(nd);
                return error;
        }

J'ai failli m'étouffer. Je crois qu'il est temps de se diriger calmement vers la salle Pleyel (c'est dans le kernel Linux, pour la 2.6.23.9, lignes 1795 à 1817 de fs/namei.c).

dimanche 2 décembre 2007

pour rompre avec le laisser-aller

Ça manque d'informatique, dans le coin, vous ne trouvez pas ? :) Comme j'ai pris le soin de cultiver artistiquement les lecteurs geeks ces temps-ci, il est temps de cultiver geekement les amateurs d'art... Enfin, ça c'est la théorie, dans la pratique, je ne suis pas dupe non plus ;).

Commençons par des choses intéressantes : d'abord les classiques attributs en C pour gcc, que ceux qui connaissent trouvent banal (mes étudiants qui m'ont foutu la honte, y'avait une imprécision erratique sur mes slides >_< ), mais que bien des ingénieurs (et des chefs de projet) ne connaissent pas du tout (c'est la lose...). J'harangue tous mes lecteurs développeurs à apprendre aux moins les principaux attribute, qui servent tous les jours ou presque :

    * "format" indique le style de format, on pourra trouver une utilisation dans KonQueR, où se trouve une fonction glprintf, qui marche comme printf mais en sur l'espace 3D (pour les menus par exemple), et qui nécessite donc plus de paramètres. C'est pourquoi le premier argument indique "à la printf" (ou autre, mais c'est le plus pratique), le second à partir de quel argument l'on doit considérer le même type de format, qui s'arrête au troisième précisé. Ça permet d'avoir des warnings quand on va faire une bêtise au niveau de la correspondance avec les %s, %i, etc. Super pratique.

extern int   my_printf (void *my_object, const char *my_format, ...)
__attribute__ ((format (printf, 2, 3)));

    * Mon préféré : packed. Il permet de minimiser l'espace mémoire requis, c'est-à-dire qu'il évite l'alignement (je n'ai jamais vu d'alignement sur 4 octets, mais cela dépend entièrement de l'architecture, et donc on peut en imaginer n'importe quel autre, mais plutôt dans les puissances de 2 a priori :) ). De base, lorsque l'on déclare une structure ou une union, le compilateur aligne effectivement les données de manière en optimiser l'accès en mémoire. Par exemple, sur x86, powerQUICC ou ARM, la structure suivante serait sur 8 octets et non 4 sans le "packing".
struct my_packed_struct __attribute__ ((__packed__))
{
char c;
int i;
};
L'avantage ensuite est de pouvoir manier ça avec une union :
union
{
struct my_packed_struct;
char table[5];
};
Ensuite, on peut faire des trucs funs comme ça. Perso, j'utilise pour envoyer et recevoir sur le réseau, sans avoir à faire de casts atroces, et avec plus de flexibilité (ne pas forcément envoyer dans l'ordre ou en une seule fois, etc). Très pratique en fait pour matcher de la mémoire élégamment et remplir ses structures.

    * Dans un genre similaire, l'attribut "aligned" permet de changer l'alignement, de le forcer sur une autre valeur ; moins utile, il faut bien avouer.

    * Les autres à connaître (mais largement moins utilisés) : deprecated, unused, pure (attention ; moi j'aime po), ...

Ensuite, les trucs et astuces dans les instanciations (ouais, vocabulaire d'informaticien : initialisation, quoi) en C : designated inits. Cette fois-ci, c'est du standard, contrairement aux attributes et autres pragma ; en revanche, la compatibilité C/C++ n'est pas forcément de mise (le C++ n'est pas le C, de toute façon, on a beau le répéter, on trouve toujours de ces horreurs...). On connaît déjà bien en théorie les désignations par nom d'attributs d'une structure :

union foo { int i; double d; };
union foo f = { .d = 4 };
Mais ce qui est moins connu et qui poutre des mamans ours en slip, c'est l'initialisation des tableaux. Exemple :
int a[6] = { [4] = 29, [2] = 15, v3 };
Ce qui nous donnera { 0, 0, 15, v3, 29, 0 } en deux coups de cuillère à pot. Déjà, rien qu'avec ça, on est assez ébahi : ça commence à ressembler à de l'Ada :p. Mais gcc implémente un truc complètement plus puissant (vraiment comme Ada) :
int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };
Ce qui troue l'anus. Fini les memset ou les horribles boucles for à chaud, qui initialisent au lancement de l'application en bouffant du CPU et du temps, alors que sacrédieu, on avait un tableau prédéfini à mettre dans le BSS (et parfois, quand on voulait absolument le mettre dans le rodata à coup de const, bien statique et intouchable, l'on voyait des gens s'échiner à écrire des milliers de valeurs dans un .h, et à la main, hein, pas avec un script tant qu'à faire :D ; pire encore, les "static" pour que tout soit garanti à 0 par défaut, oui oui, j'ai vu ça aussi...). Et puis, ce que je trouve super beau, c'est le mix des deux méthodes structure/tableau :
struct point ptarray[10] = { [2].y = yv2, [2].x = xv2, [0].x = xv0 };
Côté élégance, on retiendra ce qui suit, et qui peut marcher avec des enum :
int whitespace[256]
= { [' '] = 1, ['\t'] = 1, ['\h'] = 1,
['\f'] = 1, ['\n'] = 1, ['\r'] = 1 };

Puisqu'on est en pure geekerie, j'indique ces deux pages web super intéressantes du même site : le mécanisme d'appel système sous Linux, et les vecteurs auxilières du format ELF, c'est court et très bon :).

- page 1 de 5