RSS

カテゴリー別アーカイブ: 未分類

関数のパラメータの参照渡しとはどういうことなのか?

関数呼び出しで、関数(メソッド)のパラメータの渡し方には「値渡し」と「参照渡し」というのがあります。

ただ、Java や JavaScript には言語仕様としての参照渡しはなく、常に値渡しになります。

参照渡しの例ですが、最初に C# を例にして説明します。次のサンプルは2つのパラメータの値を交換するのに参照渡しを使っています。

using System;

public class SwapFunc
{
  static void Main(String[] args)
  {
    int x = 0;
    int y = 1;

    // x と y の値を交換
    Swap(ref x, ref y);

    Console.WriteLine("x = {0:d}, y = {1:d}\n", x, y);
  }

  // x と y の値を交換するメソッド
  static void Swap(ref int x, ref int y)
  {
    var u = x;
    x = y;
    y = u;
  }
}

これの IL (中間コード) は次のようになります。

コメントに動作を書きましたが、正確かどうかは別として、スタックマシン (仮想マシン) を使って、スタックフレーム上のデータをプッシュしたりポップしたりして計算を行っています。

このまま実行すると遅いので、通常、さらにネイティブコードに変換されて実行されます。

参照渡しに関わる部分は Main では、IL_0005 と IL_0006 のあたりです。ここで、スタックフレーム上のデータ x, y のアドレスをスタックに積んでいます。

関数 Swap 側で参照渡しに関わる部分は、ldind 命令を使っているあたりです。この命令は指定したオペランドの内容をアドレスとみなして、そのアドレスの行った先のデータをスタックに積んでいます。

//  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
    // コード サイズ       39 (0x27)
    .maxstack  3
    .locals init (int32 V_0,
             int32 V_1)
    IL_0000:  nop
    IL_0001:  ldc.i4.0  0 をプッシュ。
    IL_0002:  stloc.0  0 をポップしスタックフレーム V_0 に保存
    IL_0003:  ldc.i4.1  1 をプッシュ。
    IL_0004:  stloc.1  1 をポップしてスタックフレーム V_1 に保存
    IL_0005:  ldloca.s   V_0  V_0 のアドレスをプッシュ。
    IL_0007:  ldloca.s   V_1  V_1 のアドレスをプッシュ。
    IL_0009:  call       void SwapFunc::Swap(int32&,  Swap をコール
                                             int32&)
    IL_000e:  nop   何もしない。
    IL_000f:  ldstr      "x = {0:d}, y = {1:d}\n"  フォーマットのアドレスをプッシュ
    IL_0014:  ldloc.0  V_0 の値をプッシュ。
    IL_0015:  box        [mscorlib]System.Int32 スタックのトップをボクシング
    IL_001a:  ldloc.1  V_1 の値をプッシュ。
    IL_001b:  box        [mscorlib]System.Int32 スタックのトップをボクシング
    IL_0020:  call       void [mscorlib]System.Console::WriteLine(string, Console.WriteLine をコール
                                                                  object,
                                                                  object)
    IL_0025:  nop  何もしない。
    IL_0026:  ret  もどる。
  } // end of method SwapFunc::Main

  .method private hidebysig static void  Swap(int32& x,
                                              int32& y) cil managed
  {
    // コード サイズ       12 (0xc)
    .maxstack  2
    .locals init (int32 V_0)
    IL_0000:  nop
    IL_0001:  ldarg.0  引数0をプッシュ。
    IL_0002:  ldind.i4 スタックトップをアドレスとしてその行った先の内容をプッシュ。
    IL_0003:  stloc.0  ローカル変数 0 に格納。
    IL_0004:  ldarg.0  引数0をプッシュ。
    IL_0005:  ldarg.1  引数1をプッシュ。
    IL_0006:  ldind.i4  スタックトップをアドレスとして、その行った先の内容をプッシュ。
    IL_0007:  stind.i4  スタックトップをアドレスとして、その行った先へストア。
    IL_0008:  ldarg.1 引数1をプッシュ。
    IL_0009:  ldloc.0 ローカル変数0をプッシュ。
    IL_000a:  stind.i4 その行った先の内容をプッシュ。
    IL_000b:  ret
  } // end of method SwapFunc::Swap

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // コード サイズ       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


