Bien programmer en langage C
Vous souhaitez réagir à ce message ? Créez un compte en quelques clics ou connectez-vous pour continuer.
-29%
Le deal à ne pas rater :
PC portable – MEDION 15,6″ FHD Intel i7 – 16 Go / 512Go (CDAV : ...
499.99 € 699.99 €
Voir le deal

Questions souvent posées (FAQ)

Aller en bas

Questions souvent posées (FAQ) Empty Questions souvent posées (FAQ)

Message  -ed- Lun 26 Mai 2008 - 12:31

Je cherche une directive du préprocesseur me permettant de détecter l'option -std=c99 dans les paramètres de compilation en clair je veux détecter si on code en c99 ou autre.

Je conseille :

Code:

#ifdef __STDC_VERSION__
# if __STDC_VERSION__ == 199409L
# define C94 1
# define C99 0
# elif __STDC_VERSION__ == 199901L
# define C99 1
# define C94 0
# endif
#else
# define C94 0
# define C99 0
#endif

A placer dans un header genre "cver.h" ou "c99.h"

Ensuite, on utilise la macro C99 selon les besoin avec #if :

Code:

#if C99
/* ...*/
#else
/* ...*/
#endif

-ed-, said78vf




Je me pose la question de l'utilité de 'int argc' au lieu de 'size_t argc' dans les paramètres formels de la fonction main, d'ailleurs gcc émet un warning si on met size_t.

La norme dit int. C'est historique. Lorsque le C a été crée dans les années 70, size_t n'existait pas. C'est une création de la normalisation en 89/90. (comme void ...)

Il était malheureusement trop tard pour revenir en arrière et int est resté.

Je suis d'accord que size_t aurait, en théorie, été plus approprié. Mais dans la pratique, je vois mal un appel de programme avec plus de 32767 paramètres !

Quelle est la différence entre int et size_t ?

size_t est le type retourné par l'opérateur sizeof. C'est un entier non signé, à l'évidence suffisamment grand pour recevoir la plus grand valeur que peut retourner sizeof.

Sachant que la taille est donnée en bytes, ça donne une indication. Il faut savoir qu'en C90, la taille maximale d'un objet ne peut être inférieure à 32767. Cette valeur est de 65535 en C99. Evidemment selon l'implémentation, ça peut être beaucoup plus. En C99, c'est donné par SIZE_MAX. Ca ne peut évidemment pas être supérieur à (size_t) -1.

Détails ici




J'aimerais savoir comment on fait pour lire la dernière case d'un tableau uni-dimensionnel dont la longueur est indéterminée ?

Si la taille est réellement indéterminée, on ne peut rien faire. Heureusement, ça n'arrive pas, sauf si on a oublié de

  • marquer la fin (par exemple : chaines)
  • noter la taille (autres tableaux).

Le dernier caractère d'une chaine se situe à chaine[strlen(chaine) - 1]

Est-t-il possible de remplacer ce dernier caractère par un autre ?

Par exemple :
Code:

si (dernier caractère = !)
{
  dernier caractère = ?;
}

Du moment que la chaine est modifiable, oui.

Et concrètement comment on fait ?

Une chaine littérale "Hello" n'est pas modifiable. de même un pointeur sur une telle chaine :
Code:
char *s = "Hello";
ne permet pas de modifier cette chaine. Il est d'ailleurs recommandé d'écrire :
Code:
char const *p = "Hello";
Ça permet au compilateur de vérifier qu'il n'y a pas d'erreur de codage.

Par contre, un tableau de char initialisé avec les caractères d'une chaine valide est modifiable :
Code:
char s[] = "Hello";
ou
Code:

char *s = malloc(20);
strcpy (s, "Hello");
ou encore
Code:
char *s = strdup ("hello");
permettent la modification.

Nota : La fonction strdup() n'est pas standard C, mais POSIX.1, donc très portable.




Vaut-il mieux utiliser puts que printf lorsqu'on n'a pas de valeur de variable à afficher ?

L'usage courant est printf(). Il couvre tous les cas. Si il n'y a pas de champ et qu'on souhaite ajouter un \n à la fin du texte, on peut utiliser puts()...




Dans la section sur les [ Objets variables], j'ai du mal à faire la distinction entre les 'g_variables globales' par 'bloc, programme' et 'G_variables Globales' par ' Programme, programme'.

Je comprends par g_variables globales 'bloc, programme' : variable interne d'un bloc mais accessible dans tout le programme ; est ce bien la bonne interprétation ? Est ce possible?


La distinction est subtile. En fait, le langage C admet 2 qualifications pour les variables :

  • avec lien externe
  • sans lien externe

Ce qui s'implémente :
Code:

int G_xxx; /* avec lien externe */
static int S_xxx; /* sans lien externe */

La différence joue sur la portée.

G_xxx est de portée globale
S_xxx est de portée réduite au module de compilation courant.

Mais cette distinction n'est parfois pas suffisante pour les grosses applications. En effet, il est parfois nécessaire qu'un bloc fonctionnel soit implémenté non pas par une unité de compilation, mais par plusieurs, tout simplement pour des questions de taille ou d'organisation interne.

Il est donc possible de créer 'logiquement' (alors que ça ne fait pas partie du langage), une qualification 'intermédiaire' qui utilise évidemment le mode 'avec lien externe', puisqu'il y a plusieurs unités de compilations concernées, mais dont on ne fournit l'interface externe qu'aux modules concernés.

On distingue ces variables un peu spéciales en les préfixant par g_ (globales, ... mais pas trop !) au lieu de G_ (globales totales). C'est bien sûr une convention et non une obligation...

Exemple : soit à implémenter le bloc fonctionnel A

On peut imaginer qu'il soit découpé en 2 fichiers .c (unités de compilations :

a.c
a_tools.c

qu'il existe une variable 'globale' ne concernant que les fichiers a.c et a_tools.c :
Code:
int a_g_xxx;
Celle-ci est, par exemple, définie dans a_tools.c :
Code:

/* a_tools.c */
#include "a_tools.h"

int a_g_xxx;
et sa déclaration est placée dans a_tools.h :
Code:

/* a_tools.h */

extern int a_g_xxx;

Elle n'est pas dans a.h (l'interface externe du BF A), ce qui fait qu'elle est inconnue des autres parties de l'application.

