C exercises(d) Scope and Storage class

Published by:

変数の通用範囲

自動変数のことを局所変数、外部変数のことをグローバル変数(大域変数)ともいいます。

変数のスコープの範囲を図で表してみます。

c_09_03

赤色で囲った部分がグローバル変数の有効範囲です。
青色で囲った部分がローカル変数の有効範囲です。

この図ではローカル変数の寿命を関数内と説明しましたが、正確にはブロック内です。
ブロックとは、{}で囲まれている範囲のことを指しています。

記憶クラス

Cで扱う記憶領域は一般に、プログラム領域、静的領域、スタック領域、ヒープ領域の 4つに大別されます。

記憶クラスには、4つあり、自動、静的、外部、レジスタがあります。

記憶クラス 記憶領域 スコープ 記憶クラス指定子
自動変数 スタック { } の内側 auto( 不要 )
静的変数 静的領域 { } の内側* static
外部変数 静的領域 全域 extern**
レジスタ変数 レジスタまたはスタック { } の内側 register
  • * { }の外側で宣言された時は、その行以降
  • ** 他のファイルにある時、externを付け、その変数を宣言(メモリを確保)しているファイルでは何も付けない。

自動変数は使う前に何らかの値を代入します。このことを、初期化するといいます。

静的変数は初期化の式がなくても、コンパイル時に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)

ヒント:

 

C exercises(c) Command line arg.

Published by:

今まで、main 関数へ引数なしを意味する

int main(void) と記述、

実は main関数にも引数を渡すことができます。 この main関数に渡す引数のことを「コマンドライン引数」といいます。

コマンドライン引数

main関数へ渡せる引数は、

  1. 引数の総個数
  2. 引数の文字列を指すポインタの配列

の 2つです。

一般に

  • int main(int argc, char *argv[]) と記述し、
  • int argc: 引数の総個数(プログラム名も含む)
  • char *argv[]: 引数の文字列を指すポインタの配列を表します。

とりあえず、下の例を見てみましょう。

(例)

#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 ファイル生成されたと確認。

2016-07-04 (1)

実行結果

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>

演習

演習1

教科書 P291
コマンドラインで数値を入力し、その合計値を求めるプログラムを作成してください。

プログラムソースファイルは、a8-5-1.c とする。

実行結果

> a8-5-1 10 15 22 45 9 66 71 4 37 82
合計は 361です

演習2

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

 

C# exercises (b) Web2 Form

Published by:

Visual Studio community 2015 アカウントについて (学内専用)

  1. WebBrowser (基本)
  2. Form(自動操作)←今週
  3. HttpClient
  4. REST & JSON

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プロジェクトを作成します。

フォーム上次のコントロールを配置してください

ボタン:

  • button1
  • button2
  • button3
  • button4

リストビュー:

  • listView1
    • listView1.View = View.Details;
    • columnHeader1=HERF
    • columnHeader2=text

ウェブブラウザ:

  • webBrowser1

 

スクリーンショット 2016-07-01 11.28.26

イベントハンドラ

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#」という文字列が自動的に入力される。

スクリーンショット 2016-07-01 12.20.27

 

button2、button3のボタンをクリックすると、検索が実行されて検索結果ページが表示される。

スクリーンショット 2016-07-01 12.20.34

button4のボタンをクリックすると、ページに含まれるすべてのリンク文字列とそのURLを表示する。

スクリーンショット 2016-07-01 12.20.47

機能追加

フォーム上textbox などを追加て、検索は「C#」に固定するではなく、textbox などの内容を利用するように機能追加する。

C programming (c) while / for statement

Published by:

東京魅力PRサークル会員募集中

http://svn.mki.biz/pukiwiki/index.php?u-tokyo

興味があれば、ぜひコメントください。

 

while文構文

条件式を前判定して反復制御を行います。

while (条件式) {
  繰り返す文;
}
  • 継続条件が真である間、文を繰り返し実行。
  • 継続条件式がはじめから偽の場合は一度も実行されない。

例題:

for文構文

定められた回数だけ反復制御を行います。

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文は「指定した条件を満たすまで処理を繰り返す」と言う意味で、for文とwhile文は似ているようで定義されている意味は違います。