// =============================================================

// *********** 逆アセンブルが完了しました ***********************
// 警告: Win32 リソース ファイル C:\workspace\dotNET\IL\Swap\Swap.res を作成しました。

次に、C 言語の例を見てみます。

C 言語の場合、VM (仮想マシン) は使用しない、つまり、いきなりネイティブコードを生成します。

ここでは、PC で使われている x86 と x64 について述べます。RISC や IBM などでは、基本的考え方は同じはずですが、Calling Convention (呼び出し規約) がそれぞれ異なるのでいろいろ異なります。

まず、C のソースですが、下のようなものとします。

#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;
}

 

これのコンパイル結果は、次のようになります。(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)  0 をスタックフレームのローカルアドレスに保存(x)
	movl	$1, -12(%ebp)  1 をスタックフ
レームのローカルアドレスに保存(y)
	leal	-12(%ebp), %eax y のアドレスを EAX にロード
	movl	%eax, 4(%esp) EAX をスタックに積む。
	leal	-8(%ebp), %eax x のアドレスを EAX にロード
	movl	%eax, (%esp) EAX をスタックに積む。
	call	swap  関数 swap を呼び出す。
	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 後から積んだパラメータ(x)を EAX にロード
	movl	(%eax), %eax  EAX の行った先のデータを EAX にロード
	movl	%eax, -4(%ebp) EAX をローカル変数に保存。
	movl	12(%ebp), %eax 最初に積んだパラメータ(y)を EAX にロード
	movl	(%eax), %edx  EAX の行った先のデータを EDX にロード
	movl	8(%ebp), %eax 後から積んだパラメータ(x)を EAX にロード
	movl	%edx, (%eax) EDX を EAX の行った先に保存
	movl	12(%ebp), %edx 最初に積んだパラメータ(y)を EDX にロード
	movl	-4(%ebp), %eax ローカル変数の内容を EAX にロード
	movl	%eax, (%edx) EAX を EDX の行った先に保存。
	leave
	ret
	.size	swap, .-swap
	.ident	"GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
	.section	.note.GNU-stack,"",@progbits

x64 の場合は、関数の呼び出し規約が異なっており、整数や浮動小数点数は最適化しなくてもレジスタ渡しになります。

これは、x64 では、汎用レジスタが8本から16本に増えたためで、余ったレジスタを関数のパラメータとして活用しています。

	.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 には y のアドレスが入る。
	leaq	-8(%rbp), %rax  RAX には x のアドレスが入る。
	movq	%rdx, %rsi  呼び出し規約に基づいて、パラメータを RSI レジスタ渡しにしている。
	movq	%rax, %rdi  呼び出し規約に基づいて、パラメータを RDI レジスタ渡しにしている。
	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

 

参照渡しができない言語の場合、言語仕様として参照表現がないというだけで、参照渡しは使われています。

パラメータが値渡しされる場合は、汎用レジスタに値がロードできるもの、具体的には整数や浮動小数点数が値渡しになり、汎用レジスタに値がロードできないもの、具体的には配列や一般のオブジェクトが参照渡しになります。

したがって、参照渡しされる型を利用すれば、swap 関数と同様の機能が実現できます。

つぎのコードは JavaScript で参照渡しと同様の機能を実現する例です。配列は参照渡しなので、swap 関数により2つのパラメータの中身が交換されます。

 

'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);


 

広告
 
コメントする

投稿者: : 2017/11/30 投稿先 未分類

 

ASP.NET: アプリケーションのバージョンを表示するには

アプリケーションのプロパティの「アセンブリ情報」で定義したバージョンをWebページに表示したい場合、MVC の場合なら、Views/Shared/_Layout.vbhtml (または .cshtml) に次のように1行追加するとよい。(場所は任意)

Version @ViewContext.Controller.GetType().Assembly.GetName().Version

画面上では、次のような感じで表示される。

 
コメントする

投稿者: : 2017/06/04 投稿先 未分類

 

FREETEL KATANA01 (Windows Mobile) に機種変更したとき

freetel はいわゆる格安スマホで有名ですが、これまで使っていた priori2 (Android 5) から KATANA01 (Windows Mobile) に機種変更しました。

ここで問題が・・・ネットにつながらない!!

