Aller au menu - Aller au contenu
Inscris-toi au e-camp "Héberge ton jeu Facebook sur Azure" de Microsoft vendredi 25 mai à 13h30 !

FAQ C

Foire Aux Questions - la votre y est peut-être.

Pour accéder à cette section
Connectez-vous !
connexion_rpx
Page Précédente  1  2  3  4  5 
Auteur Message
3 visiteurs sur ce sujet (3 anonymes)
Page Précédente  1  2  3  4  5 
Hors ligne Lucas-84 # Posté le 29/01/2012 à 18:33:02
[Tomber en marche]
Avatar
Validateurs
Flux RSS

Ville : Uchaux
Pays : France métropolitaine

Reprise du dernier message de la page précédente :
Citation : candide
Citation : informaticienzero
une valeur de type void*, ce qui fait qu'elle peut être convertie implicitement dans n'importe quel type :


Pas en type pointeur vers fonction me semble-t-il.


Secret (cliquez pour afficher)
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

#define PRINT(p) printf("@%s {%p}\n", __FUNCTION__, (p))

void f(void) {
  PRINT((void*)f);
}

int main(void) {
  void *g = f;
  /* Conversion implicite {void -> void(*)(void)} */
  void (*p)(void) = g;

  PRINT((void*)p);
  p();

  return 0;
}


Code : Console
$ ./a.out
@main {0x80483f0}
@f {0x80483f0}


Est-ce indéfini par la norme ? (désolé pour le flood sur le topic, mais je ne savais pas trop où réagir)
 
Publicité # Posté le 29/01/2012 à 18:33:02

Hors ligne Taurre # Posté le 29/01/2012 à 19:16:19
Avatar

Études : Université de Liege

Citation : lucas-84

Est-ce indéfini par la norme ? (désolé pour le flood sur le topic, mais je ne savais pas trop où réagir)


Oui, la norme spécifie seulement le comportement pour une conversion d'un pointeur de fonction vers un autre, pas pour la conversion d'un pointeur de fonction vers un pointeur sur objet:

Citation : Norme C11 6.3.2.3 § 8 p 56

A pointer to a function of one type may be converted to a pointer to a function of another
type and back again; the result shall compare equal to the original pointer. If a converted
pointer is used to call a function whose type is not compatible with the referenced type,
the behavior is undefined.


Cependant, cette conversion est supportée par pas mal de compilateur (cf Annexe J.5.7) ;)

Pour une coloration syntaxique du C à jour :pirate:

Secret (cliquez pour afficher)

Citation : Brian W. Kernighan & Dennis M. Ritchie

In our experience, C has proven to be a pleasant, expressive, and versatile language for a wide variety of programs. It is easy to learn, and it wears well as one's experience with it grows.

 
Hors ligne Two Jay # Posté le 14/02/2012 à 07:53:12 Message supprimé pour le motif suivant : Merci de faire un nouveau topic.
Hors ligne @che # Posté le 14/02/2012 à 10:14:56 Message supprimé pour le motif suivant : Réponse au post précédent.
Hors ligne sadiq2008 # Posté le 09/03/2012 à 02:06:34 Message supprimé pour le motif suivant : C'est gentil de remercier, mais on laisse ce topic uniquement pour les questions fréquentes. :) .
Hors ligne Maëlan # Posté le 15/04/2012 à 13:18:53
Avatar

[8][12] Que signifie « ++i » ? Quelle différence avec « i++ » ?


Saviez-vous qu’il existe en fait deux opérateurs d’incrémentation (++), et autant pour la décrémentation (--) ? On parle d’incrémentation ou de décrémentation postfixée (n++, n--) ou préfixée (++n, --n). La version préfixée ressemble beaucoup à la postfixée, mis à part que l’opérateur se place avant son opérande.

Quelle est la différence ? Les opérateurs préfixés modifient la variable, puis renvoient sa nouvelle valeur, alors que les postfixés renvoient la valeur de la variable avant de la modifier.
Exemple :Code : C
1
2
3
4
5
int  a=5,  b=5;
printf("%d\n", ++a);    // affiche 6
// a vaut maintenant 6
printf("%d\n", b++);    // affiche 5
// b vaut maintenant 6


