下記の演習サイトに、ミニテストを行ってください
- ログインID : 15TE123
- パスワード: 15TE123@ueno.daiichi-koudai.ac.jp
- http://lms.chenlab.net/ (http://lmspress.net/) — メインサイト
- http://lms2.chenlab.net/ — ミラーサイト、演習専用
- http://lms3.chenlab.net/ — ミラーサイト、演習専用
ユビキタスコンピューティング研究ポータル
下記の演習サイトに、ミニテストを行ってください
自動変数のことを局所変数、外部変数のことをグローバル変数(大域変数)ともいいます。
変数のスコープの範囲を図で表してみます。
赤色で囲った部分がグローバル変数の有効範囲です。
青色で囲った部分がローカル変数の有効範囲です。
この図ではローカル変数の寿命を関数内と説明しましたが、正確にはブロック内です。
ブロックとは、{}で囲まれている範囲のことを指しています。
Cで扱う記憶領域は一般に、プログラム領域、静的領域、スタック領域、ヒープ領域の 4つに大別されます。
記憶クラスには、4つあり、自動、静的、外部、レジスタがあります。
記憶クラス | 記憶領域 | スコープ | 記憶クラス指定子 |
自動変数 | スタック | { } の内側 | auto( 不要 ) |
静的変数 | 静的領域 | { } の内側* | static |
外部変数 | 静的領域 | 全域 | extern** |
レジスタ変数 | レジスタまたはスタック | { } の内側 | register |
自動変数は使う前に何らかの値を代入します。このことを、初期化するといいます。
静的変数は初期化の式がなくても、コンパイル時に0に初期化されます。
変数は名前を付けなければ、使えません。C言語では、名前(識別子)は、英字または’_’(アンダースコア)で始まり、英数字または’_’が0個以上続くという決まりになっています。’_’一つまたは二つで始まる変数は、システムに密着したデータに使います。例えば、コンパイラのプログラム等で使われます。
変数に名前を付ける時、外部変数は定義されている所から遠い所でも使われるので、その変数の用途を想像できるような名にすることが推奨されています。
例:timetable, itemindex, thisyear
自動変数は通用範囲が狭いので、何の変数かがすぐわかるので、タイピングの手間を省く意味でも、短くて良いとされています。
例:i, j, m, x, c
その場合でも、その変数がなんであるかを、瞬時に連想できるものが良いとされています。例えば、整数ならば、i、j、k、n、浮動小数点数ならば、x、y、z、文字ならば、cなどです。
#include <stdio.h> void Func(void); /* プロトタイプ宣言 */ void main(void); /* 静的変数 i と自動変数 j の値を表示する */ void Func(void) { /* i の値を読み書きできるのはこの関数の中でだけ */ /* i はずっと存在し続け、 */ static int i; /* func( ) が呼ばれる度に1増える */ auto int j; /* 自動変数は初期化しないと */ /* ゴミの値が入っている */ /* j はゴミの値が表示される */ /* 幾つになるかはわからない */ /* auto は付けなくてもよい */ /* というより、普通は付けない */ printf("i = %d j = %d\n", i, j); /* i, j の値を表示 */ i++; /* 値を1増やす */ j++; /* 値を1増やす */ } /* 関数を抜けたこの時点で、j は破壊される */ void main(void) { Func( ); /* 呼び出す度に i の値は 1 増える */ Func( ); Func( ); }
渡された引数の最大値を返す関数を作成してください。(教科書p299)
ヒント:
今まで、main 関数へ引数なしを意味する
int main(void) と記述、
実は main関数にも引数を渡すことができます。 この main関数に渡す引数のことを「コマンドライン引数」といいます。
main関数へ渡せる引数は、
の 2つです。
一般に
とりあえず、下の例を見てみましょう。
#include <stdio.h> int main(int argc,char *argv[]) { int i; printf("引数の総個数 = %d\n", argc); for (i = 0; i < argc; i++) { printf("%d番目の引数 = %s\n", i, argv[i]); } return 0; }
例題は、command_line.c と保存して、コンパイルだけしてください。
dir コマンドで、command.exe ファイル生成されたと確認。
Ctrl-Qでコマンドプロンプトを起動、 下記のコマンドを入れてください。
> command_line Kitty on your lap
E:\chen\My Documents\C>command_line Kitty on your lap 引数の総個数 = 5 0番目の引数 = E:\chen\My Documents\C\command_line.exe 1番目の引数 = Kitty 2番目の引数 = on 3番目の引数 = your 4番目の引数 = lap E:\chen\My Documents\C>
教科書 P291
コマンドラインで数値を入力し、その合計値を求めるプログラムを作成してください。
プログラムソースファイルは、a8-5-1.c とする。
> a8-5-1 10 15 22 45 9 66 71 4 37 82 合計は 361です
http://chenlab.net/2016/06/07/c-exercises8-pointer-summary/
を改造し、整数は配列ではなく、コマンドライン引数として引き渡し、引数を順に調べ、奇数の値のみ、別の大きさ10の整数型配列に代入しなさい。また、配列の中身と、何個格納したかを画面表示しなさい。
> find_odd_number 10 15 22 45 9 66 71 4 37 82 実行結果 15 45 9 71 37 格納個数 = 5
Visual Studio community 2015 アカウントについて (学内専用)
WebBrowser コントロールを利用した、ブラウザをコントロールアプリケーションの作成手順を紹介します。
Googleの検索ページを利用し、ページに表示されているテキストボックスに文字列が自動的に入力、フォームのサブミットボタンのクリックができ、ページを解析し、含まれるすべてのリンク文字列とそのURLを表示するなどもできる。
このようにプログラムからWebページを操作する場合、事前にそのHTMLのソースをチェックして、操作対象となるHTML要素を明確にしておく必要がある。Googleの検索ページのソースを見ると、フォームの定義部分で次のような記述を見つけることができる。
<form action="/search" name=f >
<input name=q size=55 value="" …… >
<input name=btnG type=submit value="Google 検索" …… >
……
Googleの検索ページ内のフォーム定義部分(抜粋)
この記述から、フォーム(<form>要素)には「f」という名前(name属性)が付けられており、またテキストボックス(<input>要素)には「q」、[Google検索]ボタン(サブミット・ボタン。「type=submit」という属性が付いている<input>要素)には「btnG」という名前が付けられていることが分かる。
Visual Studioを起動し、新しいWindows Formプロジェクトを作成します。
フォーム上次のコントロールを配置してください
ボタン:
リストビュー:
ウェブブラウザ:
Form1_Load
private void Form1_Load(object sender, EventArgs e) { webBrowser1.Navigate("http://www.google.co.jp"); }
button1_Click
private void button1_Click(object sender, EventArgs e) { HtmlElementCollection all = webBrowser1.Document.All; HtmlElementCollection forms = all.GetElementsByName("q"); forms[0].InnerText = "c#"; // テキストボックスに「C#」を入力 }
button2_Click
private void button2_Click(object sender, EventArgs e) { HtmlElementCollection all = webBrowser1.Document.All; HtmlElementCollection forms = all.GetElementsByName("f"); forms[0].InvokeMember("submit"); // フォームのサブミット }
button3_Click
private void button3_Click(object sender, EventArgs e) { HtmlElementCollection all = webBrowser1.Document.All; HtmlElementCollection forms = all.GetElementsByName("btnG"); forms[0].InvokeMember("click"); // ボタンのクリック }
button4_Click
private void button4_Click(object sender, EventArgs e) { HtmlDocument doc = webBrowser1.Document; // リンク文字列とそのURLの列挙 foreach (HtmlElement he in doc.GetElementsByTagName("A")) { string href = he.GetAttribute("href"); // HREF属性の値 string text = he.InnerText; // リンク文字列 if (!string.IsNullOrEmpty(href) && !string.IsNullOrEmpty(text)) { text = text.Replace("\r\n", ""); // 改行文字の削除 string[] row_1 = { href, text }; listView1.Items.Add(new ListViewItem(row_1)); } } }
button1クリックすると、ページに表示されているテキストボックスに「C#」という文字列が自動的に入力される。
button2、button3のボタンをクリックすると、検索が実行されて検索結果ページが表示される。
button4のボタンをクリックすると、ページに含まれるすべてのリンク文字列とそのURLを表示する。
フォーム上textbox などを追加て、検索は「C#」に固定するではなく、textbox などの内容を利用するように機能追加する。
東京魅力PRサークル会員募集中
http://svn.mki.biz/pukiwiki/index.php?u-tokyo
興味があれば、ぜひコメントください。
条件式を前判定して反復制御を行います。
while (条件式) { 繰り返す文; }
例題:
定められた回数だけ反復制御を行います。
for (初期化式; 継続条件式; 後始末) { 文; }
例題:
#include <stdio.h> int main(void) { int i; for (i = 1;i <= 10;i++) { printf("%02d 回目\n",i); } return 0; }
一般的にfor文は「指定した回数分だけ処理を繰り返す」、while文は「指定した条件を満たすまで処理を繰り返す」と言う意味で、for文とwhile文は似ているようで定義されている意味は違います。
while文は、for文の条件式だけと同じです。
逆に言えば、for文はwhile文を拡張した文だと言えるでしょう。
実は、この2つは、同じような使い方をすることが可能です。
while文をfor文のように使う場合、次のようにします。
初期化式; while (継続条件式) { 繰り返す文; 後始末; }
for文をwhile文のように使う場合、次のようにします。
for (;継続条件式;) { 繰り返す文; }
東京魅力PRサークル会員募集中
http://svn.mki.biz/pukiwiki/index.php?u-tokyo
興味があれば、ぜひコメントください。
皆さんは「階乗の計算」を覚えていますか? 5の階乗なら「5!」と表記し,
5! = 5 × 4 × 3 × 2 × 1
と計算します。つまり答えは120です。
0 の階乗:1
1 の階乗:1
2 の階乗:2 × 1
3 の階乗:3 × 2 × 1
– – – – – – –
n の階乗: n × (n – 1) × (n – 2) – – – 3 × 2 × 1
– – – – – – –
n! = n × (n-1) × (n-2) .. × 1
#include <stdio.h> long Factorial1(int n) ; long Factorial2(int n) ; void main(void); /* 階乗を求める(非再帰版) */ long Factorial1(int n) { long p = 1L; if (n < 2) /* nが0か1なら */ return (1L); /* 1を返す */ else if (n > 1) { for ( ; n > 0; n--) p = p * n; return (p); } } void main(void) { int n; long f; printf("整数を入力して下さい\t"); scanf("%d", &n); f = Factorial1(n); printf("Factorial1( ) = %ld\n", f); }
再帰呼び出し (recursive call)とは,関数の中から自分自身を呼び出すプログラミングのテクニックです。再帰呼び出しで階乗の計算式を考えてみると,
n! = n × (n-1)!
と定義できることがわかるでしょうか。
#include <stdio.h> long Factorial1(int n) ; long Factorial2(int n) ; void main(void); /* 階乗を求める(再帰版) */ long Factorial2(int n) { if (n < 2) /* nが0か1なら */ return (1L); /* 1を返す */ else /* nに自分より1小さい自分を掛ける */ return (n * Factorial2(n -1)); } void main(void) { int n; long f; printf("整数を入力して下さい\t"); scanf("%d", &n); f = Factorial2(n); printf("Factorial2( ) = %ld\n", f); }
再帰呼び出しを使う上で、大切なポイントが2つあります。
1.終着点があること
呼び出しが無限に繰り返されてはなりません。再帰とは再び帰ってくるということ。そのためには終着点が必要になります。nの階乗はn=1が終着点です。 |
2.関数を抜けるときは元に戻す
グローバルな変数を用いて状態を把握している場合、関数を抜けるときは関数に入ったときの状態に必ず戻すようにして下さい。そうしないと探索の整合性が失われてしまいます。 |
一般に次のことが言えます。
それでも,再帰プログラムが基本的であると言われるのは何故でしょうか。それは再帰プログラムが大きな力を秘めているからです。つまり
ということです。
このような問題が意外とあるのです。再帰プログラム技法を,身につけたら,プログラミングを行う際,次のような視点で考えるのが良いかもしれません。
再帰が有効な例
再帰呼び出しの方法で、整数を二進数で表示するプログラムを作成
/*
実行例
12345678
101111000110000101001110
*/
ヒント
void print_binary(unsigned int n) { if (n >= 2) print_binary(n/2); printf("%d", n%2); }
Visual Studio community 2015 アカウントについて (学内専用)
今週から、4回分けてWebについて学習する
WebBrowser コントロールを利用した、シンプルなアプリケーションの作成手順を紹介します。
Visual Studioを起動し、新しいWindows Formプロジェクトを作成します。
ツールボックスから”WebBrowser”コントロールをクリックして選択します。選択後フォームデザイナにドラッグ&ドロップしフォームにWebBrowserコントロールを配置します。
フォームにWebBrowserコントロールを配置した直後の状態です。フォームいっぱいにコントロールが配置されます.
フォーム全体にコントロールが広がる原因は、”Dock”プロパティがデフォルトで”Fill”に設定されるためです。Dockプロパティを”None”に変更します。
Dockプロパティを”None”に変更するとコントロールのリサイズができるようになります。
下記のコードを記述します。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WebBrowserControl { public partial class FormMain : Form { public FormMain() { InitializeComponent(); } private void FormMain_Load(object sender, EventArgs e) { webBrowser1.Navigate("http://lmspress.net"); } } }
WebBrowerコントロールのNavigateメソッドを呼び出すことでページの取得と表示ができます。
プロジェクトを実行します。下図のフォームが表示されます。Webサイトのページが読み込まれ表示されました。
Dockプロパティ、またはAnchorプロパティを変更し、ウィンドウのリサイズに伴いWebBrowserコントロールのサイズが変化する設定に変えます。
実行し、ウィンドウをリサイズします。Webページが正しく表示されています。
下記の機能を追加してください
C言語には、全部で3つのループ文が用意されています。
while文、for文、そして、do(デュウ)~while文です。
do~while文は、一般に、次のような書き方で使用します。
do { 繰り返す文; } while (条件式);
do~while文には、1つ大きな利点があります。それは、繰り返し文が、必ず1回は実行されるという点です。入力チェックの時に威力を発揮します。
円の面積を計算するプログラム。半径入力値が間違っているならば、再入力させる。
int main(void) { int r; double s; do { printf("半径?:"); scanf("%d",&r); } while (r < 0); s = r * r * 3.14; printf("面積は %f です。\n",s); return 0; }
http://lmspress.net/ (登録は : 学籍番号 / 学校e-mailアドレス)
詳しくはLMS Pressとは参照してください。
下記のプログラムの入力チェックを追加して下さい。
入力値が間違って誤った月を入力された場合、再入力を行ってください。
#include <stdio.h> int main(int argc, char* argv[]) { int month; printf("何月ですか:"); scanf("%d", &month); switch (month) { case 3: case 4: case 5: puts("春です。"); break; case 6: case 7: case 8: puts("夏です。"); break; case 9: case 10: case 11: puts("秋です。"); break; case 12: case 1: case 2: puts("冬です。"); break; default: puts("そんな月はありませんよ!!"); } return 0; }
ある関数が別の関数を呼び出して、呼び出した関数に制御が戻って来た時に、関数によっては値が返ってくるものがあります。
関数から値を返すには、関数の戻り値の他に、ポインタを使って、関数から呼び出し側の領域を書き換えるも可能です。
関数の定義
戻り値の型 関数名(パラメータリスト) { 文 ... }
関数の戻り値で返す。
この方法では、常に1つの情報しか返すことが出来ません。
2つ以上の情報を返したい時などは不便です。
配列の先頭要素のアドレスを引数にして、上位関数側と下位関数側で配列を共有し、見かけ上、複数のデータを返したようにします。
#include <stdio.h> void waru2(int *p); int main(void) { int i; int dt[] = { 20, 10, 4, 35, 66, 78, -1 }; waru2(dt); /* 配列の先頭要素のアドレスを渡す */ for(i = 0; dt[i] != -1; i++) { printf("%d ", dt[i]); } printf("\n"); return 0; } void waru2(int *p) /* 配列dt のアドレスをポインタp に入れる */ { while(*p != -1) { *p = *p / 2; /* ポインタの中身を 2 で割る */ ++p; } }
複数の変数のアドレスを引数にして、上位関数側と下位関数側でデータを共有し、見かけ上、複数のデータを返したようにします。
#include <stdio.h> void swap(int *x, int *y); int main(void) { int a = 123, b = 456; printf("呼出し前a = %d b = %d\n", a, b); swap(&a,&b); /* 変数a と変数b のアドレスを渡す */ printf("呼出し後a = %d b = %d\n", a, b); return 0; } /* 変数a のアドレスをポインタx に、変数b のアドレスをポインタy に入れる */ void swap(int *x, int *y) { int wk; wk = *x; *x = *y; /* ポインタを使って中身を入換える。*/ *y = wk; }
ポインタを使って、関数から呼び出し側の領域を書き換えるも可能です。しかし、書き換えて困る場合もある。
関数の引数を const
として宣言すると、その関数が引数の値を変更しないことを約束するということを意味する。
関数の引数が const
修飾されている場合、ポインタによって参照される値を変更しようとするとコンパイラが致命的エラーを出す。
void foo(const int *x) { if (x != NULL) { *x = 3; /* コンパイルエラーを出す */ } /* ... */ }
http://lmspress.net/ (登録は : 学籍番号 / 学校e-mailアドレス)
ABクラス:a-8-4-1-1 (p283) 文字列を大文字に変換する関数を作成してください。
CDクラス:a-8-4-2-2 (p287) 三つの整数の中身を入れ替える関数を作成してください。
Visual Studio community 2015 アカウントについて (学内専用)
パズルゲームUIの改進
改進点:
ピースを順番でセットするではなく、あらかじめランダムにセットして、マウス移動できるようにする
// 変数関係の初期化処理 private void initialData() { flg = new bool[9]; data = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; answer = new int[] { -1, -1, -1, -1, -1, -1, -1, -1, -1 }; Random r = new Random(Environment.TickCount); for (int i = 0; i < 100; i++) { int a = r.Next(9); int b = r.Next(9); int n = data[a]; data[a] = data[b]; data[b] = n; } current = 0; answer = data; // chen : ランダムデータを直接結果に playflg = true; clearflg = false; }
// PlayBoxの表示 private void PlayBox_Paint(object sender, PaintEventArgs e) { if (img == null) { return; } Graphics g = e.Graphics; for (int i = 0; i < 9; i++) { // if (flg[i] == false) { continue; } // chen : flg 無視 if (answer[i] == 8) { continue; } // chen : 9個目描画しない int x1 = i % 3; int y1 = i / 3; int x2 = answer[i] % 3; int y2 = answer[i] / 3; Rectangle r1 = new Rectangle(100 * x1, 100 * y1, 100, 100); Rectangle r2 = new Rectangle(100 * x2, 100 * y2, 100, 100); g.DrawImage(img, r1, r2, GraphicsUnit.Pixel); } if (playflg == false) { if (clearflg) { g.DrawString("CLEAR!!", new Font("Impact", 48, FontStyle.Bold), new SolidBrush(Color.Red), new Point(40, 100)); } else { g.DrawString("GAMEOVER...", new Font("Impact", 36, FontStyle.Bold), new SolidBrush(Color.Blue), new Point(20, 200)); } } }
private void swap_answer(int n, int m) { int t; t = answer[m]; answer[m] = answer[n]; answer[n] = t; } // PlayBoxをクリックした時の処理 private void PlayBox_MouseDown(object sender, MouseEventArgs e) { if (playflg == false) { return; } if (img == null) { return; } if (current > 8) { return; } int x = e.X / 100; int y = e.Y / 100; if (x < 0) { return; } if (y < 0) { return; } if (x >= 3) { return; } if (y >= 3) { return; } int n = x + y * 3; //flg[n] = true; //answer[n] = data[current]; //current++; if ((x > 0) && answer[(x - 1) + y * 3] == 8) swap_answer(n, (x - 1) + y * 3); if ((x < 2) && answer[(x + 1) + y * 3] == 8) swap_answer(n, (x + 1) + y * 3); if ((y > 0) && answer[x + (y - 1) * 3] == 8) swap_answer(n, x + (y - 1) * 3); if ((y < 2) && answer[x + (y + 1) * 3] == 8) swap_answer(n, x + (y + 1) * 3); // this.checkGameEnd(); this.Refresh(); }
private void PlayBox_MouseDown(object sender, MouseEventArgs e) { if (playflg == false) { return; } if (img == null) { return; } if (current > 8) { return; } int x = e.X / 100; int y = e.Y / 100; if (x < 0) { return; } if (y < 0) { return; } if (x >= 3) { return; } if (y >= 3) { return; } int n = x + y * 3; //flg[n] = true; //answer[n] = data[current]; //current++; if ((x > 0) && answer[(x - 1) + y * 3] == 8) swap_answer(n, (x - 1) + y * 3); if ((x < 2) && answer[(x + 1) + y * 3] == 8) swap_answer(n, (x + 1) + y * 3); if ((y > 0) && answer[x + (y - 1) * 3] == 8) swap_answer(n, x + (y - 1) * 3); if ((y < 2) && answer[x + (y + 1) * 3] == 8) swap_answer(n, x + (y + 1) * 3); // this.checkGameEnd(); this.checkClear(); // chen : clear check playflg = !clearflg; // chen : clear なら、 not play this.Refresh(); }