Par contre, elle est connue de a.c qui inclue a_tools.h :
Code:

/* a.c */
#include "a.h"
#include "a_tools.h"

void a_init(void)
{
  a_g_xxx = 123;
}
etc.

Je rappelle que tout ceci s'inscrit dans le cadre d'une remarque plus générale qui est "les globales, çaÿ mal".

Il faut les éviter quand c'est possible, car cela rompt un certain nombre de principes d'indépendances et de réutilisation du code. Mais c'est parfois utile.




Depuis la norme C99 on est plus obligé de déclarer ses variables en début de bloc : on peut le faire n'importe ou (moyennant les contraintes de visibilité bien sûr).
Seulement je vois 2 types de comportement à cela : le 1er étant de systématiquement regrouper ses déclarations de variables en début de bloc (come avant quoi) et le second étant de retarder au maximum les déclarations et au minimum les initialisations ( l'idéal étant d'initialiser sa variable en même temps que de la déclarer).

Je voulais savoir quelle était la meilleure pratique ?


Je pense que tu a tout compris. Soit tu fais le choix le la lisibilité, soit celui de la légèreté.

Personnellement, je fais comme ça : le plus tard possible. D'autant plus que je trouve ça souvent plus facile à lire.

Par exemple, lorsque l'on fait du réseau, je trouve plus commode de s'occuper des sockets dans un premier temps ; puis de l'adresse ensuite. et de finir sur un ligature propre.

Plutôt que de commencer à déclarer chacune des deux choses, de les initialiser ensuite puis ensuite des faire la ligature.

Ensuite, il faut bien comprendre que la compilation ne pourra pas se faire sur de vieux compilateur.

Tu dis dans un premier temps que tu n'aimes cette seconde méthode du C99 et pourtant tu la mets en application quand même ?

Oui, mais pas sous la forme C99, car ne je sais pas quelle est est la portée exacte de la variable. J'utilise la méthode C90 qui consiste à définir la variable en début de bloc, quitte à créer un bloc supplémentaire de toutes pièces.


et en ce qui concerne les variables : tu parles de celles déclarés avec 'register' si c'est le cas je croyais que de nos jours c'était d'un intérêt limité ?

Les variables locales sont implémentées au choix du compilateur. Certaines sont placées en registre, d'autres en pile.