新規購入のときは、SIMの設定がされているようですが、機種変更だとSIMの再設定が必要のようです。

これがわかりづらいです。下記のURLを開いて設定を行いますが、素人には苦しいですね。

https://www.freetel.jp/sim/apn_Setting/

特に最後のプロファイルの設定が手動でやらなければならず(登録済み設定が機能しなかったため)、しかも画像の入力項目が空欄になっていて横に設定方法が文言で書かれているのはいただけません。

さらに、MMS??のURLがないとエラーになるのですが、その設定方法がどこにも書かれてありません。適当に、http::/freetel.jp とか入れたら通りましたが、これでいいんでしょうかね。

katana01setting1

KATANA01Setting2.png

 
コメントする

投稿者: : 2017/03/03 投稿先 未分類

 

ASP.NET:HTMLタグを含む文字列がSQL Serverのテーブルに書き込みできない件

ASP.NET ではデフォルトでデータベースへ格納する文字列の検証をしているようで、HTMLのscriptタグを含んだ文字列をテーブルへ書こうとするとエラーになってしまいました。

aspnet_sqlserver_script

これを回避するには、ValidateInput 属性を false にしてやるとよいようです。

        [ValidateInput(false)]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult ViewAddClip(Pixels p)
        {
       ....
        }
 
コメントする

投稿者: : 2017/01/29 投稿先 未分類

 

PowerShell: 配列の内容を指定通りに並べ替える。

JSON ファイルで指定した順番に配列の内容を入れ替えるサンプルです。

# alloc.json で指定された順序で配列を並び替える。
$alloc = get-content -raw alloc.json | ConvertFrom-Json
get-content alloc.json
$data = "ふぐ", "さば", "はぜ", "いわし", "あじ"
$data -join ","
$arr = new-object System.String[] 5

for ($i = 0; $i -lt $alloc.length; $i++) {
  $p = $alloc[$i]
  $arr[$p] = $data[$i]
}

$arr

実行例

allocarray

 
コメントする

投稿者: : 2016/12/21 投稿先 PowerShell, 未分類

 

sed を使って行末コードを変更する

Windows ではテキストファイルの行末は CRLF である。一方、Linux では LF のみである。Windows のテキストファイルを Linux 用にするには sed で

sed “s/\r//” text1.txt

のようにすると、不要な CR を除去できる。

逆に Linux のテキストファイルを Windows 用にするには

sed “s/$/\r/” text2.txt

のようにすると行末つまり LF の前に \r が挿入される。

結果を別のファイルに保存するにはリダイレクトを使用する。結果をそのファイルに上書き保存するには -i スイッチを使用する。

sed -i “s/\r//” text1.txt

 
コメントする

投稿者: : 2016/10/19 投稿先 未分類

 

VMWare のネットワークをNATで接続する

VMWare のネットワーク設定では「ブリッジ接続」が一番簡単ですが、なぜかうまく繋がらなかったり、社内のネットワーク環境では都合が悪かったりすることがあります。

そのような場合、NAT 接続を試してみるといいかもしれません。

VMWare の仮想マシン設定で「ネットワークアダプタ」を選び、ネットワーク接続ラジオボタン群の中から NAT をチェックします。

ゲストOSのネットワーク設定は DHCP にします。例えばUbuntuなら、/etc/network/interfaces を編集して下のようにします。

auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet dhcp

仮想マシンを再起動して、IP アドレスを ip a コマンドなどで確認します。

その IP アドレスで SSH 接続などを行ってみます。

DHCP でなく固定IPアドレスを使いたい場合は、Windows のコントロールパネルで

コントロール パネル\ネットワークとインターネット\ネットワーク接続

を開き、VMWare Network Adapter VMnet8 を開き、「詳細」ボタンをクリックして「ネットワーク接続の詳細」ウィンドウを表示します。

その内容と矛盾しないように、ゲストOS のネットワーク設定を行ってください

例えば下の例で、ネットワークアダプタVMnet8 のIPアドレスが 192.168.44.1 になっているので、ゲートウェイはこのアドレスにしないと外部と接続できません。

VMWareNetworkAdapterVMnet8

 

 

 

 
コメントする

投稿者: : 2016/08/07 投稿先 Linux, 未分類