C/C++ memset/ZeroMemory

Le problème exposé dans ce sujet a été résolu.

ZeroMemory est une fonction disponible sur Windows seulement. Le code n’est pas pas portable

Au niveau de l’utilité. ZeroMemory assure éviter les effets indésirables des optimisations du compilateur mais sans préciser lesquelles.

Bref, préfère utiliser memset qui est standard et portable. De plus, tu es sûr de bénéficier des optimisations du compilateur. Sauf si tu fais de l’assembleur spécifiquement sous Windows, où du coup ZeroMemory pourrait t’être utile.

PS: La documentation de ZeroMemory signale qu’elle fonctionne différemment de ton exemple. cf: https://msdn.microsoft.com/fr-fr/library/windows/desktop/aa366920(v=vs.85).aspx

+3 -0

Salut,

Au niveau de l’utilité. ZeroMemory assure éviter les effets indésirables des optimisations du compilateur mais sans préciser lesquelles.

ache

Juste pour préciser, la doc dit le contraire, la fonction ZeroMemory() ne les évite justement pas, c’est son équivalent SecureZeroMemory() qui est censée les éviter. ;)

Concernant les optimisations indésirables, je suppose que c’est typiquement le cas où un tableau est mis à zéro avant d’être désalloué, le plus souvent pour effacer des données sensibles (comme des mots de passe). Dans un tel cas, voyant que le tableau n’est plus employé après sa mise à zéro, les compilateurs modernes ont la fâcheuse tendance à supprimer purement et simplement cette mise à zéro.

Pour contrer cela, on peut soit désactiver les optmisations (il vaut mieux un programme fiable et lent qu’un programme rapide dans certains cas) soit employer un tableau et une fonction dédiée qualifiés avec le mot-clé volatile.

1
2
3
4
5
static void mzero(unsigned char volatile *dest, size_t size)
{
        for (size_t i = 0; i < size; size++)
                dest[i] = 0;
}
+0 -0

Hello! Précision :

Bref, préfère utiliser memset qui est standard et portable.

ache

Si l’on se limite uniquement aux systèmes windows, ZeroMemory est supposé être standard et portable. Comme toutes les API Win32 dont on se sert, pour ainsi dire, de moins en moins.

Ce sont les APIs non documentées dont l’utilisation est à proscrire pour les développeurs, car leur interface est sujette à changement d’une version de Windows à une autre. Mais cela n’intéresse que les adeptes de la magie noire. :magicien:

Pour le reste, rien à ajouter… memset est à privilégier si tu pars sur du code multi-plateformes, bien entendu ! :)

Salut.

Vu qu’il y a C++ dans le titre (d’ailleurs « C/C++ » c’est moche), je vais donner le point de vue C++ par rapport à memset (rien à rajouter sur ZeroMemory, tout a déjà été dit.)

En C++ on n’utilise pas memset mais std::fill (documentation). Ça a beaucoup d’avantages, comme le fait d’être type safe et d’être plus général que memset, et ça n’a aucun inconvénient dans les cas d’utilisations similaires à memset, le compilateur pouvant implémenter std::fill en tant que memset dans ces cas là.

Plus de détails ici.

+1 -0

Je me permet de réagir sur l’article que tu cites. Techniquement, memset() n’a pas à être utilisée (et ne devrait même pas être utilisée) pour initialiser un aggrégat, une initialisation partielle suffit. En effet, chaque élément de l’aggrégat se verra assigner le « zéro » de son type soit 0 pour les entiers, .0 pour les flottants et (void *)0 pour les pointeurs.

If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

ISO/IEC 9899:201x, N1570, 6.7.9 Initialization, par. 21, p. 141

Autrement dit, peu importe le nombre et le type d’éléments de ton aggrégat, cette écriture est toujours correcte et fait le café.

1
type_aggregat obj = { 0 };

