C Language
Les opérateurs
Recherche…
Introduction
Un opérateur dans un langage de programmation est un symbole qui indique au compilateur ou à l'interprète d'exécuter une opération mathématique, relationnelle ou logique spécifique et de produire un résultat final.
C a beaucoup d'opérateurs puissants. De nombreux opérateurs C sont des opérateurs binaires, ce qui signifie qu'ils ont deux opérandes. Par exemple, dans a / b
, /
est un opérateur binaire qui accepte deux opérandes ( a
, b
). Il y a des opérateurs unaires qui prennent un opérande (par exemple: ~
, ++
), et un seul opérateur ternaire ? :
.
Syntaxe
- opérateur expr1
- opérateur expr2
- opérateur expr1 expr2
- expr1? expr2: expr3
Remarques
Les opérateurs ont une arité , une priorité et une associativité .
Arity indique le nombre d'opérandes. En C, il existe trois opérateurs différents:
- Unaire (1 opérande)
- Binaire (2 opérandes)
- Ternaire (3 opérandes)
La priorité indique quels opérateurs "se lient" en premier à leurs opérandes. C'est-à-dire quel opérateur a la priorité d'opérer sur ses opérandes. Par exemple, le langage C obéit à la convention selon laquelle la multiplication et la division ont priorité sur l'addition et la soustraction:
a * b + c
Donne le même résultat que
(a * b) + c
Si ce n'est pas ce qui était souhaité, la priorité peut être forcée à l'aide de parenthèses, car elles ont la plus haute priorité de tous les opérateurs.
a * (b + c)
Cette nouvelle expression produira un résultat différent des deux expressions précédentes.
Le langage C a de nombreux niveaux de priorité; Un tableau est donné ci-dessous de tous les opérateurs, par ordre décroissant de priorité.
Tableau de préséance
Les opérateurs Associativité ()
[]
->
.
de gauche à droite !
~
++
--
+
-
*
(déréférencement)(type)
sizeof
de droite à gauche *
(multiplication)/
%
de gauche à droite +
-
de gauche à droite <<
>>
de gauche à droite <
<=
>
>=
de gauche à droite ==
!=
de gauche à droite &
de gauche à droite ^
de gauche à droite |
de gauche à droite &&
de gauche à droite ||
de gauche à droite ?:
de droite à gauche =
+=
-=
*=
/=
%=
&=
^=
|=
<<=
>>=
de droite à gauche ,
de gauche à droite L'associativité indique comment les opérateurs d'égale priorité sont liés par défaut et il en existe deux types: de gauche à droite et de droite à gauche . Un exemple de liaison de gauche à droite est l'opérateur de soustraction (
-
). L'expressiona - b - c - d
a trois soustractions de priorité identique, mais donne le même résultat que
((a - b) - c) - d
parce que le plus à gauche
-
se lie d'abord à ses deux opérandes.Un exemple d'associativité de droite à gauche sont les opérateurs dereference
*
et post-incrément++
. Les deux ont la même priorité, donc s'ils sont utilisés dans une expression telle que* ptr ++
, cela équivaut à
* (ptr ++)
parce que l'opérateur unaire le plus à droite (
++
) se lie d'abord à son seul opérande.
Opérateurs relationnels
Les opérateurs relationnels vérifient si une relation spécifique entre deux opérandes est vraie. Le résultat est évalué à 1 (ce qui signifie vrai ) ou à 0 (ce qui signifie faux ). Ce résultat est souvent utilisé pour affecter le flux de contrôle (via if
, while
, for
), mais peut également être stocké dans des variables.
Est égal à "=="
Vérifie si les opérandes fournis sont égaux.
1 == 0; /* evaluates to 0. */
1 == 1; /* evaluates to 1. */
int x = 5;
int y = 5;
int *xptr = &x, *yptr = &y;
xptr == yptr; /* evaluates to 0, the operands hold different location addresses. */
*xptr == *yptr; /* evaluates to 1, the operands point at locations that hold the same value. */
Attention: cet opérateur ne doit pas être confondu avec l'opérateur d'affectation ( =
)!
Pas égal à "! ="
Vérifie si les opérandes fournis ne sont pas égaux.
1 != 0; /* evaluates to 1. */
1 != 1; /* evaluates to 0. */
int x = 5;
int y = 5;
int *xptr = &x, *yptr = &y;
xptr != yptr; /* evaluates to 1, the operands hold different location addresses. */
*xptr != *yptr; /* evaluates to 0, the operands point at locations that hold the same value. */
Cet opérateur renvoie effectivement le résultat opposé à celui de l'opérateur égal ( ==
).
Ne pas "!"
Vérifiez si un objet est égal à 0
.
Le !
peut également être utilisé directement avec une variable comme suit:
!someVal
Cela a le même effet que:
someVal == 0
Plus grand que ">"
Vérifie si l'opérande de gauche a une valeur supérieure à l'opérande de droite
5 > 4 /* evaluates to 1. */
4 > 5 /* evaluates to 0. */
4 > 4 /* evaluates to 0. */
Moins que "<"
Vérifie si l'opérande de gauche a une valeur inférieure à l'opérande de droite
5 < 4 /* evaluates to 0. */
4 < 5 /* evaluates to 1. */
4 < 4 /* evaluates to 0. */
Supérieur ou égal à "> ="
Vérifie si l'opérande de gauche a une valeur supérieure ou égale à l'opérande de droite.
5 >= 4 /* evaluates to 1. */
4 >= 5 /* evaluates to 0. */
4 >= 4 /* evaluates to 1. */
Inférieur ou égal à "<="
Vérifie si l'opérande de gauche a une valeur inférieure ou égale à l'opérande de droite.
5 <= 4 /* evaluates to 0. */
4 <= 5 /* evaluates to 1. */
4 <= 4 /* evaluates to 1. */
Opérateurs d'affectation
Attribue la valeur de l'opérande de droite à l'emplacement de stockage nommé par l'opérande de gauche et renvoie la valeur.
int x = 5; /* Variable x holds the value 5. Returns 5. */
char y = 'c'; /* Variable y holds the value 99. Returns 99
* (as the character 'c' is represented in the ASCII table with 99).
*/
float z = 1.5; /* variable z holds the value 1.5. Returns 1.5. */
char const* s = "foo"; /* Variable s holds the address of the first character of the string 'foo'. */
Plusieurs opérations arithmétiques ont un opérateur d' affectation composé .
a += b /* equal to: a = a + b */
a -= b /* equal to: a = a - b */
a *= b /* equal to: a = a * b */
a /= b /* equal to: a = a / b */
a %= b /* equal to: a = a % b */
a &= b /* equal to: a = a & b */
a |= b /* equal to: a = a | b */
a ^= b /* equal to: a = a ^ b */
a <<= b /* equal to: a = a << b */
a >>= b /* equal to: a = a >> b */
Une caractéristique importante de ces affectations composées est que l'expression du côté gauche ( a
) n'est évaluée qu'une seule fois. Par exemple, si p
est un pointeur
*p += 27;
dereferences p
qu'une seule fois, alors que le suivant le fait deux fois.
*p = *p + 27;
Il convient également de noter que le résultat d'une assignation telle que a = b
est ce qu'on appelle une valeur . Ainsi, l'affectation a en réalité une valeur qui peut ensuite être affectée à une autre variable. Cela permet de chaîner des affectations pour définir plusieurs variables dans une seule instruction.
Cette valeur peut être utilisée dans les expressions de contrôle des instructions if
(ou boucles ou instructions switch
) qui protègent du code sur le résultat d'une autre expression ou d'un appel de fonction. Par exemple:
char *buffer;
if ((buffer = malloc(1024)) != NULL)
{
/* do something with buffer */
free(buffer);
}
else
{
/* report allocation failure */
}
Pour cette raison, il faut veiller à éviter une faute de frappe commune pouvant conduire à des bogues mystérieux.
int a = 2;
/* ... */
if (a = 1)
/* Delete all files on my hard drive */
Cela aura des résultats désastreux, car a = 1
sera toujours évalué à 1
et donc l'expression de contrôle de l'instruction if
sera toujours vraie (en savoir plus sur cet écueil commun ici ). L'auteur avait certainement l'intention d'utiliser l'opérateur d'égalité ( ==
) comme indiqué ci-dessous:
int a = 2;
/* ... */
if (a == 1)
/* Delete all files on my hard drive */
Associativité des opérateurs
int a, b = 1, c = 2;
a = b = c;
Cela assigne c
à b
, qui renvoie b
, qui est assigné à a
. Cela se produit parce que tous les opérateurs d'affectation ont une associativité correcte, ce qui signifie que l'opération la plus à droite de l'expression est évaluée en premier et se poursuit de droite à gauche.
Opérateurs arithmétiques
Arithmétique de base
Renvoie une valeur résultant de l'application de l'opérande de gauche à l'opérande de droite, à l'aide de l'opération mathématique associée. Des règles mathématiques normales de commutation s'appliquent (c.-à-d. L'addition et la multiplication sont commutatives, la soustraction, la division et le module ne le sont pas).
Opérateur Additionnel
L'opérateur d'addition ( +
) est utilisé pour ajouter deux opérandes ensemble. Exemple:
#include <stdio.h>
int main(void)
{
int a = 5;
int b = 7;
int c = a + b; /* c now holds the value 12 */
printf("%d + %d = %d",a,b,c); /* will output "5 + 7 = 12" */
return 0;
}
Opérateur de soustraction
L'opérateur de soustraction ( -
) est utilisé pour soustraire le second opérande du premier. Exemple:
#include <stdio.h>
int main(void)
{
int a = 10;
int b = 7;
int c = a - b; /* c now holds the value 3 */
printf("%d - %d = %d",a,b,c); /* will output "10 - 7 = 3" */
return 0;
}
Opérateur de multiplication
L'opérateur de multiplication ( *
) est utilisé pour multiplier les deux opérandes. Exemple:
#include <stdio.h>
int main(void)
{
int a = 5;
int b = 7;
int c = a * b; /* c now holds the value 35 */
printf("%d * %d = %d",a,b,c); /* will output "5 * 7 = 35" */
return 0;
}
Ne pas confondre avec l'opérateur *
dereference.
Opérateur de division
L'opérateur de division ( /
) divise le premier opérande par le second. Si les deux opérandes de la division sont des nombres entiers, il retournera une valeur entière et rejettera le reste (utilisez l'opérateur %
modulo pour calculer et acquérir le reste).
Si l'un des opérandes est une valeur à virgule flottante, le résultat est une approximation de la fraction.
Exemple:
#include <stdio.h>
int main (void)
{
int a = 19 / 2 ; /* a holds value 9 */
int b = 18 / 2 ; /* b holds value 9 */
int c = 255 / 2; /* c holds value 127 */
int d = 44 / 4 ; /* d holds value 11 */
double e = 19 / 2.0 ; /* e holds value 9.5 */
double f = 18.0 / 2 ; /* f holds value 9.0 */
double g = 255 / 2.0; /* g holds value 127.5 */
double h = 45.0 / 4 ; /* h holds value 11.25 */
printf("19 / 2 = %d\n", a); /* Will output "19 / 2 = 9" */
printf("18 / 2 = %d\n", b); /* Will output "18 / 2 = 9" */
printf("255 / 2 = %d\n", c); /* Will output "255 / 2 = 127" */
printf("44 / 4 = %d\n", d); /* Will output "44 / 4 = 11" */
printf("19 / 2.0 = %g\n", e); /* Will output "19 / 2.0 = 9.5" */
printf("18.0 / 2 = %g\n", f); /* Will output "18.0 / 2 = 9" */
printf("255 / 2.0 = %g\n", g); /* Will output "255 / 2.0 = 127.5" */
printf("45.0 / 4 = %g\n", h); /* Will output "45.0 / 4 = 11.25" */
return 0;
}
Opérateur Modulo
L'opérateur modulo ( %
) reçoit uniquement des opérandes entiers et est utilisé pour calculer le reste après la division du premier opérande par le second. Exemple:
#include <stdio.h>
int main (void) {
int a = 25 % 2; /* a holds value 1 */
int b = 24 % 2; /* b holds value 0 */
int c = 155 % 5; /* c holds value 0 */
int d = 49 % 25; /* d holds value 24 */
printf("25 % 2 = %d\n", a); /* Will output "25 % 2 = 1" */
printf("24 % 2 = %d\n", b); /* Will output "24 % 2 = 0" */
printf("155 % 5 = %d\n", c); /* Will output "155 % 5 = 0" */
printf("49 % 25 = %d\n", d); /* Will output "49 % 25 = 24" */
return 0;
}
Opérateurs d'incrémentation / décrémentation
Les opérateurs d'incrémentation ( a++
) et de décrémentation ( a--
) sont différents en ce sens qu'ils changent la valeur de la variable à laquelle ils sont appliqués sans opérateur d'affectation. Vous pouvez utiliser des opérateurs d'incrémentation et de décrémentation avant ou après la variable. L'emplacement de l'opérateur modifie la synchronisation de l'incrémentation / décrémentation de la valeur avant ou après l'affectation à la variable. Exemple:
#include <stdio.h>
int main(void)
{
int a = 1;
int b = 4;
int c = 1;
int d = 4;
a++;
printf("a = %d\n",a); /* Will output "a = 2" */
b--;
printf("b = %d\n",b); /* Will output "b = 3" */
if (++c > 1) { /* c is incremented by 1 before being compared in the condition */
printf("This will print\n"); /* This is printed */
} else {
printf("This will never print\n"); /* This is not printed */
}
if (d-- < 4) { /* d is decremented after being compared */
printf("This will never print\n"); /* This is not printed */
} else {
printf("This will print\n"); /* This is printed */
}
}
Comme le montre l'exemple de c
et d
, les deux opérateurs ont deux formes, la notation préfixe et la notation postfixe. Les deux ont le même effet en incrémentant ( ++
) ou en décrémentant ( --
) la variable, mais diffèrent par la valeur qu'ils renvoient: les opérations de préfixe font l'opération en premier et ensuite la valeur, alors que les opérations postfixes déterminent d'abord la valeur à être retourné, puis effectuez l'opération.
En raison de ce comportement potentiellement contre-intuitif, l'utilisation d'opérateurs d'incrémentation / décrémentation à l'intérieur des expressions est controversée.
Opérateurs logiques
Logique ET
Effectue un booléen logique AND-ing des deux opérandes renvoyant 1 si les deux opérandes ne sont pas à zéro. L'opérateur logique AND est de type int
.
0 && 0 /* Returns 0. */
0 && 1 /* Returns 0. */
2 && 0 /* Returns 0. */
2 && 3 /* Returns 1. */
OU logique
Effectue un OU booléen logique des deux opérandes retournant 1 si l'un des opérandes est non nul. L'opérateur logique OU est de type int
.
0 || 0 /* Returns 0. */
0 || 1 /* Returns 1. */
2 || 0 /* Returns 1. */
2 || 3 /* Returns 1. */
Logique NON
Effectue une négation logique. L'opérateur logique NOT est de type int
. L'opérateur NOT vérifie si au moins un bit est égal à 1, si tel est le cas, il renvoie 0. Sinon, il renvoie 1;
!1 /* Returns 0. */
!5 /* Returns 0. */
!0 /* Returns 1. */
Évaluation en court-circuit
Il y a quelques propriétés essentielles communes à la fois &&
et ||
:
- l'opérande de gauche (LHS) est entièrement évalué avant que l'opérande de droite (RHS) soit évalué,
- il y a un point de séquence entre l'évaluation de l'opérande gauche et l'opérande droit,
- et, plus important encore, l’opérande de droite n’est pas du tout évalué si le résultat de l’opérande de gauche détermine le résultat global.
Cela signifie que:
- si le LHS est évalué à «vrai» (non nul), l'ERS de
||
ne sera pas évalué (parce que le résultat de «vrai OU quelque chose» est «vrai»), - Si le LHS est évalué à "false" (zéro), le RHS de
&&
ne sera pas évalué (car le résultat de "false AND importe" est "false").
Ceci est important car cela vous permet d'écrire du code tel que:
const char *name_for_value(int value)
{
static const char *names[] = { "zero", "one", "two", "three", };
enum { NUM_NAMES = sizeof(names) / sizeof(names[0]) };
return (value >= 0 && value < NUM_NAMES) ? names[value] : "infinity";
}
Si une valeur négative est transmise à la fonction, la value >= 0
terme est évaluée à false et la value < NUM_NAMES
terme n'est pas évaluée.
Incrémenter / Décrémenter
Les opérateurs d'incrémentation et de décrémentation existent sous forme de préfixe et de postfixe .
int a = 1;
int b = 1;
int tmp = 0;
tmp = ++a; /* increments a by one, and returns new value; a == 2, tmp == 2 */
tmp = a++; /* increments a by one, but returns old value; a == 3, tmp == 2 */
tmp = --b; /* decrements b by one, and returns new value; b == 0, tmp == 0 */
tmp = b--; /* decrements b by one, but returns old value; b == -1, tmp == 0 */
Notez que les opérations arithmétiques n'introduisent pas de points de séquence , de sorte que certaines expressions avec des opérateurs ++
ou --
peuvent introduire un comportement indéfini .
Opérateur conditionnel / opérateur ternaire
Évalue son premier opérande et, si la valeur résultante n'est pas égale à zéro, évalue son deuxième opérande. Sinon, il évalue son troisième opérande, comme illustré dans l'exemple suivant:
a = b ? c : d;
est équivalent à:
if (b)
a = c;
else
a = d;
Ce pseudo-code le représente: condition ? value_if_true : value_if_false
. Chaque valeur peut être le résultat d'une expression évaluée.
int x = 5;
int y = 42;
printf("%i, %i\n", 1 ? x : y, 0 ? x : y); /* Outputs "5, 42" */
L'opérateur conditionnel peut être imbriqué. Par exemple, le code suivant détermine le plus grand des trois nombres:
big= a > b ? (a > c ? a : c)
: (b > c ? b : c);
L'exemple suivant écrit même des entiers dans un fichier et des entiers impairs dans un autre fichier:
#include<stdio.h>
int main()
{
FILE *even, *odds;
int n = 10;
size_t k = 0;
even = fopen("even.txt", "w");
odds = fopen("odds.txt", "w");
for(k = 1; k < n + 1; k++)
{
k%2==0 ? fprintf(even, "\t%5d\n", k)
: fprintf(odds, "\t%5d\n", k);
}
fclose(even);
fclose(odds);
return 0;
}
L'opérateur conditionnel associe de droite à gauche. Considérer ce qui suit:
exp1 ? exp2 : exp3 ? exp4 : exp5
Comme l'association est de droite à gauche, l'expression ci-dessus est évaluée comme
exp1 ? exp2 : ( exp3 ? exp4 : exp5 )
Opérateur de virgule
Évalue son opérande gauche, supprime la valeur résultante, puis évalue son opérande de droits et result renvoie la valeur de son opérande le plus à droite.
int x = 42, y = 42;
printf("%i\n", (x *= 2, y)); /* Outputs "42". */
L'opérateur de virgule introduit un point de séquence entre ses opérandes.
Notez que la virgule utilisée dans les fonctions appelant des arguments séparés n'est PAS l' opérateur virgule , mais plutôt un séparateur différent de l' opérateur virgule . Par conséquent, il n'a pas les propriétés de l' opérateur de virgule .
L'appel printf()
ci-dessus contient à la fois l' opérateur de virgule et le séparateur .
printf("%i\n", (x *= 2, y)); /* Outputs "42". */
/* ^ ^ this is a comma operator */
/* this is a separator */
L'opérateur virgule est souvent utilisé dans la section d'initialisation ainsi que dans la section de mise à jour d'une boucle for
. Par exemple:
for(k = 1; k < 10; printf("\%d\\n", k), k += 2); /*outputs the odd numbers below 9/*
/* outputs sum to first 9 natural numbers */
for(sumk = 1, k = 1; k < 10; k++, sumk += k)
printf("\%5d\%5d\\n", k, sumk);
Opérateur de casting
Effectue une conversion explicite dans le type donné à partir de la valeur résultant de l'évaluation de l'expression donnée.
int x = 3;
int y = 4;
printf("%f\n", (double)x / y); /* Outputs "0.750000". */
Ici, la valeur de x
est convertie en double
, la division favorise également la valeur de y
pour double
, et le résultat de la division, un double
est transmis à printf
pour impression.
taille de l'opérateur
Avec un type comme opérande
Evalue dans la taille en octets, de type size_t
, des objets du type donné. Nécessite des parenthèses autour du type.
printf("%zu\n", sizeof(int)); /* Valid, outputs the size of an int object, which is platform-dependent. */
printf("%zu\n", sizeof int); /* Invalid, types as arguments need to be surrounded by parentheses! */
Avec une expression comme opérande
Évalue la taille en octets, de type size_t
, des objets du type de l'expression donnée. L'expression elle-même n'est pas évaluée. Les parenthèses ne sont pas requises; Cependant, comme l'expression doit être unaire, il est recommandé de toujours les utiliser.
char ch = 'a';
printf("%zu\n", sizeof(ch)); /* Valid, will output the size of a char object, which is always 1 for all platforms. */
printf("%zu\n", sizeof ch); /* Valid, will output the size of a char object, which is always 1 for all platforms. */
Arithmétique du pointeur
Ajout de pointeur
Étant donné un pointeur et un type scalaire N
, évalue en un pointeur le N
ème élément du type pointé qui succède directement à l'objet pointé en mémoire.
int arr[] = {1, 2, 3, 4, 5};
printf("*(arr + 3) = %i\n", *(arr + 3)); /* Outputs "4", arr's fourth element. */
Peu importe si le pointeur est utilisé comme valeur d'opérande ou comme valeur scalaire. Cela signifie que des choses telles que 3 + arr
sont valides. Si arr[k]
est le membre k+1
d'un tableau, alors arr+k
est un pointeur sur arr[k]
. En d'autres termes, arr
ou arr+0
est un pointeur sur arr[0]
, arr+1
est un pointeur sur arr[2]
, etc. En général, *(arr+k)
est identique à arr[k]
.
Contrairement à l'arithmétique habituelle, l'ajout de 1
à un pointeur sur un int
ajoutera 4
octets à la valeur d'adresse actuelle. Comme les noms de tableaux sont des pointeurs constants, +
est le seul opérateur que nous pouvons utiliser pour accéder aux membres d'un tableau via la notation du pointeur en utilisant le nom du tableau. Cependant, en définissant un pointeur sur un tableau, nous pouvons obtenir plus de flexibilité pour traiter les données dans un tableau. Par exemple, nous pouvons imprimer les membres d'un tableau comme suit:
#include<stdio.h>
static const size_t N = 5
int main()
{
size_t k = 0;
int arr[] = {1, 2, 3, 4, 5};
for(k = 0; k < N; k++)
{
printf("\n\t%d", *(arr + k));
}
return 0;
}
En définissant un pointeur sur le tableau, le programme ci-dessus est équivalent à ce qui suit:
#include<stdio.h>
static const size_t N = 5
int main()
{
size_t k = 0;
int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr; /* or int *ptr = &arr[0]; */
for(k = 0; k < N; k++)
{
printf("\n\t%d", ptr[k]);
/* or printf("\n\t%d", *(ptr + k)); */
/* or printf("\n\t%d", *ptr++); */
}
return 0;
}
Voir que les membres du tableau arr
sont accédés en utilisant les opérateurs +
et ++
. Les autres opérateurs pouvant être utilisés avec le pointeur ptr
sont -
et --
.
Soustraction de pointeur
Étant donné deux pointeurs sur le même type, évalue en un objet de type ptrdiff_t
qui contient la valeur scalaire qui doit être ajoutée au second pointeur afin d'obtenir la valeur du premier pointeur.
int arr[] = {1, 2, 3, 4, 5};
int *p = &arr[2];
int *q = &arr[3];
ptrdiff_t diff = q - p;
printf("q - p = %ti\n", diff); /* Outputs "1". */
printf("*(p + (q - p)) = %d\n", *(p + diff)); /* Outputs "4". */
Opérateurs d'accès
Les opérateurs d'accès au membre (dot .
Et flèche ->
) sont utilisés pour accéder à un membre d'une struct
.
Membre d'objet
Évalue la valeur indiquant l'objet qui est membre de l'objet accédé.
struct MyStruct
{
int x;
int y;
};
struct MyStruct myObject;
myObject.x = 42;
myObject.y = 123;
printf(".x = %i, .y = %i\n", myObject.x, myObject.y); /* Outputs ".x = 42, .y = 123". */
Membre d'objet pointé
Sucre syntaxique pour déréférencement suivi de l'accès des membres. Effectivement, une expression de la forme x->y
est un raccourci pour (*x).y
- mais l'opérateur de la flèche est beaucoup plus clair, surtout si les pointeurs de la structure sont imbriqués.
struct MyStruct
{
int x;
int y;
};
struct MyStruct myObject;
struct MyStruct *p = &myObject;
p->x = 42;
p->y = 123;
printf(".x = %i, .y = %i\n", p->x, p->y); /* Outputs ".x = 42, .y = 123". */
printf(".x = %i, .y = %i\n", myObject.x, myObject.y); /* Also outputs ".x = 42, .y = 123". */
Adresse de
Le unaire &
opérateur est l'adresse de l'opérateur. Il évalue l'expression donnée, où l'objet résultant doit être une lvalue. Ensuite, il évalue un objet dont le type est un pointeur sur le type de l'objet résultant et contient l'adresse de l'objet résultant.
int x = 3;
int *p = &x;
printf("%p = %p\n", (void *)&x, (void *)p); /* Outputs "A = A", for some implementation-defined A. */
Déréférence
L'opérateur unaire *
supprime un pointeur. Il évalue la valeur résultant du déréférencement du pointeur résultant de l'évaluation de l'expression donnée.
int x = 42;
int *p = &x;
printf("x = %d, *p = %d\n", x, *p); /* Outputs "x = 42, *p = 42". */
*p = 123;
printf("x = %d, *p = %d\n", x, *p); /* Outputs "x = 123, *p = 123". */
Indexage
L’indexation est du sucre syntaxique pour l’ajout de pointeur suivi d’un déréférencement. En effet, une expression de la forme a[i]
est équivalente à *(a + i)
- mais la notation en indice explicite est préférable.
int arr[] = { 1, 2, 3, 4, 5 };
printf("arr[2] = %i\n", arr[2]); /* Outputs "arr[2] = 3". */
Interchangeabilité de l'indexation
Ajouter un pointeur à un entier est une opération commutative (c’est-à-dire que l’ordre des opérandes ne change pas le résultat) donc pointer + integer == integer + pointer
.
Une conséquence de ceci est que arr[3]
et 3[arr]
sont équivalents.
printf("3[arr] = %i\n", 3[arr]); /* Outputs "3[arr] = 4". */
L'utilisation d'une expression 3[arr]
au lieu de arr[3]
n'est généralement pas recommandée, car elle affecte la lisibilité du code. Il a tendance à être populaire dans les concours de programmation obscurcis.
Opérateur d'appel de fonction
Le premier opérande doit être un pointeur de fonction (un désignateur de fonction est également acceptable car il sera converti en pointeur sur la fonction), identifiant la fonction à appeler et tous les autres opérandes, le cas échéant, sont appelés collectivement les arguments de l'appel de fonction. . Evalue dans la valeur de retour résultant de l'appel de la fonction appropriée avec les arguments respectifs.
int myFunction(int x, int y)
{
return x * 2 + y;
}
int (*fn)(int, int) = &myFunction;
int x = 42;
int y = 123;
printf("(*fn)(%i, %i) = %i\n", x, y, (*fn)(x, y)); /* Outputs "fn(42, 123) = 207". */
printf("fn(%i, %i) = %i\n", x, y, fn(x, y)); /* Another form: you don't need to dereference explicitly */
Opérateurs sur les bits
Les opérateurs binaires peuvent être utilisés pour effectuer des opérations au niveau du bit sur les variables.
Vous trouverez ci-dessous une liste des six opérateurs binaires pris en charge dans C:
symbole | Opérateur |
---|---|
Et | binaire ET |
| | bit à bit inclus OU |
^ | exclusif binaire OU (XOR) |
~ | pas du tout (son complément) |
<< | décalage gauche logique |
>> | décalage logique droite |
Le programme suivant illustre l'utilisation de tous les opérateurs binaires:
#include <stdio.h>
int main(void)
{
unsigned int a = 29; /* 29 = 0001 1101 */
unsigned int b = 48; /* 48 = 0011 0000 */
int c = 0;
c = a & b; /* 32 = 0001 0000 */
printf("%d & %d = %d\n", a, b, c );
c = a | b; /* 61 = 0011 1101 */
printf("%d | %d = %d\n", a, b, c );
c = a ^ b; /* 45 = 0010 1101 */
printf("%d ^ %d = %d\n", a, b, c );
c = ~a; /* -30 = 1110 0010 */
printf("~%d = %d\n", a, c );
c = a << 2; /* 116 = 0111 0100 */
printf("%d << 2 = %d\n", a, c );
c = a >> 2; /* 7 = 0000 0111 */
printf("%d >> 2 = %d\n", a, c );
return 0;
}
Les opérations binaires avec des types signés doivent être évitées car le bit de signe de cette représentation binaire a une signification particulière. Des restrictions particulières s’appliquent aux opérateurs de quarts:
Le décalage à gauche d'un bit dans le bit signé est erroné et conduit à un comportement indéfini.
Le décalage à droite d'une valeur négative (avec le bit de signe 1) est défini par l'implémentation et n'est donc pas portable.
Si la valeur de l'opérande droit d'un opérateur de décalage est négative ou est supérieure ou égale à la largeur de l'opérande gauche promu, le comportement n'est pas défini.
Masquage:
Le masquage fait référence au processus d'extraction des bits souhaités à partir d'une variable (ou à la transformation des bits souhaités) à l'aide d'opérations binaires logiques. L'opérande (constante ou variable) utilisé pour effectuer le masquage est appelé masque .
Le masquage est utilisé de différentes manières:
- Décider du modèle de bit d'une variable entière.
- Pour copier une partie d'un motif de bit donné dans une nouvelle variable, alors que le reste de la nouvelle variable est rempli de 0 (à l'aide du bit AND)
- Pour copier une partie d'un motif de bit donné dans une nouvelle variable, alors que le reste de la nouvelle variable est rempli de 1 (à l'aide d'un bit OU).
- Pour copier une partie d'un motif de bit donné dans une nouvelle variable, le reste du motif de bit d'origine est inversé dans la nouvelle variable (à l'aide d'un OU exclusif au niveau du bit).
La fonction suivante utilise un masque pour afficher le modèle de bit d'une variable:
#include <limits.h>
void bit_pattern(int u)
{
int i, x, word;
unsigned mask = 1;
word = CHAR_BIT * sizeof(int);
mask = mask << (word - 1); /* shift 1 to the leftmost position */
for(i = 1; i <= word; i++)
{
x = (u & mask) ? 1 : 0; /* identify the bit */
printf("%d", x); /* print bit value */
mask >>= 1; /* shift mask to the right by 1 bit */
}
}
_Alignof
Interroge l'exigence d'alignement pour le type spécifié. L'exigence d'alignement est une puissance intégrale positive de 2 représentant le nombre d'octets entre lesquels deux objets du type peuvent être alloués. En C, l'exigence d'alignement est mesurée en size_t
.
Le nom du type peut ne pas être un type incomplet ni un type de fonction. Si un tableau est utilisé comme type, le type de l'élément de tableau est utilisé.
Cet opérateur est souvent accessible via la macro de commodité alignof
de <stdalign.h>
.
int main(void)
{
printf("Alignment of char = %zu\n", alignof(char));
printf("Alignment of max_align_t = %zu\n", alignof(max_align_t));
printf("alignof(float[10]) = %zu\n", alignof(float[10]));
printf("alignof(struct{char c; int n;}) = %zu\n",
alignof(struct {char c; int n;}));
}
Sortie possible:
Alignment of char = 1
Alignment of max_align_t = 16
alignof(float[10]) = 4
alignof(struct{char c; int n;}) = 4
Comportement en court-circuit des opérateurs logiques
La mise en court-circuit est une fonctionnalité qui permet d'évaluer certaines parties d'une condition (if / while / ...) lorsque cela est possible. Dans le cas d'une opération logique sur deux opérandes, le premier opérande est évalué (à true ou false) et s'il y a un verdict (le premier opérande est faux avec &&, le premier opérande est vrai avec ||) le deuxième opérande est non évalué.
Exemple:
#include <stdio.h>
int main(void) {
int a = 20;
int b = -5;
/* here 'b == -5' is not evaluated,
since a 'a != 20' is false. */
if (a != 20 && b == -5) {
printf("I won't be printed!\n");
}
return 0;
}
Vérifiez vous-même:
#include <stdio.h>
int print(int i) {
printf("print function %d\n", i);
return i;
}
int main(void) {
int a = 20;
/* here 'print(a)' is not called,
since a 'a != 20' is false. */
if (a != 20 && print(a)) {
printf("I won't be printed!\n");
}
/* here 'print(a)' is called,
since a 'a == 20' is true. */
if (a == 20 && print(a)) {
printf("I will be printed!\n");
}
return 0;
}
Sortie:
$ ./a.out
print function 20
I will be printed!
La mise en court-circuit est importante lorsque vous souhaitez éviter d'évaluer des termes coûteux (en termes de calcul). De plus, cela peut fortement affecter le déroulement de votre programme, comme dans ce cas: Pourquoi ce programme imprime-t-il "forked!" 4 fois?