Il se trouve que j'ai constaté sur plusieurs compilateurs, après analyse du code assembleur généré, que l'utilisation massive des techniques de réduction de portée que je préconise favorise la réutilisation par le compilateur, des variables définies en registre. C'est malheureusement rarement le cas de celles définies en pile... Mais on y peut rien, c'est comme ça. Ce n'est qu'une constatation.


Dernière édition par Admin le Lun 26 Mai 2008 - 20:18, édité 11 fois (Raison : Post-it)
-ed-
-ed-
Admin
Admin

Messages : 290
Date d'inscription : 26/05/2008
Age : 67
Localisation : Paris 14eme arrondissement (75, France)

http://bien-programmer.fr

Revenir en haut Aller en bas

Questions souvent posées (FAQ) Empty Questions souvent posées (FAQ-2)

Message  -ed- Lun 26 Mai 2008 - 13:54

J'aimerais savoir où je pourrais trouver la liste complète des caractères spéciaux tels que \n ou \t.

\a alerte sonore ou visuelle selon implémentation
\b BS retour arrière backspace
\f FF fin de page form feed
\n NL fin de ligne new line
\r CR retour chariot carriage return
\t HT tabulation horizontale
\v VT tabulation verticale
\\ \ antislash
\' ' apostrophe caractère seul. Dans une chaine : "'"
\" " guillemet
\xnn caractere de code hexa nn
\nnn caractere de code octal nnn
\uxxxx caractere unicode de code hexa xxxx
\Uxxxxxxxx caractere unicode de code hexa xxxxxxxx

Références :
n1124 a écrit:
5.2.2 Character display semantics
1 The active position is that location on a display device where the next character output by
the fputc function would appear. The intent of writing a printing character (as defined
by the isprint function) to a display device is to display a graphic representation of
that character at the active position and then advance the active position to the next
position on the current line. The direction of writing is locale-specific. If the active
position is at the final position of a line (if there is one), the behavior of the display device
is unspecified.
2 Alphabetic escape sequences representing nongraphic characters in the execution
character set are intended to produce actions on display devices as follows:
\a (alert) Produces an audible or visible alert without changing the active position.
\b (backspace) Moves the active position to the previous position on the current line. If
the active position is at the initial position of a line, the behavior of the display
device is unspecified.
\f ( form feed) Moves the active position to the initial position at the start of the next
logical page.
\n (new line) Moves the active position to the initial position of the next line.
\r (carriage return) Moves the active position to the initial position of the current line.
\t (horizontal tab) Moves the active position to the next horizontal tabulation position
on the current line. If the active position is at or past the last defined horizontal
tabulation position, the behavior of the display device is unspecified.
\v (vertical tab) Moves the active position to the initial position of the next vertical
tabulation position. If the active position is at or past the last defined vertical
tabulation position, the behavior of the display device is unspecified.
3 Each of these escape sequences shall produce a unique implementation-defined value
which can be stored in a single char object. The external representations in a text file
need not be identical to the internal representations, and are outside the scope of this
§5.2.2 Environment 19
ISO/IEC 9899:TC2 Committee Draft — May 6, 2005 WG14/N1124
International Standard.
Forward references: the isprint function (7.4.1.8), the fputc function (7.19.7.3).

