Bei Funktionsaufrufen gibt es zwei Möglichkeiten, Funktionsparameter (Methodenparameter) zu übergeben: "Wert übergeben" und "Referenz übergeben".
Java und JavaScript werden jedoch nicht als Sprachspezifikation als Referenz übergeben, sondern immer als Wert.
Dies ist ein Beispiel für die Übergabe als Referenz, aber nehmen wir zunächst C # als Beispiel. Im folgenden Beispiel wird die Referenzübergabe verwendet, um die Werte zweier Parameter auszutauschen.
using System;
public class SwapFunc
{
static void Main(String[] args)
{
int x = 0;
int y = 1;
//Tauschen Sie die x- und y-Werte aus
Swap(ref x, ref y);
Console.WriteLine("x = {0:d}, y = {1:d}\n", x, y);
}
//Eine Methode zum Austauschen der Werte von x und y
static void Swap(ref int x, ref int y)
{
var u = x;
x = y;
y = u;
}
}
Der IL (Zwischencode) hierfür lautet wie folgt:
Ich habe das Verhalten in den Kommentaren geschrieben, aber ob es korrekt ist oder nicht, ich verwende eine Stapelmaschine (virtuelle Maschine), um Daten für Berechnungen auf den Stapelrahmen zu pushen und zu platzieren.
Wenn Sie es so ausführen, wie es ist, ist es langsam, daher wird es normalerweise in nativen Code konvertiert und ausgeführt.
In Main liegt der Teil, der sich auf das Übergeben als Referenz bezieht, um IL_0005 und IL_0006. Hier werden die Adressen der Daten x und y auf dem Stapelrahmen auf den Stapel geladen.
Der Teil, der sich auf das Übergeben als Referenz auf der Swap-Seite der Funktion bezieht, wird mit der Anweisung ldind erstellt. Diese Anweisung betrachtet den Inhalt des angegebenen Operanden als Adresse und lädt die Daten, an die diese Adresse gesendet wurde, auf den Stapel.
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020
// Copyright (c) Microsoft Corporation. All rights reserved.
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly Swap
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module Swap.exe
// MVID: {DDFC4E86-9AF5-4AC3-927E-267DAB2AEE1E}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x04700000
// =============== CLASS MEMBERS DECLARATION ===================
.class public auto ansi beforefieldinit SwapFunc
extends [mscorlib]System.Object
{
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
//Codegröße 39(0x27)
.maxstack 3
.locals init (int32 V_0,
int32 V_1)
IL_0000: nop
IL_0001: ldc.i4.0 'Drücken Sie 0.
IL_0002: stloc.0 'Pop 0 und Stack Frame V._Speichern Sie auf 0
IL_0003: ldc.i4.1 'Schieben Sie einen.
IL_0004: stloc.1 'Pop 1 und Stapelrahmen V._Speichern Sie auf 1
IL_0005: ldloca.s ' V_0 V_0-Adresse drücken.
IL_0007: ldloca.s ' V_1 V_Drücken Sie die Adresse 1.
IL_0009: call void SwapFunc::Swap(int32&, 'Rufen Sie Swap an
int32&)
IL_000e:Nein, tu nichts.
IL_000f: ldstr "x = {0:d}, y = {1:d}\n" 'Formatadresse drücken
IL_0014: ldloc.0 ' V_Drücken Sie einen Wert von 0.
IL_0015: box [mscorlib]System.Int32 'Boxen Sie die Oberseite des Stapels
IL_001a: ldloc.1 ' V_Drücken Sie einen Wert von 1.
IL_001b: box [mscorlib]System.Int32 'Boxen Sie die Oberseite des Stapels
IL_0020: call void [mscorlib]System.Console::WriteLine(string, ' Console.Rufen Sie WriteLine auf
object,
object)
IL_0025: nop 'nichts tun.
IL_0026: ret 'Geh zurück.
} // end of method SwapFunc::Main
.method private hidebysig static void Swap(int32& x,
int32& y) cil managed
{
//Codegröße 12(0xc)
.maxstack 2
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldarg.0 'Push-Argument 0.
IL_0002: ldind.i4 'Schieben Sie den Inhalt des Ziels mit dem Stapel oben als Adresse.
IL_0003: stloc.0 'In lokaler Variable 0 gespeichert.
IL_0004: ldarg.0 'Push-Argument 0.
IL_0005: ldarg.1 'Push-Argument 1.
IL_0006: ldind.i4 'Schieben Sie den Inhalt des Ziels mit dem Stapel oben als Adresse.
IL_0007: stind.i4 'Speichern Sie am Ziel mit dem Stapel oben als Adresse.
IL_0008: ldarg.1 'Push-Argument 1.
IL_0009: ldloc.0 'Lokale Variable 0 drücken.
IL_000a: stind.i4 'Schieben Sie den Inhalt des Ziels.
IL_000b: ret
} // end of method SwapFunc::Swap
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
//Codegröße 7(0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method SwapFunc::.ctor
} // end of class SwapFunc
// =============================================================
// ***********Die umgekehrte Montage ist abgeschlossen***********************
//Warnung:Win32-Ressourcendatei C.:\workspace\dotNET\IL\Swap\Swap.Ich habe res erstellt.
Schauen wir uns als nächstes ein Beispiel für die C-Sprache an.
In der Sprache C werden VMs (virtuelle Maschinen) nicht verwendet, dh sie generieren plötzlich nativen Code.
In diesem Abschnitt werden die in PCs verwendeten x86 und x64 beschrieben. Bei RISC, IBM usw. sollte die Grundidee dieselbe sein, sie unterscheiden sich jedoch, da die Aufrufkonventionen unterschiedlich sind.
Zuerst die C-Quelle, die wie die folgende aussieht.
#include <stdio.h>
void swap(int*, int*);
int main(int argc, char* argv[]) {
int x = 0;
int y = 1;
swap(&x, &y);
printf("x = %d, y = %d\n", x, y);
return 0;
}
void swap(int* x, int* y) {
int u = *x;
*x = *y;
*y = u;
}
Das Kompilierungsergebnis davon ist wie folgt. (Für x86)
.file "Swap.c"
.section .rodata
.LC0:
.string "x = %d, y = %d\n"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $36, %esp
movl $0, -8(%ebp) #Speichern Sie 0 an der lokalen Adresse des Stapelrahmens(x)
movl $1, -12(%ebp) #Speichern Sie 1 unter der lokalen Adresse des Stapelrahmens(y)
leal -12(%ebp), %eax #Laden Sie die Adresse von y in EAX
movl %eax, 4(%esp) #Laden Sie EAX auf den Stapel.
leal -8(%ebp), %eax #Laden Sie die Adresse von x in EAX
movl %eax, (%esp) #Laden Sie EAX auf den Stapel.
call swap #Rufen Sie die Swap-Funktion auf.
movl -12(%ebp), %eax
movl -8(%ebp), %edx
movl %eax, 8(%esp)
movl %edx, 4(%esp)
movl $.LC0, (%esp)
call printf
movl $0, %eax
addl $36, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.globl swap
.type swap, @function
swap:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl 8(%ebp), %eax #Parameter werden später geladen(x)In EAX geladen
movl (%eax), %eax #Laden Sie die Daten des Ziels von EAX in EAX
movl %eax, -4(%ebp) #Speichern Sie EAX in lokalen Variablen.
movl 12(%ebp), %eax #Zuerst geladene Parameter(y)In EAX geladen
movl (%eax), %edx #Laden Sie die Daten des Ziels von EAX in EDX
movl 8(%ebp), %eax #Parameter werden später geladen(x)In EAX geladen
movl %edx, (%eax) #Speichern Sie EDX dort, wo EAX hingegangen ist
movl 12(%ebp), %edx #Zuerst geladene Parameter(y)In EDX geladen
movl -4(%ebp), %eax #Laden Sie den Inhalt lokaler Variablen in EAX
movl %eax, (%edx) #Speichern Sie EAX dort, wo EDX hingegangen ist.
leave
ret
.size swap, .-swap
.ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
.section .note.GNU-stack,"",@progbits
Für x64 sind die Funktionsaufrufkonventionen unterschiedlich, und Ganzzahlen und Gleitkommazahlen werden ohne Optimierung durch Register geleitet.
Dies liegt daran, dass x64 die Anzahl der Universalregister von 8 auf 16 erhöht hat und die überschüssigen Register als Funktionsparameter verwendet werden.
.file "Swap.c"
.section .rodata
.LC0:
.string "x = %d, y = %d\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
movl $0, -8(%rbp)
movl $1, -4(%rbp)
leaq -4(%rbp), %rdx #RDX enthält die Adresse von y.
leaq -8(%rbp), %rax #RAX enthält die Adresse von x.
movq %rdx, %rsi #Die Parameter werden basierend auf der Aufrufkonvention im RSI-Register übergeben.
movq %rax, %rdi #Die Parameter werden basierend auf der Aufrufkonvention an das RDI-Register übergeben.
call swap
movl -4(%rbp), %edx
movl -8(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.globl swap
.type swap, @function
swap:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -24(%rbp)
movq %rsi, -32(%rbp)
movq -24(%rbp), %rax
movl (%rax), %eax
movl %eax, -4(%rbp)
movq -32(%rbp), %rax
movl (%rax), %edx
movq -24(%rbp), %rax
movl %edx, (%rax)
movq -32(%rbp), %rax
movl -4(%rbp), %edx
movl %edx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size swap, .-swap
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
.section .note.GNU-stack,"",@progbits
Für Sprachen, die nicht als Referenz übergeben werden können, wird die Übergabe als Referenz einfach verwendet, weil die Sprachspezifikation keinen Referenzausdruck enthält.
Wenn Parameter als Wert übergeben werden, können Werte in Allzweckregister geladen werden, insbesondere Ganzzahlen und Gleitkommazahlen, die als Wert übergeben werden können, und Werte können nicht in Allzweckregister, insbesondere Arrays und Allgemein, geladen werden Das Objekt wird als Referenz übergeben.
Daher kann der als Referenz übergebene Typ verwendet werden, um dieselbe Funktion wie die Swap-Funktion zu erreichen.
Der folgende Code ist ein Beispiel für die Verwendung von JavaScript, um die gleiche Funktionalität wie beim Übergeben von Referenzen zu erzielen. Da das Array als Referenz übergeben wird, tauscht die Swap-Funktion den Inhalt der beiden Parameter aus.
'use strict';
const swap = (x, y) => {
let u = x[0];
x[0] = y[0];
y[0] = u;
};
var a = [0];
var b = [1];
swap(a, b);
console.log("%i, %i", a, b);
–