Namespaces
Variants

Implicit conversions

From cppreference.net

Lorsqu'une expression est utilisée dans un contexte où une valeur d'un type différent est attendue, une conversion peut se produire :

int n = 1L; // l'expression 1L a le type long, int est attendu
n = 2.1; // l'expression 2.1 a le type double, int est attendu
char* p = malloc(10); // l'expression malloc(10) a le type void*, char* est attendu

Les conversions ont lieu dans les situations suivantes :

Table des matières

Conversion comme par affectation

  • Dans l'opérateur d'assignation , la valeur de l'opérande de droite est convertie vers le type non qualifié de l'opérande de gauche.
  • Dans l' initialisation scalaire , la valeur de l'expression d'initialisation est convertie vers le type non qualifié de l'objet en cours d'initialisation.
  • Dans une expression d'appel de fonction , vers une fonction ayant un prototype, la valeur de chaque expression d'argument est convertie vers le type des types déclarés non qualifiés du paramètre correspondant.
  • Dans une instruction return , la valeur de l'opérande de return est convertie en un objet ayant le type de retour de la fonction.

Notez que l'affectation réelle, en plus de la conversion, supprime également la plage et la précision supplémentaires des types à virgule flottante et interdit les chevauchements ; ces caractéristiques ne s'appliquent pas à la conversion comme par affectation.

Promotions d'arguments par défaut

Dans une expression d'appel de fonction lorsque l'appel est effectué vers

1) une fonction sans prototype (jusqu'à C23) ,
2) une fonction variadique , où l'expression d'argument est l'un des arguments de fin qui sont appariés avec le paramètre ellipsis.

Chaque argument de type entier subit la promotion entière , et chaque argument de type float est implicitement converti en type double .

int add_nums(int count, ...);
int sum = add_nums(2, 'c', true); // add_nums est appelée avec trois int : (2, 99, 1)

Notez que float complex et float imaginary ne sont pas promus en double complex et double imaginary dans ce contexte.

(depuis C99)

Conversions arithmétiques habituelles

Les arguments des opérateurs arithmétiques suivants subissent des conversions implicites dans le but d'obtenir le type réel commun  , qui est le type dans lequel le calcul est effectué :

1) Si un opérande a un type flottant décimal, l'autre opérande ne doit pas avoir de type flottant standard,

complexe ou imaginaire.

  • Premièrement, si le type de l'un des opérandes est _Decimal128 , l'autre opérande est converti en _Decimal128 .
  • Sinon, si le type de l'un des opérandes est _Decimal64 , l'autre opérande est converti en _Decimal64 .
  • Sinon, si le type de l'un des opérandes est _Decimal32 , l'autre opérande est converti en _Decimal32 .
(depuis C23)
2) Sinon, si un opérande est long double , long double complex , ou long double imaginary (depuis C99) , l'autre opérande est implicitement converti comme suit :
  • type entier ou à virgule flottante réel vers long double
(depuis C99)
3) Sinon, si un opérande est double , double complex , ou double imaginary (depuis C99) , l'autre opérande est implicitement converti comme suit :
  • type entier ou à virgule flottante réel vers double
(depuis C99)
4) Sinon, si un opérande est float , float complex , ou float imaginary (depuis C99) , l'autre opérande est implicitement converti comme suit :
  • type entier vers float (le seul type réel possible est float, qui reste inchangé)
(depuis C99)
5) Sinon, les deux opérandes sont des entiers. Les deux opérandes subissent les promotions entières ; puis, après promotion entière, l'un des cas suivants s'applique :
  • Si les types sont identiques, ce type est le type commun.
  • Sinon, les types sont différents :
    • Si les types ont la même signe (signés ou non signés tous les deux), l'opérande dont le type a le rang de conversion  [1] inférieur est implicitement converti [2] vers l'autre type.
    • Sinon, les opérandes ont des signes différents :
      • Si le type non signé a un rang de conversion supérieur ou égal au rang du type signé, alors l'opérande avec le type signé est implicitement converti vers le type non signé.
      • Sinon, le type non signé a un rang de conversion inférieur au type signé :
        • Si le type signé peut représenter toutes les valeurs du type non signé, alors l'opérande avec le type non signé est implicitement converti vers le type signé.
        • Sinon, les deux opérandes subissent une conversion implicite vers la contrepartie non signée du type de l'opérande signé.
  1. Se référer aux promotions entières ci-dessous pour les règles de classement.
  2. Se référer aux « conversions entières » sous sémantique des conversions implicites ci-dessous.
