Aujourd'hui, j'ai remarqué un tas de code qui instancie une structure comme ci-dessous alors que ServiceMsg est une structure.
ServiceMsg *msg = (ServiceMsg *)malloc(sizeof * msg);
Je n'ai jamais vu malloc peut être utilisé comme ça. Quelqu'un peut-il m'expliquer cela ??? la taille du tampon est souvent assignée comme ce malloc (sizeof (int) * n) comme je le sais ..
4 Réponses :
Dans l'extrait,
ServiceMsg *msg = malloc(sizeof(*msg));
est le même que
ServiceMsg *msg = (ServiceMsg *)malloc( 1* sizeof(*msg));
ce qui est mieux écrit comme
ServiceMsg *msg = (ServiceMsg *)malloc(sizeof * msg);
Pour élaborer, malloc()
attend une taille comme argument, et dans cet appel, la taille que nous passons est sizeof(*msg)
, c'est-à-dire sizeof (ServiceMsg )
.
... ou ServiceMsg* msg = new ServiceMsg;
ou même auto msg = std::make_unique<ServiceMsg>();
@TedLyngmo uniquement si c'est le RPC, et j'ai gagné; n'utilisez pas malloc
dans le RPC.
Soupir, je n'ai pas vu le double étiquetage de la question ...
Il serait plus clair de réécrire cet appel où l'expression ressemble à une expression de multiplication
malloc(sizeof ( ServiceMsg ) );
comme
malloc(sizeof ( *msg ) );
L'opérateur sizeof
est défini de la manière suivante
sizeof unary-expression sizeof ( type-name )
L'expression *msg
qui déréférence le msg
du pointeur est une expression unaire. Le type de l'expression est déterminé (dans ce cas, il s'agit de ServiceMsg
) et la taille d'un objet de ce type est calculée. L'expression elle-même n'est pas évaluée. Le compilateur détermine uniquement son type.
Vous pouvez mettre cette expression unaire entre parenthèses pour obtenir une expression principale comme
malloc(sizeof *msg);
Dans cet enregistrement, cet enregistrement ressemble à cet enregistrement
malloc(sizeof * msg);
Il y a quelques détails qui pourraient rendre les choses déroutantes:
L'opérateur sizeof
peut être utilisé de deux manières: soit avec des parenthèses sizeof(type)
, mais alors l'argument doit être de type int
. Ou sans parenthèse sizeof expr
, alors l'argument peut être une expression. Mais bien sûr, une expression peut contenir une parenthèse en elle-même ...
Dans votre cas, sizeof *msg
prend la taille de l'expression *msg
, ce qui signifie dé-référencer le pointeur msg
(ce n'est pas l'opérateur de multiplication!) Donc nous nous retrouvons avec une variable temporaire "lvalue" de type ServiceMsg
. Qui a la taille d'un ServiceMsg
.
Normalement, dé-référencer une variable de pointeur sur la même ligne qu'elle est déclarée serait un gros non-non, elle n'est pas initialisée et ne pointe pas encore vers un emplacement raisonnable. Le hic, c'est que l'opérateur sizeof
n'évalue pas réellement (n'exécute) pas son opérande, donc l'expression *msg
n'est jamais exécutée et le dé-référencement ne se produit jamais.
C'est pourquoi le code fonctionne. Ce style est souvent même recommandé, car vous n'avez pas à vous soucier du type sur lequel le pointeur pointe - il est toujours de type* ptr = malloc(sizeof *ptr);
indépendamment de ce que vous allouez.
Le cast en (ServiceMsg*)
dans votre code est superflu.
Merci pour l'explication!! mais qu'est-ce que cela signifie: tapez ptr * = malloc (sizeof * ptr) ?? Je change de type en int et je l'ai essayé dans ma machine, ça bloque lors de la compilation.
@CaiyiZhou C'était une faute de frappe, corrigée. Mais à part cela, je voulais dire qu'au lieu de type
vous pouvez y écrire n'importe quel type valide et cela fonctionne de la même manière.
Nettoyer ça un peu, on obtient
T *p; ... p = calloc( N, sizeof *p );
qui est exactement le même que
T *p = calloc( N, sizeof *p );
où N == 1
. C'est un idiome assez courant en C pour allouer dynamiquement de la mémoire:
T *p; ... p = malloc( N * sizeof *p );
ou
T *p = malloc( N * sizeof *p ); // for any non-function type T
ou
ServiceMsg *msg = malloc(N * sizeof *msg);
ou
ServiceMsg *msg = malloc(sizeof *msg);
Il a l'avantage de ne pas répéter les informations de type, ce qui facilite la maintenance.
Le cast est inutile 1 , et comme le type de l'expression *msg
est ServiceMessage
, sizeof *msg == sizeof( ServiceMessage )
.
Rappelez-vous que sizeof
est un opérateur, pas une fonction - les parenthèses ne sont nécessaires que si l'opérande est un nom de type comme ServiceMessage
ou int
ou char
, ou s'il s'agit d'une expression arithmétique comme 1 + 2
. Puisque sizeof
est un opérateur unaire, il a une priorité plus élevée qu'un opérateur arithmétique, donc sizeof 1 + 2
est analysé comme (sizeof 1) + 2
, ce qui n'est probablement pas ce que vous voulez.
*alloc
char *
au lieu de void *
, donc un cast était nécessaire si la cible n'était pas un char *
. C ++ n'autorise pas la conversion implicite de void *
vers d'autres types de pointeurs, il nécessite donc également un cast, mais si vous écrivez du C ++, vous ne devriez de toute façon pas utiliser la gestion de la mémoire de style C.
C'est
sizeof(*msg)
, pas une multiplication. (Et unnew msg
serait un million de fois meilleur (Et un pointeur intelligent encore un million de fois meilleur (Et il n'y a pas de langage appeléC/C++
)))@john Pourquoi ça?
@ KenY-N Vous savez que je pense que j'aurais repéré cela sans l'espace entre le
*
etmsg
.Pour que cela soit clair pour l'OP. Les parenthèses ne sont requises dans
sizeof(X)
siX
est un type. SiX
est une expression (comme ci-dessus), ils ne le sont pas.