L’intérêt de tout ça ? L’opérateur préfixé est en principe plus rapide que le postfixé. En effet, le postfixé impose de créer une copie temporaire de la variable pour renvoyer sa valeur avant modification. Vous pouvez donc optimiser vos programmes en utilisant les versions préfixées partout ou vous n’avez pas besoin de l’ancienne valeur, par exemple dans vos boucles : for(i=0; i<n; ++i).

Cependant, en pratique, votre compilateur favori est sans doute capable de faire cette optimisation tout seul (c’est le cas de GCC et de Clang entre autres). Voyant que vous ne récupérez pas la valeur de retour de l'expression (i++), il sait qu’il peut la remplacer par (++i).

En C++, c’est différent. En effet, dans le cas d’un objet, les opérateurs ++ et -- sont en fait des appels de fonctions maquillés par la surcharge des opérateurs, et le compilateur ne peut donc pas optimiser (car il ne sait pas ce qui se cache derrière, contrairement au C).
Utiliser les opérateurs adaptés est donc une bonne habitude à prendre. D’autant plus qu’en C++, le gain de performance peut être significatif car l’objet est peut-être très lourd à copier.
Pour plus d’informations à ce sujet, allez voir ici.
Édité le 17/04/2012 à 22:25:30 par Maëlan
Hors ligne Maëlan # Posté le 16/04/2012 à 21:10:30
Avatar


[3][9] Comment passer d’un caractère de ‘0’ à ‘9’ au nombre correspondant (et vice-versa) ?


Comme vous devez le savoir, les caractères (stockés dans le type char) sont en fait représentés par des nombres. Les valeurs varient selon votre système (compilateur et machine) mais c’est souvent celles de l’ASCII.

Parmi les caractères disponibles, il y a notamment les chiffres de zéro à neuf : '0', '1', '2',, '9'. Cependant, leur valeur n’est pas 0, 1, 2, …, 9 (en ASCII, '0' vaut 48).
Heureusement, il existe une astuce simple pour passer de l’un à l’autre :

Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
char c;
int i;

/* pour passer du caractère au nombre */
c = '6';          // c vaut maintenant '6' (soit 54 en ASCII)
i = c - '0';      // i vaut maintenant 6, et non 54 (si ASCII)

/* pour passer du nombre au caractère */
i = 7;            // i vaut maintenant 7
c = i + '0';      // c vaut maintenant '7' (soit 55 en ASCII), et non le caractère spécial de valeur 7 (si ASCII)

Écrire simplement « c = i » (ou « i = c » dans l’autre sens) n’aurait pas marché, car dans le 1er cas i aurait valu 54 au lieu de 6, et dans le 2nd cas c aurait valu 7, ce qui correspond en ASCII à un caractère spécial.

Cette astuce se base sur le fait que les caractères chiffres ('0', '1', '2', '3'…) ont des valeurs consécutives ; la 1ère valeur est celle du chiffre zéro, soit '0' dans le cas général.
Il suffit donc d’une simple opéraiton de décalage (rajouter ou soustraire la valeur '0') pour passer d’un nombre de 0 à 9 au caractère chiffre correspondant, et vice-versa.


Secret (cliquez pour afficher)
NB : Si jamais un évêque à plume du forum C (ou n’importe qui de façon générale, n’attendez pas les plumes) vient vous sermonner que cette solution n’est pas portable, vous lui mettrez sous le bec cet extrait de la norme : ;)

Citation : norme C99 (draft n1256) − 5.2.1, §3
Both the basic source and basic execution character sets shall have the following members:
[…]
  • the 10 decimal digits 0 1 2 3 4 5 6 7 8 9
[…]
[…] In both the source and execution basic character sets, the value of each character after 0
in the above list of decimal digits shall be one greater than the value of the previous. […]