1.f + 20000001; // int est converti en float, donnant 20000000.00
                // l'addition puis l'arrondi en float donne 20000000.00
(char)'a' + 1L; // d'abord, le char 'a', qui vaut 97, est promu en int
                // types différents : int et long
                // même signe : tous deux signés
                // rang différent : long a un rang supérieur à int
                // par conséquent, int 97 est converti en long 97
                // le résultat est 97 + 1 = 98 de type signed long
2u - 10; // types différents : unsigned int et signed int
         // signe différent
         // même rang
         // par conséquent, signed int 10 est converti en unsigned int 10
         // puisque l'opération arithmétique est effectuée pour les entiers non signés
         // (voir le sujet "Opérateurs arithmétiques"), le calcul effectué est (2 - 10)
         // modulo (2 puissance n), où n est le nombre de bits de valeur de unsigned int
         // si unsigned int fait 32 bits de long et qu'il n'y a pas de bits de remplissage dans sa représentation objet
         // alors le résultat est (-8) modulo (2 puissance 32) = 4294967288
         // de type unsigned int
5UL - 2ULL; // types différents : unsigned long et unsigned long long
            // même signe
            // rang différent : le rang de unsigned long long est supérieur
            // par conséquent, unsigned long 5 est converti en unsigned long long 5
            // puisque l'opération arithmétique est effectuée pour les entiers non signés
            // (voir le sujet "Opérateurs arithmétiques"),
            // si unsigned long long fait 64 bits de long, alors
            // le résultat est (5 - 2) modulo (2 puissance 64) = 3 de type
            // unsigned long long
0UL - 1LL; // types différents : unsigned long et signed long long
           // signe différent
           // rang différent : le rang de signed long long est supérieur
           // si ULONG_MAX > LLONG_MAX, alors signed long long ne peut pas représenter tous
           // les unsigned long donc, c'est le dernier cas : les deux opérandes sont convertis
           // en unsigned long long unsigned long 0 est converti en unsigned long long 0
           // long long 1 est converti en unsigned long long 1 puisque l'opération
           // arithmétique est effectuée pour les entiers non signés
           // (voir le sujet "Opérateurs arithmétiques"),
           // si unsigned long long fait 64 bits de long, alors
           // le calcul est (0 - 1) modulo (2 puissance 64)
           // ainsi, le résultat est 18446744073709551615 (ULLONG_MAX) de type
           // unsigned long long

Le type du résultat est déterminé comme suit :

  • si les deux opérandes sont complexes, le type du résultat est complexe ;
  • si les deux opérandes sont imaginaires, le type du résultat est imaginaire ;
  • si les deux opérandes sont réels, le type du résultat est réel ;
  • si les deux opérandes à virgule flottante ont des domaines de type différents (complexe vs réel, complexe vs imaginaire, ou imaginaire vs réel), le type du résultat est complexe.
double complex z = 1 + 2*I;
double f = 3.0;
z + f; // z reste inchangé, f est converti en double, le résultat est double complex
(depuis C99)

Comme toujours, le résultat d'un opérateur à virgule flottante peut avoir une plage et une précision supérieures à celles indiquées par son type (voir FLT_EVAL_METHOD ).

Note : les opérandes réelles et imaginaires ne sont pas implicitement converties en complexes car cela nécessiterait des calculs supplémentaires, tout en produisant des résultats indésirables dans certains cas impliquant des infinis, des NaNs et des zéros signés. Par exemple, si les réels étaient convertis en complexes, 2.0×(3.0+i∞) serait évalué comme (2.0+i0.0)×(3.0+i∞) ⇒ (2.0×3.0–0.0×∞) + i(2.0×∞+0.0×3.0) ⇒ NaN+i∞ plutôt que le résultat correct 6.0+i∞. Si les imaginaires étaient convertis en complexes, i2.0×(∞+i3.0) serait évalué comme (0.0+i2.0) × (∞+i3.0) ⇒ (0.0×∞ – 2.0×3.0) + i(0.0×3.0 + 2.0×∞) ⇒ NaN + i∞ au lieu de –6.0 + i∞.

(depuis C99)

Remarque : indépendamment des conversions arithmétiques habituelles, le calcul peut toujours être effectué dans un type plus étroit que celui spécifié par ces règles en vertu de la règle du "as-if" .

Transformations de valeurs

Conversion de lvalue

Toute expression lvalue de tout type non-tableau, lorsqu'elle est utilisée dans tout contexte autre que

subit une conversion lvalue  : le type reste le même, mais perd const / volatile / restrict -qualificateurs et atomic propriétés, le cas échéant. La valeur reste la même, mais perd ses propriétés lvalue (l'adresse ne peut plus être prise).

Si la lvalue a un type incomplet, le comportement est indéfini.

Si la lvalue désigne un objet de durée de stockage automatique dont l'adresse n'a jamais été prise et si cet objet n'était pas initialisé (non déclaré avec un initialiseur et aucune assignation n'a été effectuée avant son utilisation), le comportement est indéfini.

Cette conversion modélise la charge mémoire de la valeur de l'objet depuis son emplacement.

volatile int n = 1;
int x = n;            // conversion lvalue sur n lit la valeur de n
volatile int* p = &n; // pas de conversion lvalue : ne lit pas la valeur de n

Conversion de tableau en pointeur

Toute expression lvalue (jusqu'en C99) expression (depuis C99) de type tableau , lorsqu'elle est utilisée dans tout contexte autre que

subit une conversion en un pointeur non-lvalue vers son premier élément.

Si le tableau a été déclaré register , le comportement est indéfini.

Un tableau non-lvalue, ou l'un de ses éléments , n'est pas accessible (jusqu'à C99) , a une durée de vie temporaire (depuis C99) .

int a[3], b[3][4];
int* p = a;      /* conversion vers &a[0] */
int (*q)[4] = b; /* conversion vers &b[0] */
struct S
{
    int a[1];
};
struct S f(void)
{
    struct S result = {{0}}; /* {0} depuis C99 */
    return result;
}
void g(void)
{
    int* p = f().a;    /* erreur jusqu'à C99 ; OK depuis C99 */
    int n  = f().a[0]; /* erreur jusqu'à C99 ; OK depuis C99 */
    f().a[0] = 13;     /* erreur jusqu'à C99 ; UB depuis C99 */
    (void)p, (void)n;
}
int main(void) { return 0; }

Conversion de fonction en pointeur

Toute expression désignatrice de fonction, lorsqu'elle est utilisée dans tout contexte autre que

subit une conversion en un pointeur non-lvalue vers la fonction désignée par l'expression.

int f(int);
int (*p)(int) = f; // conversion vers &f
(***p)(1); // déréférencement répété vers f et reconversion vers &f

Sémantique de conversion implicite

La conversion implicite, que ce soit comme par affectation ou une conversion arithmétique habituelle , consiste en deux étapes :

1) transformation de la valeur (si applicable),
2) une des conversions listées ci-dessous (si elle peut produire le type cible).

Types compatibles

La conversion d'une valeurs de n'importe quel type vers n'importe quel type compatible est toujours une opération neutre et ne modifie pas la représentation.

uint8_t (*a)[10];         // si uint8_t est un typedef pour unsigned char
unsigned char (*b)[] = a; // alors ces types de pointeurs sont compatibles

Promotions entières

La promotion entière est la conversion implicite d'une valeur de tout type entier ayant un rang inférieur ou égal au rang de int ou d'un champ de bits de type _Bool (jusqu'en C23) bool (depuis C23) , int , signed int , unsigned int , en une valeur de type int ou unsigned int .

Si int peut représenter l'ensemble des valeurs du type d'origine (ou la plage de valeurs du champ de bits d'origine), la valeur est convertie en type int . Sinon, la valeur est convertie en unsigned int .

La valeur d'un champ de bits d'un type entier de précision binaire est convertie vers le type entier de précision binaire correspondant. Sinon, les types entiers de précision binaire sont exemptés des règles de promotion des entiers.

(depuis C23)

Les promotions d'entiers préservent la valeur, y compris le signe :

int main(void)
{
    void f(); // déclaration de fonction de style ancien
              // depuis C23, void f(...) a le même comportement concernant les promotions
    char x = 'a'; // conversion entière de int vers char
    f(x); // promotion entière de char vers int
}
void f(x) int x; {} // la fonction attend un int

rang ci-dessus est une propriété de chaque type entier et est défini comme suit :

1) les rangs de tous les types entiers signés sont différents et augmentent avec leur précision : rang de signed char < rang de short < rang de int < rang de long int < rang de long long int
2) les rangs de tous les types entiers signés sont égaux aux rangs des types entiers non signés correspondants
3) le rang de tout type entier standard est supérieur au rang de tout type entier étendu ou type entier à précision de bits (depuis C23) de même taille (c'est-à-dire, le rang de __int64 < rang de long long int , mais le rang de long long < rang de __int128 en raison de la règle (1) )
4) le rang de char est égal au rang de signed char et au rang de unsigned char
5) le rang de _Bool (jusqu'en C23) bool (depuis C23) est inférieur au rang de tout autre type entier standard
6) le rang de tout type énuméré est égal au rang de son type entier compatible
7) le classement est transitif : si le rang de T1 < le rang de T2 et le rang de T2 < le rang de T3 alors le rang de T1 < le rang de T3.
8) le rang d'un type entier signé à précision de bits doit être supérieur au rang de tout type entier standard ayant une largeur inférieure ou de tout type entier à précision de bits ayant une largeur inférieure.
9) le rang de tout type entier à précision de bits par rapport à un type entier étendu de même largeur est défini par l'implémentation.
(depuis C23)
10) tout aspect du classement relatif des types entiers étendus non couvert ci-dessus est défini par l'implémentation.

Note : les promotions d'entiers sont appliquées uniquement

  • dans le cadre des conversions arithmétiques usuelles (voir ci-dessus),
  • dans le cadre des promotions d'arguments par défaut (voir ci-dessus),
  • pour l'opérande des opérateurs arithmétiques unaires + et - ,
  • pour l'opérande de l'opérateur binaire unaire ~ ,
  • pour les deux opérandes des opérateurs de décalage << et >> .

Conversion booléenne

Une valeur de tout type scalaire peut être implicitement convertie en _Bool (jusqu'en C23) bool (depuis C23) . Les valeurs qui sont égales à une expression constante entière de valeur zéro (jusqu'en C23) sont un zéro (pour les types arithmétiques), nul (pour les types pointeur) ou ont un type nullptr_t (depuis C23) sont converties en 0 (jusqu'en C23) false (depuis C23) , toutes les autres valeurs sont converties en 1 (jusqu'en C23) true (depuis C23) .

bool b1 = 0.5;              // b1 == 1 (0.5 converted to int would be zero)
bool b2 = 2.0*_Imaginary_I; // b2 == 1 (but converted to int would be zero)
bool b3 = 0.0 + 3.0*I;      // b3 == 1 (but converted to int would be zero)
bool b4 = 0.0 / 0.0;        // b4 == 1 (NaN does not compare equal to zero)
bool b5 = nullptr;          // b5 == 0 (since C23: nullptr is converted to false)
(depuis C99)

Conversions d'entiers

Une valeur de tout type entier peut être implicitement convertie en tout autre type entier. Sauf dans les cas couverts par les promotions et les conversions booléennes ci-dessus, les règles sont :

  • si le type cible peut représenter la valeur, la valeur reste inchangée,
  • sinon, si le type cible est non signé, la valeur 2 b
    , où b est le nombre de bits de valeur dans le type cible, est soustraite ou ajoutée de manière répétée à la valeur source jusqu'à ce que le résultat tienne dans le type cible. Autrement dit, les entiers non signés implémentent une arithmétique modulaire.
  • sinon, si le type cible est signé, le comportement est défini par l'implémentation (ce qui peut inclure le déclenchement d'un signal).
char x = 'a'; // int -> char, résultat inchangé
unsigned char n = -123456; // cible est non signée, résultat est 192 (c'est-à-dire -123456+483*256)
signed char m = 123456;    // cible est signée, résultat est défini par l'implémentation
assert(sizeof(int) > -1);  // l'assertion échoue :
                           // l'opérateur > demande la conversion de -1 en size_t,
                           // cible est non signée, résultat est SIZE_MAX

Conversions réelles flottantes-entiers

