48040


私は本格的にはPythonしか触れた事が無いのですが、  
私はPythonを学んだ時、静的型付けと動的型付けを以下のように考えていました。  
静的型付言語・・・型宣言をしなくちゃ駄目。  
動的型付言語・・・型宣言をしなくてもOK.  
これでもプログラムを組むには何ら支障はありませんでした。  
だけど、最近さすがにそれじゃいけないだろうと思うようになりました。そのため、自分なりに考えを整理してみる事にしました。

不十分な理解を承知の上で、私の頭の中に、以下のように定めて行きます。  
wikipedia によると、

静的型付言語・・・コンパイル（プログラム実行）前に変数の型が決定されている。  
動的型付言語・・・コンパイル（プログラム実行）後に変数の型が決定される。

静的型付言語の場合、プログラム実行中に型が変更されることはないとします。  
それぞれの変数のアドレスがメモリに割り当てられる。  
そしてプログラムが終了するまで、このメモリは解放されないとします。  
しかし、プログラマが、メモリを解放する処理を明示的に行った場合は別とします。

動的型付言語の場合、プログラム実行中に型が変更されるので、その都度型が調べられ、  
適当な型がその変数に割り当てられるとします。  
（動的型付言語は、この決定のためのプロセスに時間がかかり、処理速度が遅くなる最も大きな原因となる。）

もしその変数が別の型の値が入れば、その型に上書きされる。`Python`の場合、`s`(`str`型)という変数を、`i`(`int`型)という変数に代入すると、`i`は`str`型となる。  
  
元々の`i`という変数には、`s`のメモリが割り当てられ、元々iのあるアドレスは解放される。

[code]

      i = 0;s = "a";
      i is a;-False
      i = s
      i is a;-True
      i;-"a"
    
[/code]

メモリ解放のプロセスは、言語側が自動で行ってくれており、プログラマが行わなくてもいい。  
静的型付言語だと、このような事は出来ないとします。

極端に言えば、静的型付言語と、動的型付言語は、どんな場合でも、このように変数の型が定まるものであるとします。  
頭の中で、このように動的型付言語と、静的型付言語を整理したとします。

しかし、静的型付言語である`Java`では、強制型変換が行われます。また、自動型変換という機能もあります。

この静的型付言語であるはずの`Java`で、メモリが割り当てられているのに、強制的に型が変換されるとすれば、メモリの動きはどのようになるのでしょうか。（強制）という言葉は、解放プロセスもなく、無理やりというニュアンスに思えます。  
型が変換された後の型になるのはわかります。しかし、メモリの動きはいったいどうなっているんでしょうか？メモリはのっぺらな配列であると聞きます。そして、静的型付けの場合、最初に割り当てられたアドレスがギュッと詰まっているとします。その時、その中のアドレスの一部が強制的に書き換えられたとき、もし元の領域よりも大きな領域が割り当てられれば、それは何らかのエラーを発生させるのでしょうか？（別の領域と被ると思うからです。）それとも、元のアドレス自体は一旦解放され、新しく別の領域にメモリが割り当てられるのでしょうか？それとも、被ると思われるアドレスを、そのまま大きくなった分だけスライドさせるのでしょうか？元々そんなに詰めた形では割り当てられていないのでしょうか？  
  
いずれにせよ、このような事が可能であるとするならば、静的型付言語の中にも、動的型付と言える部分があるんだなと感じます。

メモリにはスタックと、ヒープというのがあり、スタックは整列的。ヒープはランダム的に割り当てられると言います。静的型付言語だとしても、この二つを使い分けて移し合いながらメモリの動的管理を行っているのでしょうか？

とすれば、スタックとヒープ両方のメモリが満杯になった時に、なおメモリの領域を要求する時に起るエラーが、スタック・オーバーフローと考えてもよろしいでしょうか？  
  
wikipediaによると、結局静的型付と動的型付の境界線は曖昧だ。と知りました。  
動的型付の場合は、メモリの解放を自動で行ってくれるため、楽ですし、また任意の場所に割り当てればよいと思いますが、静的型付けの場合、これはプログラマが解放と割り当ての責任を負う。という認識で間違いないでしょうか？  
  