A ne pas confondre avec la liste des caractères constants :
n1124 a écrit:
6.4.4.4 Character constants
Syntax
1 character-constant:
' c-char-sequence '
L' c-char-sequence '
c-char-sequence:
c-char
c-char-sequence c-char
c-char:
any member of the source character set except
the single-quote ', backslash \, or new-line character
escape-sequence
escape-sequence:
simple-escape-sequence
octal-escape-sequence
hexadecimal-escape-sequence
universal-character-name
simple-escape-sequence: one of
\' \" \? \\
\a \b \f \n \r \t \v
octal-escape-sequence:
\ octal-digit
\ octal-digit octal-digit
\ octal-digit octal-digit octal-digit
hexadecimal-escape-sequence:
\x hexadecimal-digit
hexadecimal-escape-sequence hexadecimal-digit
§6.4.4.4 Language 59
ISO/IEC 9899:TC2 Committee Draft — May 6, 2005 WG14/N1124
Description
2 An integer character constant is a sequence of one or more multibyte characters enclosed
in single-quotes, as in 'x'. A wide character constant is the same, except prefixed by the
letter L. With a few exceptions detailed later, the elements of the sequence are any
members of the source character set; they are mapped in an implementation-defined
manner to members of the execution character set.
3 The single-quote ', the double-quote ", the question-mark ?, the backslash \, and
arbitrary integer values are representable according to the following table of escape
sequences:
single quote' \'
double quote" \"
question mark? \?
backslash\ \\
octal character \octal digits
hexadecimal character \x hexadecimal digits
4 The double-quote " and question-mark ? are representable either by themselves or by the
escape sequences \" and \?, respectively, but the single-quote ' and the backslash \
shall be represented, respectively, by the escape sequences \' and \\.
5 The octal digits that follow the backslash in an octal escape sequence are taken to be part
of the construction of a single character for an integer character constant or of a single
wide character for a wide character constant. The numerical value of the octal integer so
formed specifies the value of the desired character or wide character.
6 The hexadecimal digits that follow the backslash and the letter x in a hexadecimal escape
sequence are taken to be part of the construction of a single character for an integer
character constant or of a single wide character for a wide character constant. The
numerical value of the hexadecimal integer so formed specifies the value of the desired
character or wide character.
7 Each octal or hexadecimal escape sequence is the longest sequence of characters that can
constitute the escape sequence.
8 In addition, characters not in the basic character set are representable by universal
character names and certain nongraphic characters are representable by escape sequences
consisting of the backslash \ followed by a lowercase letter: \a, \b, \f, \n, \r, \t,
and \v.65)
65) The semantics of these characters were discussed in 5.2.2. If any other character follows a backslash,
the result is not a token and a diagnostic is required. See ‘‘future language directions’’ (6.11.4).
60 Language §6.4.4.4
WG14/N1124 Committee Draft — May 6, 2005 ISO/IEC 9899:TC2
Constraints
9 The value of an octal or hexadecimal escape sequence shall be in the range of
representable values for the type unsigned char for an integer character constant, or
the unsigned type corresponding to wchar_t for a wide character constant.
Semantics
10 An integer character constant has type int. The value of an integer character constant
containing a single character that maps to a single-byte execution character is the
numerical value of the representation of the mapped character interpreted as an integer.
The value of an integer character constant containing more than one character (e.g.,
'ab'), or containing a character or escape sequence that does not map to a single-byte
execution character, is implementation-defined. If an integer character constant contains
a single character or escape sequence, its value is the one that results when an object with
type char whose value is that of the single character or escape sequence is converted to
type int.
11 A wide character constant has type wchar_t, an integer type defined in the
<stddef.h> header. The value of a wide character constant containing a single
multibyte character that maps to a member of the extended execution character set is the
wide character corresponding to that multibyte character, as defined by the mbtowc
function, with an implementation-defined current locale. The value of a wide character
constant containing more than one multibyte character, or containing a multibyte
character or escape sequence not represented in the extended execution character set, is
implementation-defined.
12 EXAMPLE 1 The construction '\0' is commonly used to represent the null character.
13 EXAMPLE 2 Consider implementations that use two’s-complement representation for integers and eight
bits for objects that have type char. In an implementation in which type char has the same range of
values as signed char, the integer character constant '\xFF' has the value -1; if type char has the
same range of values as unsigned char, the character constant '\xFF' has the value +255.
14 EXAMPLE 3 Even if eight bits are used for objects that have type char, the construction '\x123'
specifies an integer character constant containing only one character, since a hexadecimal escape sequence
is terminated only by a non-hexadecimal character. To specify an integer character constant containing the
two characters whose values are '\x12' and '3', the construction '\0223' may be used, since an octal
escape sequence is terminated after three octal digits. (The value of this two-character integer character
constant is implementation-defined.)
15 EXAMPLE 4 Even if 12 or more bits are used for objects that have type wchar_t, the construction
L'\1234' specifies the implementation-defined value that results from the combination of the values
0123 and '4'.
Forward references: common definitions <stddef.h> (7.17), the mbtowc function
(7.20.7.2).
§6.4.4.4 Language 61





Peut on exécuter une action autant de fois que le nombre qui est dans une variable ?

C'est possible très simplement avec une boucle. Un exemple parmi tant d'autres :
Code:

  int ligne = 5;
  int i;
  for (i = 0; i < ligne; i++)
  {
      printf("\n");
  }