La nuance c’est que tu ne peux faire ceci qu’à l’initialisation. Toutefois, si cela s’avère nécessaire, il est aussi possible de le réaliser via un appel à memset() (edit pardon, memcpy(), sinon ça marche moins bien) et un littéral aggrégat ou une autre variable (par exemple de classe de stockage statique) précédemment initialisée.

1
memcpy(&obj, &(type_aggregat) { 0 }, sizeof obj);

Édit : pour la structure addrinfo prise en exemple.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include <stdio.h>
#include <stdint.h>


int
main(void)
{
        struct addrinfo ai = { 0 };

        printf("ai_flags: %d\n", ai.ai_flags);
        printf("ai_family: %d\n", ai.ai_family);
        printf("ai_socktype: %d\n", ai.ai_socktype);
        printf("ai_protocol: %d\n", ai.ai_protocol);
        printf("ai_addrlen: %jd\n", (intmax_t)ai.ai_addrlen);
        printf("ai_addr: %p\n", (void *)ai.ai_addr);
        printf("ai_canonname: %p\n", (void *)ai.ai_canonname);
        printf("ai_next: %p\n", (void *)ai.ai_next);
        return 0;
}
1
2
3
4
5
6
7
8
ai_flags: 0                                                                                                                            
ai_family: 0
ai_socktype: 0
ai_protocol: 0
ai_addrlen: 0
ai_addr: 0x0
ai_canonname: 0x0
ai_next: 0x0
+0 -0

Hello! Précision :

Bref, préfère utiliser memset qui est standard et portable.

ache

Si l’on se limite uniquement aux systèmes windows, ZeroMemory est supposé être standard et portable. Comme toutes les API Win32 dont on se sert, pour ainsi dire, de moins en moins.

Ge0

On se comprent. Par standard, je veux dire qui se retrouve dans la norme du langage. Et par portable, je veux dire multi-plateforme.

@Taurre, O_o Je devais pas avoir les yeux en face des trous ! Merci,

Doc :

To avoid any undesired effects of optimizing compilers, use the SecureZeroMemory function.

J’ai lu que la première partie de la phrase biensûr. -_- #boulet

+0 -0

Par exemple la : (Source)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>

int main(void)
{
  int listenfd = 0,connfd = 0;

  struct sockaddr_in serv_addr;

  char sendBuff[1025];  
  int numrv;  

  listenfd = socket(AF_INET, SOCK_STREAM, 0);
  printf("socket retrieve success\n");

  memset(&serv_addr, '0', sizeof(serv_addr));
  memset(sendBuff, '0', sizeof(sendBuff));

  serv_addr.sin_family = AF_INET;    
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 
  serv_addr.sin_port = htons(5000);    

  bind(listenfd, (struct sockaddr*)&serv_addr,sizeof(serv_addr));

  if(listen(listenfd, 10) == -1){
      printf("Failed to listen\n");
      return -1;
  }     

  while(1)
    {      
      connfd = accept(listenfd, (struct sockaddr*)NULL ,NULL); // accept awaiting request

      strcpy(sendBuff, "Message from server");
      write(connfd, sendBuff, strlen(sendBuff));

      close(connfd);    
      sleep(1);
    } 

  return 0;
}

Ligne 23 et 22,

1
2
memset(&serv_addr, '0', sizeof(serv_addr));
memset(sendBuff, '0', sizeof(sendBuff));

A quoi sert ’memset()’ dans cet exemple ? Est-ce que je pourrais directement l’initialiser a 0 en faisant

1
2
struct sockaddr_in serv_addr = { 0 };
char sendBuff = { 0 }; 

?

WTF ?!

Nan c’est pas zéro mais 48. C’est à dire le caractère ’0’.


En gros, utilise l’initialisation, là c’est une erreur de prog d’utiliser memset. Et c’est encore plus une erreur d’utiliser ’0’ au lieu de 0.

Du coup, là c’est du C pas du C++. Donc :

1
2
struct sockaddr_in serv_addr = {};
char sendBuff[255] = ""; 
+1 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte