Dans un projet en embarqué je me retrouve a pas mal manipuler pointeurs et adresses pour accéder a des cases mémoires spécifiques. J'ai donc à plusieurs reprises ce warning qui apparaît dans mon programme :"assignment makes pointer from integer without a cast [-Wint-conversion]".
Je sais qu'en général les warnings c'est pas bon, donc je me demandais si il était grave d'avoir ce genre de warning.
Je précise que je suis complètement conscient de pourquoi ce warning arrive a un endroit précis. Par exemple, j'ai cette fonction :
intptr_t Init_CAN_Tx(UART_HandleTypeDef *huart, CanTxMsgTypeDef *CAN_Tab_TxMessages)
/* Résumé : Fonction qui Initialise et alloue le tableau dynamique des Messages Tx
*
* Arguments : *huart, *CAN_Tab_TxMessages (pas encore alloué)
*
* Retourne : L'adresse de CAN_Tab_TxMessages nouvellement allouée
*/
{
uint8_t UART_ACK, UART_ERR_NB;
int cpt_for = 0, cpt_Data = 0;
/* ALLOCATION DU TABLEAU DE TRAMES CAN */
HAL_UART_Receive(huart, &CAN_nb_trames_Tx_cc, sizeof(CAN_nb_trames_Tx_cc), HAL_MAX_DELAY); // On reçoit la valeur correspondant au nombre de trames CAN à générer
if (CAN_nb_trames_Tx_cc == 0)
{
CAN_Tab_TxMessages = malloc(sizeof(CanTxMsgTypeDef));
}
else if (CAN_nb_trames_Tx_cc <= 50) // On prend au max 50 trames en même temps
{
UART_ACK = 0x6B; //'k'
HAL_UART_Transmit(huart, &UART_ACK, sizeof(UART_ACK), HAL_MAX_DELAY);
CAN_Tab_TxMessages = malloc(CAN_nb_trames_Tx_cc * sizeof(CanTxMsgTypeDef));
}
else // Si ce qu'on nous envoie n'est pas un nombre cohérent, on renvoie une erreur
{
UART_ERR_NB = 0x72; // 'r'
HAL_UART_Transmit(huart, &UART_ERR_NB, sizeof(UART_ERR_NB), HAL_MAX_DELAY);
CAN_nb_trames_Tx_cc = 0;
}
if (CAN_Tab_TxMessages == NULL)
{
return NULL; // Erreur de malloc, à renvoyer par UART et traiter sur l'IHM
}
/* INITIALISATION DES TRAMES CAN Tx */
for (cpt_for=0 ; cpt_for<CAN_nb_trames_Tx_cc ; cpt_for++)
{
CAN_Tab_TxMessages[cpt_for].StdId = 0x00000000;
CAN_Tab_TxMessages[cpt_for].ExtId = 0x00000000;
CAN_Tab_TxMessages[cpt_for].IDE = CAN_ID_STD; //CAN_ID_STD ou CAN_ID_EXT
CAN_Tab_TxMessages[cpt_for].RTR = CAN_RTR_DATA; //CAN_RTR_DATA ou CAN_RTR_REMOTE
CAN_Tab_TxMessages[cpt_for].DLC = 8;
for(cpt_Data = 0 ; cpt_Data<8 ; cpt_Data ++)
{
CAN_Tab_TxMessages[cpt_for].Data[cpt_Data] = 0x00;
}
}
return(CAN_Tab_TxMessages);
}
qui me retourne soir l'adresse d'une structure, soit NULL.
Dans mon main il se passe ceci :
CanTxMsgTypeDef *CAN_Tab_TxMessages = NULL; intptr_t Adresse_CAN_Tx = 0 ; // Adresse du tableau dynamique CAN Tx
Adresse_CAN_Tx = Init_CAN_Tx(huart, CAN_Tab_TxMessages); // On récupère l'adresse où a été créé le tableau CAN Tx
if (Adresse_CAN_Tx != NULL) // On vérifie que l'allocation s'est bien faite
{
CAN_Tab_TxMessages = Adresse_CAN_Tx;
}
Donc là le warning arrive sur la dernière ligne, parce que j'associe à un pointeur sur une structure, une valeur intptr_t. Sauf que ce que je veux dire c'est que le début de la zone mémoire associée à ce pointeur se trouve à Adresse_CAN_Tx. Je comprends que ça semble bizarre au compilateur que j'assigne pas ce qu'il attend, mais c'est bien ce que je veux faire.
Est-ce embêtant ? Si oui, il y aurait-il une manière de mieux faire (pour que le compilateur ne rale plus) ?
Dans un programme C pour desktop, cet avertissement est souvent signe de bug, ou au mieux de gros bidouillage.
Mais pour de l'embarqué quand on sait ce que l'on fait (notamment en utilisant intptr_t) ce n'est pas forcément mauvais signe. Déjà, si l'on doit accéder à une adresse de valeur numérique précise ($dff180 par exemple) et bien ça me paraît inévitable. Cependant, dans le code que tu présentes, je ne vois pas pourquoi Init_CAN_Tx retourne un type entier plutôt que pointeur.
- Edité par Marc Mongenet 6 juillet 2018 à 11:40:29
Cependant, dans le code que tu présentes, je ne vois pas pourquoi Init_CAN_Tx retourne un type entier plutôt que pointeur.
J'ai oublié de coller la dernière ligne de la fonction ...
return(CAN_Tab_TxMessages);
}
Cette fonction permet d'initialiser un tableau de structures en fonction du nombre de structures nécessaires. Ce nombre est reçu par UART.
Comme j'alloue ce tableau de structures dans une fonction, je retourne l'adresse du premier élément afin d'y accéder depuis mon main (pour pouvoir ensuite envoyer mon tableau de structures dans une fonction pour l'implémentation des différentes valeurs dans celui-ci (toujours par UART)).
Je ne comprends pas trop par contre pourquoi tu me dis que Init_Can_Tx retourne un type entier, il ne retourne justement pas une "adresse" avec intptr_t ? (Si je suis complètement à côté de la plaque, merci de me le dire ! Je ne connaissais pas le type intptr_t avant quelques jours ...)
intptr_t est un type entier capable de contenir l'adresse d'un objet. Si tu ne sais pas exactement pourquoi tu l'utilises, alors tu ne devrais pas l'utiliser.
A y regarder de plus prêt, l'utilisation de CAN_Tab_TxMessages dans le code n'a pas de sens. En effet, la valeur de ce paramètre est totalement ignorée, et remplacée par la valeur retournée par malloc. Ce paramètre ne sert à rien et ne devrait pas exister.
Quant au type de retour, avec return CAN_Tab_TxMessages;il faut évidemment qu'il soit CanTxMsgTypeDef *.
- Edité par Marc Mongenet 9 juillet 2018 à 10:38:13
intptr_t est un type entier capable de contenir l'adresse d'un objet. Si tu ne sais pas exactement pourquoi tu l'utilises, alors tu ne devrais pas l'utiliser.
Justement, c'est bien ce que j'avais cru comprendre, c'est donc pour ça que je l'utilisais ici. Je vais changer par CanTxMsgTypeDef * parce que c'est mieux ici, mais j'aimerai cependant savoir pourquoi intptr_t est un mauvais choix ?
Encore merci
edgarjacobs a écrit:
Hello,
intptr_t est un type entier, ce que ta fonction est censée renvoyer.
CAN_Tab_TxMessages est du type pointeur sur une donnée de type CanTxMsgTypeDef, et c'est ce que tu as dit à ta fonction de renvoyer.
Et donc le compilateur n'est pas d'accord.
Pour que cette fonction soit correcte, le code devrait être
Ouais, j'avais pas pensé à ma fonction comme ça lorsque je l'ai codé, dans ma tête c'était juste : "il faut que je renvoie une adresse, quelle est la variable la plus adaptée à ça ?", et j'avais trouvé intptr_t après quelques recherches.
Je vais donc changer mon code pour mettre TxMsgTypeDef *, cependant j'aimerai savoir pourquoi intptr_t n'est pas un bon choix ici alors qu'il est quand même censé contenir une adresse, non ?
....cependant j'aimerai savoir pourquoi intptr_t n'est pas un bon choix ici alors qu'il est quand même censé contenir une adresse, non ?
intptr_t est de type entier, pas de type pointeur sur entier; il est garanti avoir la taille suffisante pour contenir une adresse, mais ça reste un entier.
En laissant ta fonction de type intptr_t, tu aurais pu écrire return (intptr_t)Can_Tab_TxMessages;, à l'appelant de savoir comment manipuler la valeur qu'il reçoit. Mais je ne trouve pas ça propre.
- Edité par edgarjacobs 9 juillet 2018 à 11:06:53
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Tu confonds type et variable : une fonction retourne une valeur, qui a un type. Aucune variable là-dedans. Lorsqu'on déclare une fonction, on déclare notamment le type de retour.
Le type intprt_t étant un type entier, et pas pointeur, il est inutilisable pour faire des opérations de pointeur (déréférencement, arithmétique de pointeur). Son but et de permettre des traiter une adresse comme un entier, ce qui n'a de sens que dans des cas très particuliers, liés au hardware. Or ce n'est pas ce que tu fais.
Il faut encore savoir que l'utilisation cohérente des types en C permet au compilateur de détecter plus de bugs à la compilation. C'est pour cela que les conversions de type sont évitées autant que possible. Et c'est pour cela que les conversions les plus douteuses, entre pointeur et entier par exemple, causent un avertissement bien senti.
- Edité par Marc Mongenet 9 juillet 2018 à 11:14:41
Tu confonds type et variable : une fonction retourne une valeur, qui a un type. Aucune variable là-dedans. Lorsqu'on déclare une fonction, on déclare notamment le type de retour.
C'est un abus de langage que j'ai tendance à faire, très maladroit de ma part, mais je voulais bien parler de type et non de variable....
Marc Mongenet a écrit:
Le type intprt_t étant un type entier, et pas pointeur, il est inutilisable pour faire des opérations de pointeur (déréférencement, arithmétique de pointeur). Son but et de permettre des traiter une adresse comme un entier, ce qui n'a de sens que dans des cas très particuliers, liés au hardware. Or ce n'est pas ce que tu fais.
Ca c'était la partie qui me manquait sur le type intptr_t, et je m'en suis douté (dans le nom, intptr, int), mais j'ai pas fait plus attention que ça.
Mais pour régler ces problèmes là de cast, il faut juste les caster. exemple:
int i = 65;
char c = (char)i;
Bon la c'est un exemple bateau, mais en gros tu mets entre parenthèse le nouveau type que tu souhaites. c'est comme si tu disais que ta variable i se transfome en char.
Après tu peux pas toujours caster
Par exemple tu pourras pas transformer un char** en char
Ben là ça ne résoud pas de problème, parce qu'il n'y a pas de problème. On peut faire directement
char c = i;
Un cas où ça rend service, c'est pour de la programmation à bas niveau. Par exemple on veut accéder à des "ports" dont on sait qu'ils sont en mémoire aux adresses 0x1010, 0x1020 etc. Et que chaque "port" comporte un octet de commande, et un octet de donnée. onpeut donc le représenter par une structure à deux champs.
struct Port { char control; char data; };
Pour définir les ports, on va écrire un truc comme ça
#define PORT_A (* (struct Port *) 0x1010)
#define PORT_B (* (struct Port *) 0x1020)
et ça permettra de faire
PORT_A.data = 'a';
PORT_B.control = 'x';
Explication du tour de magie :
0x1010 est un entier
(struct Port *) .... est une adresse de Port, avec la même valeur
(* ....) est le Port qui est à cette adresse.
- Edité par michelbillaud 12 juillet 2018 à 17:36:08
Si on peut, même pas besoin de conversion explicite pour cela!
Si tu mets un char ** dans un char ça marche pas Xd essaye tu verras. un char ** est composé de char * qui lui est composé de char, certe mais avec les allocations tu pourras pas tout insérer dans un seul char.
michelbillaud a écrit:
Ben là ça ne résoud pas de problème, parce qu'il n'y a pas de problème. On peut faire directement
char c = i;
Oui je suis d’accord mais c'est un exemple bateau ce que je t'ai dit. Mettre entre parenthèses le nouveau type pour caster est bien utile par exemple quand tu envoies une variable a une fonction qui récupère un void*
Mettre entre parenthèses le nouveau type pour caster est bien utile par exemple quand tu envoies une variable a une fonction qui récupère un void*
Bin non, ce n'est pas forcément nécessaire
Si une fonction "récupère" un void*, c'est a dire a un paramètre qui est un pointeur "générique", alors on peut lui passer une adresse de n'importe quel type. Le transtypage est implicite. Exemple :
Crois moi que si, ce que tu prends comme exemple c'est normal qu'il faut pas le cast, mais si tu passes d'une structure que t'as créé à un void * (donc une fonction) ou d'une autre structure a la même fonction, crois moi que t'en as besoin du cast
Si on peut, même pas besoin de conversion explicite pour cela!
Si tu mets un char ** dans un char ça marche pas Xd essaye tu verras. un char ** est composé de char * qui lui est composé de char, certe mais avec les allocations tu pourras pas tout insérer dans un seul char.
#include <stdio.h>
int main()
{
char **ppc = 0;
char c = ppc;
printf("Hello World %d\n", c);
return 0;
}
Après, il est évident qu'il n'y a aucune garantie que toute l'information soit conservée par la conversion de type. De même que quand on convertit un pointeur en short, ou en int, où à peu près n'importe quoi d'autre qu'un intptr_t...
- Edité par Marc Mongenet 13 juillet 2018 à 11:01:29
Crois moi que si, ce que tu prends comme exemple c'est normal qu'il faut pas le cast, mais si tu passes d'une structure que t'as créé à un void * (donc une fonction) ou d'une autre structure a la même fonction, crois moi que t'en as besoin du cast
Désolé mais je ne comprends rien à ce que tu expliques. Je ne vois pas le rapport logique entre un void* et une fonction (les deux peuvent évidemment cohabiter, mais le "donc une fonction" ?)
Je veux bien te croire, quand tu me montreras un exemple, qui a un sens de préférence (parce que passer une structure à une fonction qui attend un pointeur, ou le contraire, ça le fait pas).
- Edité par michelbillaud 13 juillet 2018 à 10:56:50
Je veux bien te croire, quand tu me montreras un exemple, qui a un sens de préférence (parce que passer une structure à une fonction qui attend un pointeur, ou le contraire, ça le fait pas).
Pour l'exemple je te montre ça ce soir ou demain.
Et si, tu peux envoyer une structure dans une fonction, une structure n'est rien d'autre qu'un pointeur (si tu l'as malloc), donc si tu peux envoyer une structure dans une fonction qui reçoit un void* par exemple.
Tu peux transmettre au type void* n'importe qu'elle adresse, que la donnée pointée soit un entier ou une structure peu importe. Mais tu ne peux pas transmettre une structure à un void* (ça ne va pas faire ce que tu veux lol).
Lorsque l'on parle de concepts un peu compliqués et précis, le vocabulaire employé prend une très grande importance. Je trouve que parler des concepts et fonctionnement du C est un bon exercice pour se familiariser avec le bon vocabulaire, et perfectionner sa compréhension des choses.
C'est entre autres pour cela que je lis des forums de programmation tous les jours
Dans la même veine je me demande pourquoi printf() râle lorsqu'on lui donne un pointeur sur type pour le format %p
Les cast me font toujours peur, je ne sais jamais quand ils sont sécurisés et quand ça peut entrainer un comportement indéterminé , je ne sais pas quand il faut/ne faut pas les utiliser... ?
Finalement je ne visualise pas trop comment ça marche, que se passe t'il si l'on cast une valeur 64 bits dans un type 32 bits ? Parfois ça peut marcher ? Et dans l'autre sens 32 vers 64 ?
Je profite du sujet pour essayer de comprendre le mécanisme
Dans la même veine je me demande pourquoi printf() râle lorsqu'on lui donne un pointeur sur type pour le format %p
Les cast me font toujours peur, je ne sais jamais quand ils sont sécurisés et quand ça peut entrainer un comportement indéterminé , je ne sais pas quand il faut/ne faut pas les utiliser... ?
Finalement je ne visualise pas trop comment ça marche, que se passe t'il si l'on cast une valeur 64 bits dans un type 32 bits ? Parfois ça peut marcher ? Et dans l'autre sens 32 vers 64 ?
Je profite du sujet pour essayer de comprendre le mécanisme
La documentation de printf indique que %p attend un pointeur de void. Or un pointeur de type pourrait, théoriquement, avoir une représentation différente, et alors printf recevrait qqch d'incompatible avec ce qui est attendu. C'est très théorique, et j'ai dû insister avec les options de compilation pour obtenir l'avertissement.
Les promotions et conversions de type en C sont pleines de subtilités, et si l'on vraiment savoir ce que l'on fait, on ne coupe pas à la lecture de la norme (C11 chap. 6.3 notamment).
Finalement je ne visualise pas trop comment ça marche, que se passe t'il si l'on cast une valeur 64 bits dans un type 32 bits ? Parfois ça peut marcher ? Et dans l'autre sens 32 vers 64 ?
Je profite du sujet pour essayer de comprendre le mécanisme
alors là... ça va être drôle.
si tu fait 64 dans 32 alors, pour explique on va faire avec 16 c'est plus petit à représenter ^^.
on voit que en accès standard ( sans utiliser de pointeur/@) si tu cast dans un format plus petit tu garde la partie de poids faible, si tu cast dans un format plus grand tu complètes par des 0x00 (pour les valeurs positive et 0xff pour les valeur négatives).
Si maintenant tu utilise des pointeurs et @ comme dans la seconde moitié du programme, tu ne vas toucher que le nombre de bits spécifiés, donc si tu affecte une valeur à la partie basse tu ne changes que la partie basse.
Si tu lis un format plus grand tu vas lire une zone plus grande en RAM, même si ce n'est pas toi qui a alloué ce qu'il y à dans cette zone, donc attention, ça peut vite être ... dangereux et le bordel aussi
- Edité par ox223252 18 juillet 2018 à 9:11:54
la connaissance est une chose qui ne nous appauvrit pas quand on la partage.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
la connaissance est une chose qui ne nous appauvrit pas quand on la partage.
Mon GitHub