2009-11-02 7 views
1

Pour l'apprentissage j'ai récemment regardé un assembly existant (en utilisant Reflector) qui utilise Win32 WriteFile. La mise en œuvre est:arithmétique de pointeur et le compilateur C#

Write(IntPtr handleFile, void* bufferData, uint length){ 
void* buffer = bufferData 
while (length > 0) 
{ 
    uint wrtn; 
    if (!WriteFile(handle, buffer, len, out wrtn, IntPtr.Zero)) 
    { 
    // Do some error handling 
    } 
    // This does not compile, because of the cast but also simply because void* does not have += operators (it is unknown size). 
    buffer += (void*)wrtn; 
    len -= wrtn; 
} 

}

Il est en fait les 2 dernières lignes qui sont problématiques ... Pour un, le compilateur se plaint que vous ne pouvez pas lancer uint d'annuler *. De plus, il n'est tout simplement pas possible d'utiliser + = ou même + sur void * car il n'est pas de taille connue.

Write(IntPtr handleFile, void* bufferData, uint length){ 
    byte* buffer = (byte*)bufferData 
    while (length > 0) 
    { 
     uint wrtn; 
     if (!WriteFile(handle, (void*)buffer, len, out wrtn, IntPtr.Zero)) 
     { 
     // Do some error handling 
     } 
     // This works! I can add to a byte* 
     buffer = buffer + wrtn; // I could also have used buffer += wrtn 
     len -= wrtn; 
    } 
} 

Le code ci-dessus fonctionne, mais encore les dernières lignes compilera à:

buffer += (byte*)wrtn; 

Je ne comprends pas pourquoi et voudrais bien savoir pourquoi le compilateur se comporte de cette façon:

Pourquoi génère-t-il la distribution comme ceci (et pourquoi n'est-il pas accepté de le faire dans le code écrit par l'utilisateur)?
  1. Qu'est-ce qui se passe avec les opérateurs + = sur void * dans le premier exemple? Quel code de code original a généré buffer + = (void *) wrtn où buffer est également vide * ????

Répondre

1

Eh bien pour votre deuxième point, void * n'a pas d'information sur la taille, donc le compilateur ne sait pas de combien augmenter le pointeur. Devrait-il augmenter de sizeof (double)? Seulement avec les informations de type sait-il à quoi s'attendre.

Modifier: En fait, cela s'applique également à votre premier point. Le compilateur doit connaître la taille du type de données son incrémentation. Void * n'a pas d'information sur la taille, donc en castant sur un octet *, vous faites savoir au compilateur qu'il doit incrémenter le pointeur de sizeof (byte) * wrtn. Edit2: Avec votre clarification, il semble que vous demandiez pourquoi le réflecteur émet du code comme un vide * au lieu de son type casted correctement (octet *). Cela est probablement dû aux informations de type extraites du type de paramètre et utilisées naïvement dans la méthode. C'est probablement un problème avec Reflector plus que le compilateur. Il est également possible que ce code de pointeur perde son information de type dans l'IL, je ne l'ai pas encore testé, mais il ne peut pas transporter d'informations de typage (outre la taille des données) dans l'IL pour le réflecteur correctement Emit (normal 'sûr' IL devrait toujours avoir cette information de type). Si tel était le cas, Reflector peut par défaut annuler void * ou le type inféré le plus proche.

+0

Merci pour la réponse, mais comme je l'ai déjà signalé dans mon article original, je connais l'ensemble de la taille "inconnue". Ce qui me surprend, c'est que le compilateur génère en quelque sorte void * somepointer + = (void *) uint wrtn (j'ai ajouté le type pour plus de clarté). Cela ne devrait pas être possible (et ne compile certainement pas!) Alors je me demande quel était le code original qui a généré ceci ... – Kris

+0

Donc c'est très probablement une chose de Reflector.Après tout, la tâche des réflecteurs n'est probablement pas facile. Je pourrais comprendre comment le réflecteur recrache les moulages en fonction de l'IL qu'il trouve, donc cette partie de la question que je considère résolue. Cependant, j'ai essayé et essayé de générer une sortie de réflecteur (pour avoir un aperçu) exactement comme dans mon tout premier bloc de code (donc sans utilisation de byte *) et je ne peux pas y arriver ... Vous ne pouvez pas faire beaucoup avec void * donc je suis vraiment curieux de savoir à quoi ressemblerait le code original qui a généré ça! – Kris

+0

Très certainement, il ressemblait à votre copie «fixe» (dans une certaine tolérance). Avez-vous essayé de jeter l'IL et de le trier manuellement? Il n'est pas difficile de voir avec quoi Reflector doit travailler pour que vous puissiez voir d'où viennent les inférences. –

0

Aux fins de l'apprentissage J'ai récemment regardé un assemblage existant (en utilisant réflecteur)

Le seul problème ici est d'utiliser réflecteur - apparemment, ce n'est pas si bon à déduisant le code d'origine C# de IL . L'IL elle-même est correcte, et n'a aucun cast (aucun n'est nécessaire - en IL, vous poussez un pointeur et un argument entier sur la pile, et faites un add/subtract). Le réflecteur est faux.

+0

Alors quel code aurait pu générer le buffer + = (void *) wrtn ???? Je peux imaginer que le réflecteur est faux mais je ne peux pas écrire de code qui génèrerait la ligne juste mentionnée dans le réflecteur ... Et donc ma question reste sans réponse :) – Kris

+0

Essayez 'buffer = (byte *) buffer + wrtn'. –

Questions connexes