merci godrik pour la précision.
Salut !
Je voudrais poser une petite question. Donc je travail en ce moment avec SDL avec le langage C, et j'avais depuis un bon bout de temps des erreurs.
En gros je m'explique, j'ai créé une structure(encore incomplète) pour gérer le personnage. La voici :
http://pastebin.com/G23WfBxH
Ensuite, j'ai créé une fonction pour initialiser le personnage :
http://pastebin.com/fVQFiWiU
Dans le main pour tester, j'ai blitté l'image comme ceci :
http://pastebin.com/w6Vf5mUi
Ensuite, à la fin du programme, je voulais désallouer la mémoire pour les sprites du perso. J'écrivais donc ceci :
SDL_FreeSurface(p_player->p_sprites);
Le programme plantait à la fin, et m'affichait un return 3.
J'ai compris en essayant de blitter deux fois ma surface que la mémoire partait toute seule, et donc j'essayais de désallouer rien du tout...
La solution que j'ai trouvée est de mettre le mot clef static devant la déclaration de ma variable Player_s player;.
Est-ce bien comme ça qu'il faut procéder ? Ca fonctionne, mais j'ai l'impression de faire n'importe quoi...
Merci ! ![]()
http://bigocheatsheet.com/
Qu'est-donc que ça ?
Hé, intredasting
C'est un site qui répertorie des problèmes connus de l'algorithmique, et les solutions qu'on y a trouvées, ainsi que l'efficacité de ces dernières...
Le minimum à savoir, c'est qu'il faut éviter les O(n!) ![]()
Yep, merci pour ce site, ça me fait un récap et une remise en tête.
Toujours intéressant en effet ![]()
Sinon pour mon programme, je me demande si ça ne serait pas plus sain d'allouer dynamiquement la mémoire plutôt que d'utiliser une classe static ?
Et via un "destructeur" désallouer le tout...
Si ta fonction plantait sans utiliser le mot-clé "static" c'est parce qu'effectivement la mémoire "partait toute seule" comme tu dis, une variable locale est toujours détruite lorsque tu sors de sa portée (en l’occurrence la fonction ici) donc tu renvoyais une adresse vers un objet inexistant.
Et static n'est effectivement pas la bonne solution ici car lorsque tu utilise ce mot-clé dans une fonction la variable statique ne sera initialisée qu'une seule fois (au premier appel de la fonction ou dès le début de programme, je sais plus trop), elle ne sera pas détruite automatiquement à la sortie de la fonction et à chaque appel de la fonction elle fera référence au même espace mémoire, elle conserve sa valeur d'un appel à l'autre de la fonction en fait.
Donc en gros tu modifies toujours le même objet avec cette fonction et tu renvois toujours l'adresse de ce même objet.
Et oui ça marcherait si tu alloue dynamiquement la mémoire avec new ou malloc, le pointeur local p_player serait quand même détruit mais en revanche l'espace mémoire vers lequel il pointe restera en mémoire jusqu'à ce que tu le libère manuellement.
utilise une variable global. libérer la mémoire avant de quitter un programme est déconseillé.
elite_2009, peux tu expliquer/developper ton raisonnement ? Pourquoi ca serait deconseille? A cause du heap recollection qui prend du temps pour rien?
d’après un dev microsoft, c'est pour éviter de recharger les données paginées sur le disque dur ou déjà déchargées (le code segment est concerné aussi). c'est aussi déconseiller de fermer les handles manuellement. d’après apple ça pompe inutilement la batterie.
pour les autres os je ne saurais dire avec certitude mais il est fort probable que le kernel va libérer la mémoire de manière plus efficace. c'est entre autres pour ca que c++11 a introduit la fonction quick_exit. pourtant encore aujourd'hui, ne pas libérer la mémoire manuellement est considérer comme "bad practice".
pour la variable globale: si la variable est absolument nécessaire au bon fonctionnement du programme, autant la déclarer statiquement au lieu de l'allouer dynamiquement. quake 3 par exemple n’utilise pas de mémoire dynamique, tout est mis dans la data section avec des globals. meme la fonction malloc propre au jeu utilise un buffer statique.
https://github.com/id-Software/Quake-III-Arena/blob/master/code/game/g_mem.c
Ok, je vois d'ou ca vient. Personnellement, je prefere m'assurer que j'ai decharger toutes la memeoire de mon programme de facon a pouvoir m'assurer qu'il n'y a pas de fuite de memoire. Mai son pourrait faire deux modes d'execution different (release/debug) pour conserver les avantages des deux.
PocoIo, dans un certain sens ce n'est pas trop grave pour la fonction là car je ne m'en servirai en principe qu'une fois pour créer le personnage, mais c'est vrai que ça me créera des problèmes pour des monstres par exemple...
elite_2009, je suis un peu étonnée de lire ça, j'ai lu partout jusque maintenant que c'était ultra déconseillé d'utiliser des variables globales. On m'aurait menti ?
je sais pas sken pense les autres mais selon moi ca doit venir des programmeurs c++. les globals en C ne sont pas rares du tout. entre un pointeur qui se ballade et un global je prefere le global. si tu veux absolument une fonction qui retourne un pointeur tu peux déclarer le global avec le mot static pour qu'il ne soit pas exportable.
static Player_s player; // ne peux être utiliser avec extern
void get_player { return &player; }
Dans mon livre "Langage C" de Claude Delannoy, il est indiqué ceci :
"1 - Utilisation des variables globales :
Si l'on en croit les puristes de la programmation structurée, on peut dire que l'utilisation des variables globales constitue le second péché capital de la programmation, le premier étant l'utilisation du goto. Certains programmeurs vont jusqu'à bannir totalement l'utilisation des variables globales, parce qu'elles comportent beaucoup plus d'inconvénients que d'avantages. Résumons brièvement quels sont ces avantages et ces inconvénients, avant d'en tirer quelque conseils en forme de compromis.
1.1 Avantages des variables globales :
Grande facilité d'utilisation :
-------------------------------
A première vue, la facilité d'utilisation d'une variable globale peut apparaître comme un avantage. Il faut cependant bien noter qu'il est plus facile d'introduire une variable globale à laquelle n'importe quelle fonction pourra accéder de façon incontrôlée, plutôt que de réfléchir à l'organisation de son programme.
En fait, le recours à une variable globale permet manifestement de gagner beaucoup de temps lors de la conception et/ou de l'écriture d'une première version d'un programme. En revanche, il en fait généralement perdre beaucoup plus lors de la mise au point et surtout, lors d'une adaptation ultérieure du programme.
En définitive, cette facilité d'utilisation s'avère effectivement être un avantage pour les petits programmes ou pour les programmes jetables, c'est-à-dire à faible durée de vie et qu'on ne cherchera pas à réutiliser. En revanche, elle se transforme en inconvénient majeur pour les très gros programmes. L'utilisation judicieuse de variables globales cachées dans un fichier pourra cependant améliorer considérablement la situation, comme nous le verrons ci-dessous.
Gain de temps d'exécution :
---------------------------
L'échange d'informations par le biais d'arguments nécessite du temps machine pour la recopie des valeurs correspondantes (même lorsqu'il ne s'agit que d'un simple pointeur comme dans le cas des tableaux). Ce n'est pas le cas pour les variables globales.
Facilite la communication entre des fonctions appelées à des niveaux différents :
--------------------
Le recours à des variables globales peut s'avérer indispensable pour résoudre certains problèmes d'échange indirect d'information entre des fonctions. Par exemple, supposons que la fonction f appelle la fonction g qui, elle-même, appelle la fonction h, alors que la fonction g a déjà été écrite et qu'il n'est plus question de la modifier. Si h a besoin d'informations non reçues en arguments, la seule solution consistera à prévoir des variables globales auxquelles accéderont simultanément les fonctions f et h.
Pallie les limitations de mémoire automatique rencontrées sur certaines machines :
------------------------
Sur certaines machines, la mémoire allouée de façon automatique à l'ensemble des variables globales (généralement sous la forme d'une pile) est très limitée, par rapport à l'ensemble de mémoire disponible. On peut ainsi être amené à recourir aux variables globales pour échapper à ces limitations. On notera cependant que cette possibilité doit être mise en concurrence avec l'utilisation de la gestion dynamique, laquelle n'impose généralement pas de telles restrictions.
1.2 Inconvénients des variables globales
Malgré les différents avantages évoqués précédemment, les variables globales présentent un certain nombre d'inconvénients qui, pour peu nombreux qu'ils soient, sont néanmoins majeurs.
Trop grande facilité d'utilisation :
------------------------------------
Comme nous l'avons déjà évoqué au paragraphe précédent, la trop grande facilité d'utilisation des variables globales se transforme en un inconvénient majeur dans le cas des gros programmes.
Plus de notion de paramètre :
-----------------------------
L'usage de variables globales fait disparaître l'aspect paramétrique (arguments) d'une fonction. Ainsi, supposons qu'une fonction echange ait été écrite pour échanger les valeurs de deux variables globales a et b ; pour pouvoir échanger les valeurs de deux autres variables globales c et d, il faudrait tout d'abord les recopier dans a et b, avant d'appeler f, puis recopier a et b dans c et d ; cela perdrait manifestement beaucoup d'intérêt.
Risques d'effets de bord :
--------------------------
Par leur nature même, les variables globales sont accessibles à différentes fonctions d'un même programme. Dans le meilleur des cas, si elles sont cachées dans un fichier source, elles ne sont accessibles qu'aux fonctions de ce fichier source. Dans le cas contraire, elles sont accessibles à toutes les fonctions du programme. Dans ces conditions, on voit que les variables globales sont fortement sujettes à des modifications non désirées ou non prévues qu'on nomme des "effets de bord".
-----------------------------------------
Voilà en gros ce qu'il dit. Il donne après des idées de compromis, tout en disant qu'il est parfois utile d'en utiliser.
Le probleme principale des variables globales sont en effet les effets de bords. Ca rends les programme un peu difficile a maintenir. En particulier quand on veut "repliquer" un composant qui etait aupartavant unique et qui utilisait des variables globales, on se retrouve a retransformer les variables globales en "variables d'instance" pour avoir une copie pour chaque replica.
si tu déclare un global "x" ou "y", je peux comprendre mais avec une convention de nommage claire, utiliser un préfixe par exemple, les effets de bords sont peux probables. je trouve l'exemple "Plus de notion de paramètre" tiré par les cheveux.
"En particulier quand on veut "repliquer" un composant qui etait aupartavant unique et qui utilisait des variables globales, on se retrouve a retransformer les variables globales en "variables d'instance" pour avoir une copie pour chaque replica."
je suis d'accord mais si on construit son programme de manière cohérente on peux limiter cet inconvénient. par exemple avec un tableau global pour les entités
Entity ENT_entities[MAX_ENTITIES];
Entity* ENT_get_entity (); //retourne un pointeur vers un slot libre
a partir de là tu peux faire tes fonctions pour le joueur, monstres et compagnie indépendamment du tableau global.
void PLAYER_init (Entity* ent);
après, pour ce qui est des jeux, je connais que le code des doom et quake donc mon avis est influencer.
"je trouve l'exemple "Plus de notion de paramètre" tiré par les cheveux. "
Ahah. comme j'aimerai que cette exemple soit tire par les cheveux. C'est un cas completement classique d'utilisation des variables globales dans les vieux codes fortran... Quand tu veux rendre la lib thread safe pour faire des appel dans un programme parallele, tu pleures pendant des heures sur les choix de design du truc.
godrik
Posté le 8 mai 2013 à 03:24:10
Quand tu veux rendre la lib thread safe pour faire des appel dans un programme parallele, tu pleures pendant des heures sur les choix de design du truc.
Comment ça pourrait se résoudre en général, en C?
Tu laisses l'utilisateur de la lib te passer un pointeur de fonction qui sert d'interface d'accès?