Cette astuce ne marche pas si vous voulez dépasser 9 (eh oui, les chiffres arabes s’arrêtent à 9). Elle ne marche pas non plus si vous espérez convertir les lettres ('A', 'B', 'C', …) en nombres selon le système hexadécimal (où la lettre A signifie 10, B signifie 11, C 12…). En effet, dans le jeu de caractères du système, il n’est pas garanti que les lettres soient consécutives aux chiffres, ni même qu’elles soient « rangées » dans l’ordre et de façon croissante. Enfin, il faut distinguer les minuscules des majuscules (ce sont deux caractères différents).
Dans la plupart des jeux (dont l’ASCII), les lettres latines sont bien consécutives et triées ; par conséquent, sur ces systèmes, il est possible de recourir à une astuce similaire pour passer de la lettre au nombre :Code : C
1
2
3
4
5
6
if(c>='0' && c<='9')    // Si c est un chiffre de 0 à 9   [PORTABLE]
    i = c - '0';
if(c>='A' && c<='Z')    // Si c est une lettre majuscule  [NON PORTABLE]
    i = c - 'A' + 10;
if(c>='a' && c<='z')    // Si c est une lettre minuscule  [NON PORTABLE]
    i = c - 'a' + 10;
Le +10 servant à donner la valeur 10 et non 0 à la lettre A. Toutefois, cette solution n’est pas portable. :'(

Hors ligne Maëlan # Posté le 17/04/2012 à 21:48:49
Avatar

[8][13] Que signifient les deux-points « : » à coté d’un membre de structure ?
co-rédigé par Taurre et Maëlan



Code : C
1
2
3
4
5
6
7
8
struct Exemple {
    int  a  :2,
         b  :3,
         c  :1,
         d  :1,
         e  :1;
    char f;
};


Peut-être avez-vous déjà rencontré des codes qui ressemblent à ça.

Les deux-points à côté d’un membre d'une structure (ou d’une union) signifient que ce dernier est un « champ de bits » (“bit-field” en anglais). Cela permet de spécifier, pour un champ de type int , unsigned int ou _Bool (C99), le nombre de bits que l’on souhaite utiliser (ce nombre ne doit bien évidemment pas excéder le nombre de bits composant le type).

Cette technique peut-être très utile afin de gagner de la place quand vous savez que vous n’utiliserez jamais toute la capacité du type int, unsigned int ou _Bool : dans un seul int « normal », vous faites tenir plusieurs membres de ce type.

Pour faire cela, il vous faut être sûr des valeurs que pourront prendre vos membres. Cette technique restreint en effet les valeurs disponibles. Dans un champ de m bits, vous pouvez écrire 2m valeurs différentes (de 0 à 2m-1 dans le cas d’un nombre non signé).
Ainsi, vous pouvez par exemple faire des booléens ou des flags qui ne prennent que les valeurs 0 ou 1 et n’ont donc besoin que d’un bit, tout ça sans utiliser d’opérations bit-à-bit (c’est le compilateur qui s’en charge pour vous). Par exemple :Code : C
 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
/** Structure contenant 8 booléens **/
struct Booleans {
    _Bool  b1  :1,    /* (le type _Bool est apparu en C99) */
           b2  :1,
           b3  :1,    /* Ces membres n’utilisent qu’un bit et ne peuvent */
           b4  :1,    /*  donc valoir que 0 ou 1. */
           b5  :1,
           b6  :1,
           b7  :1,
           b8  :1;
};

/** Exemple d’utilisation **/
struct Booleans  myBools =  {1, 1, 0, 1, 0, 1, 1, 0};
myBools.b3 =  1;
myBools.b7 =  myBools.b2;
if(myBools.b1)    {}
          /* ⇒ On s’en sert comme des membres normaux. */

/*/ Taille totale de la structure **/
printf("sizeof Booleans: %u\n", sizeof(struct Booleans) );
    // ↑ probablement 1
printf("sizeof _Bool[8]: %u\n", sizeof(_Bool[8]) );
    // ↑ probalement 8
          /* ⇒ Si on n’avait pas eu recours aux champs de bits pour stocker
            8 booléens (un tableau par exemple), on aurait utilisé plus de
            place. */


Il est possible de spécifier un champ de bit sans nom, par exemple :Code : C
1
2
3
4
5
struct StructureAvecBourrage {
    int  a  :3,
            :2,    /* champ anonyme de 2 bits */
         b  :3;
};