En relisant les notes d'Emmanuel concernant les pointeurs sur objet, j'ai trouvé:
Pour définir un pointeur, on utilise un type, puis le signe '*' et enfin l'identificateur, suivit de ';' si on ne désire pas l'initialiser à la déclaration (peu recommandé).

Pourquoi n'est-ce pas recommandé ?


Parce qu'un pointeur devrait toujours avoir une valeur définie qui exprime ses 2 états clairement :

  • La valeur vaut NULL, l'état est 'invalide', le déréférencement est interdit.
  • La valeur est différente de NULL : l'état est valide. Le pointeur pointe vers une zone mémoire valide, le déréférencement est autorisé.

Ce principe est fondamental si on cherche à écrire du code sérieux (industriel).

Est-il valable uniquement dans le cas d'un appel à malloc ou autre fonction semblable parce qu'il se peut que l'allocation échoue (quoique, dans ce cas le pointeur est initialisé à NULL, il me semble), ou est-ce valable aussi lorsqu'on initialise un pointeur avec l'adresse d'une autre variable? On m'a appris à toujours initialiser un pointeur à NULL au moment de la déclaration, est-ce acceptable dans ce cas?

Initialiser à NULL est acceptable si il y a un 'retard' entre le moment où la variable est définie et le momùent où elle est affectée réellement. Mais dans la plupart des cas, on pourra combiner définition et initialisation 'vraie'.

Par exemple :
Code:
FILE *fp = fopen(...);
D'ailleurs, par rapport à ce qui suit dans cette note : initialiser un pointeur à NULL est-il équivalent au fait de l'initialiser à 0? Je n'avais jamais vu un pointeur initialisé à 0.

0 et NULL ont la même sémantique quand il s'agit de pointeurs. NULL est recommandé, car plus parlant que 0 dans ce cadre (Il se rapproche du terme NIL utilisé en algorithmique).

Code:

/* apres utilisation, l'espace memoire est libere */
free (p);

/* le pointeur est force a l'etat invalide */
p = NULL;
}

free(p); ne remet-il pas automatiquement la valeur de p à NULL?


Certainement pas. Comment le pourrait-il ? Il n'a pas l'interface pour ça. C'est pour ça que je recommande fortement l'affectation explicite p = NULL après un free(), sous la forme :
Code:
free (p), p = NULL;
ce qui ne change rien sur plan de l'exécution, mais qui sur le plan visuel montre bien que ces opérations sont 'indissociables'. (Pas question de faire quoique ce soit entre ces 2 instructions).




Il y a un point que je n'arrive vraiment pas a comprendre, c'est void. J'arrive pas a piger comment ça marche et quand il faut le mettre.

void est un mot clé qui signifie 'absence de ' ou 'vide'.

Il s'utilise principalement pour indiquer au compilateur qu'une fonction n'a pas de paramètres :
Code:

int f (void)
{
...
ou bien qu'elle ne retourne pas de valeur :
Code:

void f (char *s)
{
...
Evidemment, on peut combiner les 2 :
Code:

void f(void)
{
...
il y a d'autres utilisations plus avancées, mais on va déjà commencer par ça...




J'aimerais savoir comment est ce que l'on peut faire une fonction qui peut prendre un nombre indéfini de paramètres comme semble le faire la fonction printf().

C'est ce qu'on appelle une fonction 'variadic'. Elle s'appuie sur des fonctions (macros) définies dans <stdarg.h>
Code:

#include <stdio.h>
#include <stdarg.h>
#include <string.h>

void my_printf (int num, ...)
{
  va_list arguments;

  va_start (arguments, num);

  int i;
  for (i = 0; i < num; i++)
      printf ("%s\n", va_arg (arguments, char *));

  va_end (arguments);
}

int main (void)
{
  char lala[] = "Lala";
  char str1[50] = "Copie de ";
  char str2[] = "Chaines";

  my_printf (5, "Salut", "Bonjour", "1982", lala, strcat (str1, str2));

  return 0;
}
-ed-
-ed-
Admin
Admin

Messages : 290
Date d'inscription : 26/05/2008
Age : 67
Localisation : Paris 14eme arrondissement (75, France)

http://bien-programmer.fr

Revenir en haut Aller en bas

Revenir en haut

- Sujets similaires

 
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum
Ne ratez plus aucun deal !
Abonnez-vous pour recevoir par notification une sélection des meilleurs deals chaque jour.
IgnorerAutoriser