while文は、for文の条件式だけと同じです。
逆に言えば、for文はwhile文を拡張した文だと言えるでしょう。

実は、この2つは、同じような使い方をすることが可能です。
while文をfor文のように使う場合、次のようにします。

初期化式;
while (継続条件式) {
  繰り返す文;
  後始末;
}

for文をwhile文のように使う場合、次のようにします。

for (;継続条件式;) {
  繰り返す文;
}

演習

  1.  演習4-12 (A, Bクラス) p89
    正の整数値を読み込んで、その桁数を表示するプログラムを作成せよ。
  2.  演習4-13 (C,Dクラス)p93
    1からnまでの和を求めるプログラムを作成せよ。nの値はキーボードから読み込むこと。

 

C exercises(b) Recursive call

Published by:

東京魅力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
– – – – – – –

方法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);

}

 

方法2)関数の中から自分自身を呼び出す

再帰呼び出し (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.関数を抜けるときは元に戻す

グローバルな変数を用いて状態を把握している場合、関数を抜けるときは関数に入ったときの状態に必ず戻すようにして下さい。そうしないと探索の整合性が失われてしまいます。

 再帰は使うべきか

一般に次のことが言えます。

  • 再帰プログラムは計算機に負荷をかけるプログラムである。
  • 時によっては,膨大な負荷をかけることもある。
  • 簡単に非再帰プログラムとして書けるものは再帰プログラムを使うべきではない。

それでも,再帰プログラムが基本的であると言われるのは何故でしょうか。それは再帰プログラムが大きな力を秘めているからです。つまり

  • 再帰プログラムでは簡単に書けるが,非再帰プログラムはかなり複雑なプログラムになってしまうようなものがある。

ということです。

このような問題が意外とあるのです。再帰プログラム技法を,身につけたら,プログラミングを行う際,次のような視点で考えるのが良いかもしれません。

  • まずは,非再帰プログラムで問題を考えてみる。
  • 難しいと判断した場合,再帰プログラムで考えてみる。

再帰が有効な例

  • ハノイの塔
  • データの木構造, 入れ子構造
  • XML文書
  • ファイルシステム

演習

再帰呼び出しの方法で、整数を二進数で表示するプログラムを作成

/*
実行例

12345678
101111000110000101001110

*/

ヒント

void print_binary(unsigned int n)
{
    if (n >= 2) print_binary(n/2);
    printf("%d", n%2);
}

 

C# exercises (a) Web1 WebBrowser

Published by:

Visual Studio community 2015 アカウントについて (学内専用)

今週から、4回分けてWebについて学習する

  1. WebBrowser (基本)
  2. WebBrowser (自動操作)
  3. HttpClient
  4. REST & JSON

WebBrowser コントロール

WebBrowser コントロールを利用した、シンプルなアプリケーションの作成手順を紹介します。

Visual Studioを起動し、新しいWindows Formプロジェクトを作成します。

WebBrowserコントロールの配置

ツールボックスから”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サイトのページが読み込まれ表示されました。
webbrowser

ウィンドウをリサイズ対応

Dockプロパティ、またはAnchorプロパティを変更し、ウィンドウのリサイズに伴いWebBrowserコントロールのサイズが変化する設定に変えます。

実行し、ウィンドウをリサイズします。Webページが正しく表示されています。
webbrowser2

機能拡張

下記の機能を追加してください

  1. アドレスバー:URL入力
  2. 戻るボタン:前のページに戻る
  3. リロードボタン:ページ再読み込む

C programming (b) do statement

Published by:

C言語には、全部で3つのループ文が用意されています。
while文、for文、そして、do(デュウ)~while文です。

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

 

C exercises(a) Return Values

Published by:

ある関数が別の関数を呼び出して、呼び出した関数に制御が戻って来た時に、関数によっては値が返ってくるものがあります。

関数から値を返すには、関数の戻り値の他に、ポインタを使って、関数から呼び出し側の領域を書き換えるも可能です。

値を関数戻り値で返す

関数の定義

戻り値の型 関数名(パラメータリスト) {
  文
  ...
}

関数の戻り値で返す。

この方法では、常に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 として宣言すると、その関数が引数の値を変更しないことを約束するということを意味する。

関数の引数が 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) 三つの整数の中身を入れ替える関数を作成してください。