Les champs anonymes ne peuvent évidemment pas être utilisés. Ils permettent de régler l’alignement des autres champs en laissant de l’espace précis entre eux.
Dernière possibilité, on peut spécifier un champ anonyme de longueur 0. Cela indique au compilateur de remplir le reste de l’unité de stockage et de faire commencer le champ suivant à la prochaine unité. Par exemple :Code : C
1
2
3
4
5
6
struct StructureAvecAlignement {
    int  a  :3,
         b  :2,
            :0,    /* champ anonyme s’étendant jusqu’à la fin du « int » conteneur */
         c  :3;    /* champ commençant au « int » suivant */
};



Point norme :
  • Vous ne pouvez faire ceci que pour des membres de structure ou union.
  • Cela se limite aux types entiers int, signed int, unsigned int ou _Bool (depuis C99). Les champs de bits sont du même type que leur « conteneur ». Dans le cas de int, le fait que les champs de bits soient signés ou non est au choix de l’implémentation.
  • La norme n’impose pas d’ordre pour les champs de bits (des bits de poids forts vers les bits de poids faibles ou le contraire) : c’est à l’implémentation de trancher.
    Secret (cliquez pour afficher)
    Attention donc si vous tentez de gérer un ensemble de flags avec une union de cette façon :Code : C
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    typedef union {
        uint8_t flags;        /* ensemble de 8 flags */
        struct {              /* accès individuel aux divers flags */
            _Bool  hasBuffer    :1,
                   usesTruck    :1,
                   hasDrunk     :1,
                   knowsTruth   :1,
                   hasReadDraft :1,
                   hasSeenWolf  :1,
                   canFly       :1,
                   isLastMohic  :1;
        } flag;
    } Flags;
    

    Pour les mêmes valeurs de flags, l’entier les englobant tous (le membre flags) pourra avoir une valeur différente d’un système à l’autre, selon l’ordre dans lequel sont rangés les champs de bits.

  • S’il ne reste plus assez de place dans l’unité de stockage pour le prochain champ de bits, celui-ci peut être décalé pour commencer avec l’unité suivante (donc ajout de bits inutilisés), ou à cheval sur les deux unités (ce qui diminue les performances) ; à l’implémentation de décider.

