コンテンツにスキップ

宿題:命令型言語1#

命令型言語#宿題1

Homework 約30分 答え

次のC言語プログラムを適当な実行環境で実行し,その出力結果を答えよ.さらに各出力行に対して,何番目のarrayの右辺値とアドレスを出力しているかを答えよ."%p"はアドレスを16進で表示する書式指定である.

#include <stdio.h>

int main() {
    int array[3] = {1, 2, 3};
    for (int i = 0; i < 3; i++) {
        printf("%d %p\n", array[i], &array[i]);
    }
    printf("\n");

    int *p = array;
    int offset = 1;
    printf("%d %p\n", *(p+offset), p+offset);

    *(p+offset) += 5;
    printf("%d %p\n", *(p+offset), p+offset);

    p++;
    printf("%d %p\n", *p, p);
}

以下の書式をコピーして回答せよ.左側が出力結果,//以降が値の意味である.

1 0x7ffd76b36d3c  // array[0]の右辺値とアドレス
???

回答フォーム

Answer

1 0x7ffd76b36d3c  // array[0]の右辺値とアドレス
2 0x7ffd76b36d40  // array[1]の右辺値とアドレス
3 0x7ffd76b36d44  // array[2]の右辺値とアドレス

2 0x7ffd76b36d40  // array[1]の右辺値とアドレス
7 0x7ffd76b36d40  // array[1]の右辺値とアドレス
7 0x7ffd76b36d40  // array[1]の右辺値とアドレス

*(p+offset)は配列ポインタの典型操作である.基準点となるポインタにオフセット(補正値)を加えることで,任意の配列要素にアクセスできる.

ポインタで混乱している人は以下の図をよく考えること.

C言語のポインタ操作
int x = 5;
int *p = &x;

メモリ領域に対する操作
     &x    &p
           
  ... 70 71 72 73 74 ...    // メモリのアドレス(値は仮)
[ ... 05 00 70 00 AC ... ]  // メモリの値(右辺値)
           
      x     p
     *p

Q&A#

誤字#

  • <ジャンプ文> ::= goto <ラベル>";" | ...
  • 低姿勢停止性問題
  • -32,767~32,768-32,768~32,767

thx

試験の過去問の答えを隠す機能が欲しい#

OK

アドレスの桁を増やしたほうが読みやすいかも#

確かに.来年考えます.

"Goto Statement Considered Harmful"はDijkstraの言ではないらしい#

編集者が直したそうです.とはいえ当該記事の著者はDijkstraです.

コンパイラがおとなしい言語は?#

Pythonはかなりおとなしいです(インタプリタ型なので当然といえば当然).実行するまでエラーに気付けないともいえる.

java
String s;
s = 1;  // Javaはコンパイル時点で型エラー
python
s = "aaa"
s + 1  # 実行時に型エラー(コンパイルには成功してしまう)

gotoが推奨される状況はあるか?#

ない.新しい言語はもはやgotoを採用していない.

コンピュータ側からするとむしろgotoを使った方が親切ではないか#

コンパイラを楽にさせても大したメリットはない.

コンピュータよりも人に親切にやろう.プログラミングのほとんどの問題は人が原因です.プログラミングにおいては人を最優先に扱って下さい.まずは可読性です.そのために抽象度の高い表現を積極的に使って下さい.

ポインタの*と正規表現の*は全く別物で単なる記号の重複か?#

Yes

int *pint* pか?#

好みの問題なのでどちらでも.参考:

動的型付け言語と静的型付け言語で実行速度の差はあるか?#

ある.動的は型推論が必要なため遅くなります.ただ様々な工夫(JITコンパイル等)でカバーしています.

型推論の際にintlongをどう見分けているのか?#

まず「推論」なので不確実性を伴う処理だと認識して下さい.そのうえで互換性のある型をどう扱うかは言語によります.RustやHaskellは安全な型を選びます.この場合longの方が広いので安全です.

goto以外にも低水準で不要説が唱えられている構文はあるか?#

良い質問 👍👍👍

今日の講義で扱う動的メモリ確保malloc() free()は低水準命令です.

また,不要とは言わなくても抽象度の高い記述ができるパターンがたくさんあります.例えばJavaによるループ処理を考えよう.文字列の配列arrの全要素を大文字化して出力する処理を考える.

まずはよくあるfor文.汎用的だが低水準だといえる.

String arr[] = {"a", "ab", "aaba"};
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i].toUpperCase());  // A AB AABA
}

拡張for文で置き換える.誘導変数iが減るだけでなく可読性も上がる.添字のバグも減る.

String arr[] = {"a", "ab", "aaba"};
for (String s: arr) {
    String tmp = s.toUpperCase();
    System.out.println(s.toUpperCase());  // A AB AABA
}

Stream APIとラムダ式を使うとベスト.命令的ではなく宣言的に書く.まさに関数型の考え.

String arr[] = {"a", "ab", "aaba"};
Stream.of(arr).map(String::toUpperCase)
              .forEach(System.out::println);  // A AB AABA

break continueの代わりにgotoを使うのはアリか?#

ナシです.構造化プログラミングの考えには則ってますが,それでもgotoは極力避けるべきです.関数 + returnを使おう.

gotoを使いたくなるよくあるケースは二重ループの脱出です.

for (int i = 0; ...) {
    for (int j = 0; ...) {
        if (...) break;  // 二重ループを脱けたいがiのループを抜けられない
        ...
    }
}

フラグを使えば抜けられるがダサい.

for (int i = 0; ...) {
    for (int j = 0; ...) {
        if (...) { flag = 1; break; }  // break前にflagを立てる
        ...
    }
    if (flag) break;  // う~ん
}

gotoなら解決するがダサい.

for (int i = 0; ...) {
    for (int j = 0; ...) {
        if (...) goto EXIT;
        ...
    }
}
EXIT:

関数 + returnを使おう.

void doSomething() {
    for (int i = 0; ...) {
        for (int j = 0; ...) {
            return;
        }
    }
}

今日の授業は関数の話をします.

変数のメモリ領域はどのような規則で確保されるのか?ランダムか?#

今日の授業でやります.

各研究室の情報を知る手段は?#

B4の研究室配属の際に見学がありますが,かなり遅いとは思います.自主的に動くしかない.調べる・見学を依頼する等やってください.夏休み・冬休みはおすすめです.