Une valeur finie de tout type flottant réel peut être implicitement convertie en tout type entier. Sauf dans les cas couverts par la conversion booléenne ci-dessus, les règles sont :

  • La partie fractionnaire est supprimée (tronquée vers zéro).
  • Si la valeur résultante peut être représentée par le type cible, cette valeur est utilisée
  • sinon, le comportement est indéfini.
int n = 3.14; // n == 3
int x = 1e10; // comportement indéfini pour un entier 32 bits

Une valeur de tout type entier peut être implicitement convertie en tout type flottant réel.

  • si la valeur peut être représentée exactement par le type cible, elle reste inchangée.
  • si la valeur peut être représentée, mais pas exactement, le résultat est un choix défini par l'implémentation entre la valeur immédiatement supérieure ou inférieure, bien que si l'arithmétique IEEE est supportée, l'arrondi se fait au plus près. Il n'est pas spécifié si FE_INEXACT est déclenché dans ce cas.
  • si la valeur ne peut pas être représentée, le comportement est indéfini, bien que si l'arithmétique IEEE est supportée, FE_INVALID est déclenché et la valeur résultante n'est pas spécifiée.

Le résultat de cette conversion peut avoir une plage et une précision supérieures à ce qu'indique son type cible (voir FLT_EVAL_METHOD ).

Si un contrôle sur FE_INEXACT est nécessaire dans les conversions flottant-vers-entier, rint et nearbyint peuvent être utilisés.

double d = 10; // d = 10.00
float f = 20000001; // f = 20000000.00 (FE_INEXACT)
float x = 1 + (long long)FLT_MAX; // comportement indéfini

Conversions des nombres réels à virgule flottante