Conclusion : les champs de bits sont sympathiques et parfois bien pratiques, mais si vous visez la portabilité, mieux vaut les oublier. :( Si vous avez vraiment besoin de condenser vos données, utilisez manuellement les opérations binaires (&, |, ^).
Édité le 18/04/2012 à 22:45:31 par Maëlan
Hors ligne Maëlan # Posté le 18/04/2012 à 22:21:59
Avatar

[8][14] Quels sont les opérateurs du langage C ? Comment se combinent-ils ?
co-rédigé par Taurre et Maëlan, co-corrigé par candide et rz0 (merci à eux)





Un opérateur spécifie une opération à appliquer à des éléments qu’on appelle ses opérandes. Cette opération peut produire une valeur calculée, avoir un « effet de bord » (une affectation par exemple) ou les deux.
Par exemples :Code : C
1
2
3
3 * 5;    /* renvoie la valeur 15 */
i++;      /* renvoie la valeur de i, et augmente de 1 la valeur
             de cette variable (→ effet de bord) */

3 et 5 sont les opérandes de la première opération, i est l’unique opérande de la seconde. Ces exemples montrent au passage que tous les opérateurs n’ont pas le même nombre d’opérandes.

Contrairement aux fonctions de la bibliothèque standard, les opérateurs sont des éléments du langage. Ils constituent, avec leurs opérandes, les composants élémentaires d’une expression.


Mais, le C compte combien d’opérateurs alors ? o_O



Hé bien, le langage C intègre de nombreux opérateurs : 48 en tout ! Certains sont plus fréquents que d'autres, mais tous ont leur utilité. Le tutoriel officiel de M@teo21, comme de nombreux cours pour débutants, ne les présente pas tous car ils ne sont pas utiles pour commencer.

Ci-dessous, un grand tableau vous les représente tous. Le but n’est pas de tous les expliquer ici, mais d’en offrir une vue d’ensemble et de rappeler leur caractéristiques : arité, priorité et associativité.

Rhôô, les gros mots ! Ça veut dire quoi tout ça ?

On va tout vous expliquer :
  • On appelle arité le nombre d’opérandes d’un opérateur. En C, elle varie de 1 à 3.
    1. On dit qu’un opérateur est « unaire » s'il n’a qu’un seul opérande. Par exemple, ++ (comme dans A++) et sizeof (comme dans sizeof A) sont des opérateurs unaires.
    2. De même, les opérateurs ayant deux opérandes sont dits « binaires » (rien à voir avec la base 2),
    3. et ceux en ayant trois « ternaires » (il n’en existe qu’un en C, l’opérateur ? : qui s’utilise ainsi : A?B:C et qui est simplement appelé opérateur ternaire).

     
  • La priorité des opérateurs permet de déterminer l’ordre dans lequel les opérations composant une expression seront combinées.
    Dans une expression comportant plusieurs opérations, celles-ci sont emboîtées de telle sorte qu’une opération serve d’opérande à une autre. Ça ne se fait pas n’importe comment, c’est la priorité qui impose la façon de lire : une opération prioritaire servira d’opérande à une opération qui l’est moins (la prioritaire est donc calculée avant).

    Prenons un exemple, l’expression 5 && 0 + 3. On pourrait calculer sa valeur de deux manières :

    — effectuer le ET logique puis l’addition : ( (5 && 0) + 3 ) ;
    — effectuer l’addition puis le ET logique : ( 5 && (0 + 3) ).

    L’ordre des opérations est extrêmement important puisqu’il va déterminer la valeur de l’expression. Dans le premier cas on obtiendra 3, et dans l’autre on obtiendra 1. Grâce à la priorité, nous savons que l’addition doit s’effectuer avant le ET (l’addition est prioritaire sur le ET) et que la valeur de l’expression sera donc de 1.

     
  • Malheureusement, la priorité des opérateurs ne suffit pas à lever toute ambigüité. En effet, que faire dans les cas où les opérateurs ont la même priorité ? Par exemple, dans l’expression 6 / 2 * 3, qui de la division ou de la multiplication doit être réalisée la première (sachant qu’elles ont la même priorité) ?

    — Si l’on fait la division d’abord : ( (6 / 2) * 3 ) nous donne 9.
    — Si l’on fait la multiplication d’abord : ( 6 / (2 * 3) ) nous donne 1.

    C'est ici que l’associativité intervient, en déterminant un sens de lecture. Pour les opérateurs * et /, l’associativité est de gauche à droite, donc on lit l’expression dans ce sens ; on rentre dans le premier cas et on obtient 9. L’expression pourrait être reformulée comme suit : (6 / 2) * 3.

    Les deux associativités possibles sont évidemment de la gauche vers la droite (left to right, ltr) et de la droite vers la gauche (right to left, rtl).


Pour contourner les règles de priorité et d’associativité (si l’on veut combiner les opérations différemment), on met des parenthèses.

Trêve de bavardages, voici enfin le tableau ! Il est organisé de telle sorte qu’un opérateur prioritaire sur un autre sera placé plus haut que ce dernier ; deux opérateurs de même priorité seront sur la même ligne.

Les opérateurs du langage C : priorité, associativité et arité
Catégories d'opérateurs Opérateurs Assoc. Arité
appel de fonction, indiçage,
membre de structure,
membre de structure pointée
( )   [ ]   .   ->   --1   ++1 ltr → 2 (1
pour ++
et --)
opérateurs unaires (Type) sizeof _Alignof2 * & ! ~ - + --1 ++1 rtl 1
multiplication, division, modulo *         /         % ltr → 2
addition, soustraction +            -
décalage binaire <<            >>
comparaison <      <=      >=      >
==            !=
opérations binaires &
^
|
opérations logiques &&
||
opérateur conditionnel ? : rtl 3
affectation =  +=  -=  *=  /=  %=  <<=  >>=  &=  ^=  |= 2
virgule , 3 ltr →


Remarques :
1 Il existe en fait deux opérateurs d’incrémentation et deux de décrémentation : les opérateurs postfixés (i--, i++) mais aussi les opérateurs préfixés (--i, ++i) qui sont moins connus des débutants (voir l’entrée [8][12] de cette FAQ). Les postfixés sont prioritaires sur les préfixés et ont une associativité contraire.
2 L’opérateur _Alignof a été introduit en C11.
3 L’opérateur virgule (,) est très méconnu des débutants.

Cette page très complète du Wikipedia anglais apporte plus de détail. Elle présente tous les opérateurs du C et du C++, en indiquant s’ils sont surchargeables et comment, ainsi que leur priorité et leur associativité.



Pour finir, voici deux exemples d’expressions afin de mettre en pratique ce que vous venez d’apprendre. Pour chacune d’entres elles, nous allons déterminer l’ordre dans lequel le calcul des valeurs sera effectué.

Code : C
1
*(char*)p + 5 * 3

Les opérateurs prioritaires sont * et (char*). Ces derniers ont la même priorité, il est donc nécessaire de recourir à leur associativité. Cette dernière étant de droite à gauche, c’est la conversion en char* qui sera effectuée la première. Enfin, il reste une addition et une multiplication et comme vous le savez, c’est la multiplication qui est prioritaire. Au final, l’expression peut-être réécrite comme suit :Code : C
1
( *( (char *)p ) )  +  (5 * 3)
.

Code : C
2
t[i].x++

Les opérateurs [], . et ++ ont tous la même priorité, il va donc falloir recourir à leur associativité. Cette dernière étant de gauche à droite, l’expression peut s’écrire :Code : C
2
( (t[i]).x )++
.


Ultime remarque :
Secret (cliquez pour afficher)
Certains opérateurs s’écrivent de façon identique (même symbole) mais se différencient par leur arité (1 ou 2). Il y en a 4 en tout, en voici la liste :
Symbole Opérateur unaire Opérateur binaire
Usage Description Usage Description
+ +A signe : détermine le signe de l’expression A A + B addition ou soustraction : retourne la somme ou la différence de A et B
-A A - B
* *A indirection : retourne la valeur pointée par A A * B multiplication : retourne le produit de A et B
& &A référence : retourne l’adresse de la variable A A & B ET binaire (opérateur bit-à-bit) : retourne l’entier tel que chaque bit
est à 1 si et seulement si le bit correspondant de chaque opérande est à 1
Édité le 26/04/2012 à 19:34:31 par Maëlan
Hors ligne Taurre # Posté le 21/05/2012 à 12:38:42
Avatar

Études : Université de Liege

[1][8] Comment peut-on lire un caractère sans appuyez sur Enter ?

La solution dépend de votre système d'exploitation :

- sous Windows, il est possible d'utiliser la fonction getch (déclarée dans l'en-tête conio.h);