何個も聞いてるので、まとめます。（さりげなく項目が増えています。）  
・静的型付言語でも、動的型付が行われれば、メモリはどのように動くのでしょうか。  
・スタックとヒープを使い分けて、メモリの動的管理をしているのですか？  
・スタックとヒープ両方の領域が詰まっていてなおメモリの領域を確保しようとすると、スタック・オーバーフローというエラーが起きるのですか？  
・静的型付言語の場合、元々静的に割り当てられていたメモリを解放して、別のメモリを割り当てるのは、ユーザーの責任によって管理するもので、動的型付け言語ではない場合は、全てそのようになりますか？(`c`の`malloc`関数というのをよく聞きます。）  
・実は言語ごとに異なり、結局のところ、その都度メモリについての扱いを変えるしかないでしょうか。  
・私の頭の中に敷いた理解自体に、どこかおかしいところはありますか？

・もう少し`C`や`C++`などに触れてから出直すべきですか？

  


CC BY-SA 4.0
2018-09-02T13:07:08.580
48026
2018-09-22T11:49:53.107
2018-09-22T11:49:53.107
24284
24284
question
python java c メモリ管理
静的型付と動的型付のメモリの動きを理解したい。


回答にはなりません。アドバイス的な発言です。あしからず。  
小さな組み込み系のプログラムをやっている方には当たり前の事なのですが、  
CPUやメモリー等のハードウエア上には「型」の概念はありません。あるのは

1.メモリーのアドレス  
2.CPUが前提とするバイト並び(エンディアン)  
3.バイト数

の概念だけです。  
スタックの概念がCPUのレジスタとして物理的に存在するCPUがほとんどですが、  
これを使わない実行コードも作成可能で実行も可能なので必須の概念ではないかもしれません。

従って、OSや言語が前提とする「型」の概念は、  
CPU上での実行時には「開始アドレス」とそこからの「バイト数」に還元されて取り扱われます。

まずはコンピューターの原理について調べてみてはどうでしょう。  
そうすると、OSと言語の役割などについて理解が進むかもしれません。


CC BY-SA 4.0
2018-09-03T01:32:36.580
48035
2018-09-03T01:32:36.580
3793
48026
answer


静的/動的型付けと、静的/動的メモリ割り当ては異なる概念なので一緒くたにすると余計に理解ができなくなります。動的型付けを行いたいとき、動的メモリ割り当てがあると楽になるかもしれませんが、動的メモリ割り当てが無くても動的型付けは可能でしょう。

例えば [c](/questions/tagged/c "'c' のタグが付いた質問を表示") の共用体なんてのは動的型付けに使うことができます。

[code]

    union dynamic_content_type {
        int64_t IntegralValue;
        double FloatingValue;
    };
    union dynamic_content_type u;
    
[/code]

この変数 `u`
には整数型の値を格納したり浮動小数点数型の値を格納したりできます。任意の型（の値）を格納することはできないのでこれを動的型付けと呼ぶかどうかは各個人の意見次第だったりしますが。

* * *

変数のための記憶域をどう管理するかは全く別の問題で、

  * コンパイル時点で変数の寿命が管理できるもの ([c](/questions/tagged/c "'c' のタグが付いた質問を表示") でいう自動記憶域期間)  
実装上は、たいていスタック上にとられる

    * コンパイラが自動的に生成消滅させる命令を作る [c](/questions/tagged/c "'c' のタグが付いた質問を表示") / [c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示")
    * 消滅は garbage collection 任せな [c#](/questions/tagged/c%23 "'c#' のタグが付いた質問を表示")
  * オブジェクトの寿命をプログラマが管理するもの ([c](/questions/tagged/c "'c' のタグが付いた質問を表示") でいう動的記憶域期間)  
実装上は、たいていヒープ上にとられる

    * プログラマが 100% 管理する義務がある [c](/questions/tagged/c "'c' のタグが付いた質問を表示") / [c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示")
    * garbage collection が頑張ってくれる [c#](/questions/tagged/c%23 "'c#' のタグが付いた質問を表示") / [java](/questions/tagged/java "'java' のタグが付いた質問を表示")
  * プログラム開始時から終了時まで存在し続けるもの ([c](/questions/tagged/c "'c' のタグが付いた質問を表示") でいう静的記憶域期間)  
実装上は、専用領域にとられる

    * `static` な変数は言語によらずたいていこれ

などなどと、各言語によって仕様が異なります。なおスタックやヒープというのが具体的にどんなものなのかはほとんどの言語仕様書で規定していません（言語仕様制定者は、もちろんよくある実装を意識して仕様を定めていますが）

なので答えられる質問に答えるとして

> ・実は言語ごとに異なり、結局のところ、その都度メモリについての扱いを変えるしかないでしょうか。

Yes

> ・スタックとヒープを使い分けて、メモリの動的管理をしているのですか？

そういう言語処理系もあります（っていうかまあたいていはそうです）

> ・スタックとヒープ両方の領域が詰まっていてなおメモリの領域を確保しようとすると、スタック・オーバーフローというエラーが起きるのですか？

スタックとヒープは別領域です。 Windows などだとスタック領域は固定サイズで、ヒープ領域は使うことを許可された残りメモリ（可変サイズ）です。

  * [スタックを使い切ったら] ヒープが余っていてもスタックオーバーフローとなります。
  * [ヒープを使い切ったら] スタックが余っていても Out of Memory となります。なぜか Out of Heap とは呼ばないようです。

>
> ・静的型付言語の場合、元々静的に割り当てられていたメモリを解放して、別のメモリを割り当てるのは、ユーザーの責任によって管理するもので、動的型付け言語ではない場合は、全てそのようになりますか？(cのmalloc関数というのをよく聞きます。）

型付けと記憶域管理は別物ですので質問に意味がありません。

こういう話はコンピュータ内部の実装の話ですので、深く理解したかったら各種 OS
なり各種言語なりの内側を知る必要があります。っていうか高級言語ってのはそういう細かいところを知らなくてもプログラムが作れるようにしてくれるものなわけです。現在の
[python](/questions/tagged/python "'python' のタグが付いた質問を表示")
をただ使うのに飽きたとかでコンピュータの内側を細かく詳しく知りたいのであれば

> ・もう少しCやC++などに触れてから出直すべきですか？

C はそういう細かいところを記述するための言語だったりするので、そうなりますね。


CC BY-SA 4.0
2018-09-03T05:04:27.347
48039
2018-09-03T05:04:27.347
8589
48026
answer


静的型付け／動的型付けの分類とメモリ管理手法との間に、 **強い関係はありません**
。型をいつ検査するかと、メモリ領域をどのように確保・解放するかは、ある程度独立に決めることができます。たとえば、自動的なメモリ管理の仕組みを備えた静的型付き言語は存在します。

いくつか簡単に具体例を見てみましょう。全てを理解する必要はありません。まずは色々な組み合わせがあることを確認してください。

  * C 言語は静的型付きであり、`malloc` 関数や `free` 関数などを使うことでプログラマが自分でメモリ領域の動的確保・解放を行います。
  * [Go](https://ja.wikipedia.org/wiki/Go_\(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E\)) は静的型付きであり、[ガベージ・コレクション](https://ja.wikipedia.org/wiki/%E3%82%AC%E3%83%99%E3%83%BC%E3%82%B8%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3) (GC) によって、使わなくなったメモリ領域が自動的に解放されます。メモリ領域の動的確保については `new` や `make` を使ってプログラマが自分で行います。
  * [Rust](https://ja.wikipedia.org/wiki/Rust_\(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E\)) は静的型付きです。データの[ライフタイム](https://doc.rust-jp.rs/the-rust-programming-language-ja/1.9/book/lifetimes.html)を明示的に指定することで変数の使われ方をプログラマが書き下すことにより、C 言語でいう `malloc` や `free` レベルの関数は処理系が自動的に呼び出してくれます。
  * Python は動的型付きであり、GC によって自動的にメモリ管理されます。
  * Bash のシェルスクリプトは動的型付きであり、GC を備えていません。また、`malloc` や `free` のような機能は付いていません。ただし文字列と配列を使うことはでき、関数スコープにおけるローカル変数の仕組みや変数自体を削除する `unset` という仕組みはあります。
  * あまり知られていない言語ですが、[dyon](https://github.com/PistonDevelopers/dyon) は部分的に動的型付きであり、GC を備えていません。Rust と同じように、ライフタイムを指定することでメモリが管理されるようになっています。

したがって、質問文のうち特に以下の部分は全体的に **間違っています** 。

>
> 動的型付の場合は、メモリの解放を自動で行ってくれるため、楽ですし、また任意の場所に割り当てればよいと思いますが、静的型付けの場合、これはプログラマが解放と割り当ての責任を負う。という認識で間違いないでしょうか？

型付けが **静的** であるか **動的** であるかというのは、型の検査を実行 **前** にするか実行 **中**
にするかということを言っているに過ぎません。メモリをどのように管理するかは別の話です。たとえば極端な話、メモリ領域を解放する機能なんて無くても良いのです。プログラムの実行中にすぐメモリが足りなくなって実用上困るかもしれませんが、それだけです。

更に、データの置かれるメモリ領域がスタック領域にあるかヒープ領域にあるかという問題は、もっと微妙です。というのも、これは言語処理系の実装方法と深く関係しているからです。詳しくはブログ記事[「メモリとスタックとヒープとプログラミング言語」](https://keens.github.io/blog/2017/04/30/memoritosutakkutohi_puto/)の、特に「プログラミング言語とメモリ」という章の解説が参考になります。

このあたりの事情は、複数のプログラミング言語を学んでみたり、コンパイラを作ってみたりするとより具体的に理解できることだとは思います。たとえば C 言語や
C++
で配列処理や文字列処理をするプログラムを、[メモリリーク](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%AF)しないように書いてみるのは良い練習になりそうな気がします。参考ページとして[「お気楽Ｃ言語プログラミング超入門」](http://www.geocities.co.jp/SiliconValley-
Oakland/1680/linux/clang.html)を挙げておきます。

また、型にまつわる用語のなるべく正確な意味については、[「型推論と型検査、静的な型つけと動的な型つけ、強い型つけと弱い型つけ」](http://d.hatena.ne.jp/sumii/20051018/1129631080)というブログ記事に簡潔にまとまって書かれています。

## 補足1：型を書く／書かないという分類について

上の議論とは別に、質問文中の以下の部分も間違っています。

> 静的型付言語・・・型宣言をしなくちゃ駄目。  
>  動的型付言語・・・型宣言をしなくてもOK.

静的型付き言語の中には[型推論](https://ja.wikipedia.org/wiki/%E5%9E%8B%E6%8E%A8%E8%AB%96)という仕組みを備えているものがあります。型推論とは、明示的に型を書かなくてもコンパイラが自動的に型を決めてくれるというものです。したがって型を書く／書かないという分類は静的型付け／動的型付けとは別軸の分類です。

## 補足2：動的型付けと値の再代入について

厳密には、動的型付きであるからといって、同じ変数に別の型の値を再代入できるとは限らないです。

以下のように、変数 `x` に対して数値を代入した後、文字列を再代入するようなプログラムを考えます。

[code]

    x = 42        # とりあえず数値にしておく。
    # ... しばらく後で ...
    x = "Hello!"  # やっぱり文字列にしよう。
    
[/code]

このようなプログラムを動的型付き言語で書いて実行した際、必ずしも実行時エラーを出さないという訳では **ありません** 。確かに動的型付き言語である
Python では実行時エラーを出しませんが、それは Python が持っている性質のひとつです。たとえば上で紹介した dyon
という言語では、再代入時に型のミスマッチがあると実行時エラーが出ます。

「動的型付け」という言葉はあくまで「プログラムの実行中に型を検査する」ということを言っているだけであり、どのような型検査を行うのかについては言及していません。型の異なるデータが再代入されようとしたとき、変数の型を変えメモリを確保し直して代入するか、型が異なることを検査してエラーとするかは、言語を設計する際に選ぶことができる性質です。

※補足の補足：いくつか調べていると、「どんな型の値でも代入できる」ことを動的型付けの性質として挙げているサイトを見つけました。しかし、動的型付けの定義を「プログラムの実行中に型を検査する仕組み」だとする限り、それだけでは直ちに「どんな値でも代入できる」と言うことはできません。そのような性質は型の[多相性](https://ja.wikipedia.org/wiki/%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0)などとして捉えられる、動的型付けとは別の概念だと私は思います。もちろん、広く使われている動的型付き言語の殆どでは、代入される値の型が元の型と異なっていてもエラーにならないことは事実ですが。


CC BY-SA 4.0
2018-09-03T05:26:16.667
48040
2018-09-03T06:30:52.927
2018-09-03T06:30:52.927
19110
19110
48026
answer