Une valeur de tout type réel flottant peut être implicitement convertie en tout autre type réel flottant.

  • Si la valeur peut être représentée exactement par le type cible, elle reste inchangée.
  • Si la valeur peut être représentée, mais pas exactement, le résultat est la valeur immédiatement supérieure ou inférieure la plus proche (en d'autres termes, la direction d'arrondi est définie par l'implémentation), bien que si l'arithmétique IEEE est supportée, l'arrondi se fasse au plus près.
  • Si la valeur ne peut pas être représentée, le comportement est indéfini .

Le résultat de cette conversion peut avoir une plage et une précision supérieures à ce qu'indique son type cible (voir FLT_EVAL_METHOD ).

double d = 0.1; // d = 0.1000000000000000055511151231257827021181583404541015625
float f = d;    // f = 0.100000001490116119384765625
float x = 2 * (double)FLT_MAX; // indéfini

Conversions de types complexes

Une valeur de n'importe quel type complexe peut être implicitement convertie en n'importe quel autre type complexe. La partie réelle et la partie imaginaire suivent individuellement les règles de conversion des types flottants réels.

double complex d = 0.1 + 0.1*I;
float complex f = d; // f est (0.100000001490116119384765625, 0.100000001490116119384765625)

Conversions de types imaginaires

Une valeur de n'importe quel type imaginaire peut être implicitement convertie en n'importe quel autre type imaginaire. La partie imaginaire suit les règles de conversion des types flottants réels.

double imaginary d = 0.1*_Imaginary_I;
float imaginary f = d; // f est 0.100000001490116119384765625*I

Conversions réel-complexe

Une valeur de n'importe quel type flottant réel peut être implicitement convertie en n'importe quel type complexe.

  • La partie réelle du résultat est déterminée par les règles de conversion des types flottants réels.
  • La partie imaginaire du résultat est un zéro positif (ou zéro non signé sur les systèmes non-IEEE).

Une valeur de n'importe quel type complexe peut être implicitement convertie en n'importe quel type flottant réel.

  • La partie réelle est convertie suivant les règles des types flottants réels.
  • La partie imaginaire est ignorée.

Note : dans la conversion complexe-réel, un NaN dans la partie imaginaire ne se propagera pas au résultat réel.

double complex z = 0.5 + 3*I;
float f = z;  // la partie imaginaire est ignorée, f est fixé à 0.5
z = f;        // fixe z à 0.5 + 0*I

Conversions réel-imaginaire

Une valeur de n'importe quel type imaginaire peut être implicitement convertie en n'importe quel type réel (entier ou flottant). Le résultat est toujours un zéro positif (ou non signé), sauf lorsque le type cible est _Bool (jusqu'en C23) bool (depuis C23) , auquel cas les règles de conversion booléenne s'appliquent.

Une valeur de n'importe quel type réel peut être implicitement convertie en n'importe quel type imaginaire. Le résultat est toujours un zéro imaginaire positif.

double imaginary z = 3*I;
bool b = z;  // Conversion booléenne : fixe b à true
float f = z; // Conversion réel-imaginaire : fixe f à 0.0
z = 3.14;    // Conversion imaginaire-réel : fixe z à 0*_Imaginary_I

Conversions complexe-imaginaire

Une valeur de n'importe quel type imaginaire peut être implicitement convertie en n'importe quel type complexe.

  • La partie réelle du résultat est le zéro positif.
  • La partie imaginaire du résultat suit les règles de conversion des types réels correspondants.

Une valeur de n'importe quel type complexe peut être implicitement convertie en n'importe quel type imaginaire.

  • La partie réelle est ignorée.
  • La partie imaginaire du résultat suit les règles de conversion des types réels correspondants.
double imaginary z = I * (3*I); // le résultat complexe -3.0+0i perd la partie réelle
                                // fixe z à 0*_Imaginary_I
(depuis C99)

Conversions de pointeurs

Un pointeur vers void peut être implicitement converti vers et depuis n'importe quel pointeur vers un type d'objet avec la sémantique suivante :

  • Si un pointeur vers un objet est converti en pointeur vers void et inversement, sa valeur est égale au pointeur original.
  • Aucune autre garantie n'est offerte.
int* p = malloc(10 * sizeof(int)); // malloc retourne void*

Un pointeur vers un type non qualifié peut être implicitement converti en pointeur vers la version qualifiée de ce type (en d'autres termes, const , volatile , et restrict peuvent être ajoutés). Le pointeur original et le résultat sont égaux en comparaison.

int n;
const int* p = &n; // &n a le type int*

Toute expression constante entière constante de valeur 0 ainsi que toute expression de pointeur entier de valeur zéro convertie en type void * peut être implicitement convertie en n'importe quel type de pointeur (pointeur vers objet et pointeur vers fonction). Le résultat est la valeur de pointeur nul de son type, garantie d'être inégale à toute valeur de pointeur non nulle de ce type. Cette expression entière ou void * est appelée constante de pointeur nul et la bibliothèque standard fournit une définition de cette constante via la macro NULL .

int* p = 0;
double* q = NULL;

Notes

Bien que le dépassement d'entier signé dans tout opérateur arithmétique soit un comportement indéfini, le dépassement d'un type entier signé dans une conversion d'entier est simplement un comportement non spécifié.

D'autre part, bien que le dépassement d'entier non signé dans tout opérateur arithmétique (et dans la conversion d'entier) soit une opération bien définie et suive les règles de l'arithmétique modulaire, le dépassement d'un entier non signé dans une conversion flottant-vers-entier est un comportement indéfini : les valeurs de type flottant réel qui peuvent être converties en entier non signé sont les valeurs de l'intervalle ouvert ( -1 , Unnn_MAX + 1 ) .

unsigned int n = -1.0; // comportement indéfini

Les conversions entre pointeurs et entiers (sauf d'un pointeur vers _Bool (jusqu'en C23) bool (depuis C23) et (depuis C99) d'une expression constante entière de valeur zéro vers un pointeur), entre pointeurs vers des objets (sauf lorsque l'un ou l'autre est un pointeur vers void) et les conversions entre pointeurs vers des fonctions (sauf lorsque les fonctions ont des types compatibles) ne sont jamais implicites et nécessitent un opérateur de cast .

Il n'existe aucune conversion (implicite ou explicite) entre les pointeurs vers des fonctions et les pointeurs vers des objets (incluant void * ) ou les entiers.

Références

  • Norme C23 (ISO/IEC 9899:2024) :
  • 6.3 Conversions (p: 44-50)
  • Norme C17 (ISO/CEI 9899:2018) :
  • 6.3 Conversions (p: 37-41)
  • Norme C11 (ISO/IEC 9899:2011) :
  • 6.3 Conversions (p: 50-56)
  • Norme C99 (ISO/IEC 9899:1999) :
  • 6.3 Conversions (p: 42-48)
  • Norme C89/C90 (ISO/IEC 9899:1990) :
  • 3.2 Conversions

Voir aussi

Documentation C++ pour Conversions implicites