- sous Unixoïde (GNU/Linux, BSD, Mac OS X, Solaris, ...), il est nécessaire de modifier les attributs du terminal (à l'aide des fonctions de l'en-tête termios.h), afin d'utiliser le mode non canonique.

Voici une petite fonction identique à getchar, mais qui lit un caractère sans attendre que l'utilisateur appuye sur Enter :

Secret (cliquez pour afficher)

Code : C
 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
36
37
38
39
40
41
42
43
44
#ifdef __unix
#	define _XOPEN_SOURCE 600
#	include <termios.h>
#	include <unistd.h>
#else
#	include <conio.h>
#endif

#include <stdio.h>


int
instant_getchar(void)
{
	int c;
#ifdef __unix
	struct termios term, tmp;

	if (tcgetattr(STDIN_FILENO, &term) < 0)
		return EOF;

	tmp = term;
	tmp.c_lflag &= ~ICANON;
	tmp.c_cc[VMIN] = 1;	
	tmp.c_cc[VTIME] = 0;

	if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tmp) < 0)
		return EOF;

	c = getchar();
	tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
#else
	c = getch();
#endif
	return c;
}


int
main(void)
{
	instant_getchar();
	return 0;
}

Édité le 21/05/2012 à 12:39:20 par Taurre

Pour une coloration syntaxique du C à jour :pirate:

Secret (cliquez pour afficher)

Citation : Brian W. Kernighan & Dennis M. Ritchie

In our experience, C has proven to be a pleasant, expressive, and versatile language for a wide variety of programs. It is easy to learn, and it wears well as one's experience with it grows.

 

Retour au forum "Langage C" ou à la liste des forums

Pour accéder à cette section
Connectez-vous !
connexion_rpx