Download : sample-001.c
/*
* 2020/06/19 sample-001.c
*/
/*
* "Hello, World" again
*/
#include <stdio.h>
int main ( int argc, char *argv[] ) {
printf ( "Hello, World\n" ); /* "Hello, World" という文字列と改行 ( "\n" ) を表示する */
return 0;
}
$ ./sample-001.exe Hello, World $
Download : sample-002.c
/*
* 2020/06/19 sample-002.c
*/
/*
* プログラム・ノックアウト (1)
*/
#include <stdio.h>
int main ( int argc, char *argv[] ) {
/* まず、printf をノックアウト */
return 0;
}
$ ./sample-002.exe $
Download : sample-003.c
/* * 2020/06/19 sample-003.c */ /* * プログラム・ノックアウト (2) */ /* * main 関数もなくしたら ? */ /* ここには何もない */
$ cc -c sample-003.c $ cc -o sample-003.exe sample-003.o /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 0 が無効なシンボル索引 11 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 1 が無効なシンボル索引 12 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 2 が無効なシンボル索引 2 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 3 が無効なシンボル索引 2 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 4 が無効なシンボル索引 11 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 5 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 6 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 7 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 8 が無効なシンボル索引 12 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 9 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 10 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 11 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 12 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 13 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 14 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 15 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 16 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 17 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 18 が無効なシンボル索引 13 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 19 が無効なシンボル索引 21 を持っています /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_line): 再配置 0 が無効なシンボル索引 2 を持っています /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: 関数 `_start' 内: (.text+0x20): `main' に対する定義されていない参照です collect2: error: ld returned 1 exit status $
Download : sample-004.c
/*
* 2020/06/19 sample-004.c
*/
/*
* プログラム・ノックアウト (3)
*/
/*
* #include <stdio.h> // #include を無くした
*/
int main ( int argc, char *argv[] ) {
printf ( "Hello, World\n" ); /* この行で「警告」が出る */
return 0;
}
$ cc -c sample-004.c sample-004.c: In function ‘main’: sample-004.c:15:3: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default] printf ( "Hello, World\n" ); /* この行で「警告」が出る */ ^ $
Download : sample-005.c
/*
* 2020/06/19 sample-005.c
*/
/*
* プログラム・ノックアウト (4)
*/
/*
* #include <stdio.h> // #include を無くした
*/
int main ( int argc, char *argv[] ) {
/* printf も使わないと、「警告」はなくなった */
return 0;
}
$ ./sample-005.exe $
Download : sample-006.c
/*
* 2020/06/19 sample-006.c
*/
/*
* プログラム・ノックアウト (5)
*/
void main ( void ) { /* int を void に変更, 引数宣言もなくして良い */
/* return 0; を削って、空っぽにする */
}
/*
最も短い C 言語プログラムの実現
*/
$ ./sample-006.exe $
Download : sample-007.c
/*
* 2020/06/19 sample-007.c
*/
/*
* プログラム・ノックアウト (6)
*/
/*
* #include の代りに printf の extern 宣言
*/
extern int printf ( const char *message, ... );
/* 「extern void printf ( char *message );」 ではない ?? */
/*
* main
*/
int main ( int argc, char *argv[] ) {
printf ( "Hello, World\n" ); /* 「警告」が消えた */
return 0;
}
$ ./sample-007.exe Hello, World $
Download : sample-008.c
/*
* 2020/06/19 sample-008.c
*/
#include <stdio.h>
/*
* 文字列を三度表示する : 関数定義の例
*/
void threeTimesPrint ( char *message ) {
printf ( message ); /* 一度目 */
printf ( message ); /* ニ度目 */
printf ( message ); /* 三度目 */
}
/*
* main
*/
int main ( int argc, char *argv[] ) {
threeTimesPrint ( "Hello\n" ); /* 関数呼出しの例 */
threeTimesPrint ( "こんにちは\n" );
return 0;
}
$ ./sample-008.exe Hello Hello Hello こんにちは こんにちは こんにちは $
Download : sample-009.c
/*
* 2020/06/19 sample-009.c
*/
#include <stdio.h>
/*
* echo_char ( char ch )
* char ch -- 引数で指定された文字
* 引数で指定された文字を『』付で出力
*/
void echo_char ( char ch ) {
printf ( "『" );
putchar ( ch ); /* 引数で指定された文字を出力する */
printf ( "』" );
}
/*
* main
*/
int main ( int argc, char *argv[] ) {
printf ( "何か文字を入れてから[Enter]キーを押す : " );
echo_char ( getchar() ); /* getchar() で文字を読込み、それを出力 */
putchar ( '\n' );
return 0;
}
A
$ ./sample-009.exe < sample-009.in 何か文字を入れてから[Enter]キーを押す : A 『A』 $
Download : sample-010.c
/*
* 2020/06/19 sample-010.c
*/
/*
* extern 宣言
*/
extern void println ( char *message );
extern void nprinttail ( char *tail, char *message );
/*
* n 回数の繰返し
*/
int main ( int argc, char *argv[] ) {
println ( "毎回改行「Hello」: " );
nprinttail ( "\n\n\n", "Hello" );
println ( "3 回目, 7 回目だけ改行「Hello」: " );
nprinttail ( "12\n456\n", "Hello" );
return 0;
}
$ ./sample-010.exe $
Download : sample-011.c
/*
* 2020/06/19 sample-011.c
*/
#include <stdio.h>
/*
* 文字の扱い
*/
int main ( int argc, char *argv[] ) {
printf ( "abc\n" ); /* 「abc(改行)」とでる */
printf ( "abc\n" + 1 ); /* 「bc(改行)」とでる ( 1 を加えると短くなる ) */
putchar ( 'A' ); /* 「A」とでる */
putchar ( '\n' ); /* 「(改行)」とでる */
putchar ( 'A' + 1 ); /* 文字に 1 を加えると ? */
putchar ( '\n' ); /* 「(改行)」とでる */
return 0;
}
$ ./sample-011.exe abc bc A B $
Download : sample-012.c
/*
* 2020/06/19 sample-012.c
*/
/*
* extern 宣言
*/
extern void printcharln ( char ch );
/*
* 文字の扱い(2)
*/
int main ( int argc, char *argv[] ) {
printcharln ( 'A' + 1 ); /* 'B' になった */
printcharln ( 'B' + 1 ); /* 'C' になる */
printcharln ( 'A' + 1 + 1 ); /* これも 'C' になる */
printcharln ( 'A' + 0 ); /* これはもちろん 'A' */
printcharln ( 'A' + 10 ); /* 'A', 'B', .., 'J', 'K' になるはず */
printcharln ( 'A' + 25 ); /* 'Z' !! */
printcharln ( 'Z' + 1 ); /* ??? */
return 0;
}
$ ./sample-012.exe $
Download : sample-013.c
/*
* 2020/06/19 sample-013.c
*/
/*
* extern 宣言
*/
extern void nprintcharln ( char *n, char ch );
extern void printcharln ( char ch );
extern void println ( char *string );
/*
* 文字の扱い(3)
*/
int main ( int argc, char *argv[] ) {
println ( "'A' から 10 個 :" );
nprintcharln ( "1234567890", 'A' );
println ( "'U' から 10 個 :" );
nprintcharln ( "1234567890", 'U' );
println ( "'k' から 10 個 :" );
nprintcharln ( "1234567890", 'k' );
println ( "'0' から 10 個 :" );
nprintcharln ( "1234567890", '0' );
println ( "'9' + 1 は.. :" );
printcharln ( '9' + 1 ); /* 残念ながら "10" ではない */
return 0;
}
$ ./sample-013.exe $
Download : sample-014.c
/*
* 2020/06/19 sample-014.c
*/
/*
* extern 宣言
*/
extern void println ( char *string );
extern void printonedigit ( int n );
extern void printonedigitln ( int n );
/*
* 整数
*/
int main ( int argc, char *argv[] ) {
println ( "整数値 0 の出力" );
printonedigitln ( 0 ); /* 「'0'」 でも 「"0"」 でもなく 「0」 */
println ( "整数値 9 の出力" );
printonedigitln ( 9 ); /* 一桁は OK */
println ( "整数値 11 の出力" );
printonedigitln ( 11 ); /* 上手く行かない */
println ( "整数値 1 + 1 の出力" );
printonedigitln ( 1 + 1 ); /* やっと計算がでてきた */
println ( "整数値 5 - 2 の出力" );
printonedigitln ( 5 - 2 ); /* 引算 */
println ( "整数値 3 * 2 の出力" );
printonedigitln ( 3 * 2 ); /* かけ算 */
println ( "整数値 8 / 3 の出力" );
printonedigitln ( 8 / 3 ); /* 小数点以下は余りは切り捨て */
println ( "整数値 8 % 3 の出力" );
printonedigitln ( 8 % 3 ); /* 余りは「%」をで計算 */
println ( "整数値 8 - ( 8 / 3 ) * 3 の出力" );
printonedigitln ( 8 - ( 8 / 3 ) * 3 ); /* 余りを求めるもう一つの方法 */
return 0;
}
$ ./sample-014.exe $
Download : sample-015.c
/*
* 2020/06/19 sample-015.c
*/
/*
* extern 宣言
*/
extern void println ( char *string );
extern void printpositiveint ( int n );
extern void printpositiveintln ( int n );
/*
* 整数
*/
int main ( int argc, char *argv[] ) {
println ( "整数値 0 の出力" );
printpositiveintln ( 0 );
println ( "整数値 11 の出力" );
printpositiveintln ( 11 );
println ( "整数値 12345 の出力" );
printpositiveintln ( 12345 );
println ( "整数値 12 * 34 の出力" );
printpositiveintln ( 12 * 34 );
return 0;
}
$ ./sample-015.exe $
Download : sample-016.c
/*
* 2020/06/19 sample-016.c
*/
#include <stdio.h>
/*
* check_star_char
*/
void check_star_char ( char ch ) {
if ( ch == '*' ) { /* ch の中身が '*' かどうかをチェック */
printf ( "これは「*」です\n" );
} else {
printf ( "これは「*」ではありません\n" );
}
}
/*
* main
*/
int main ( int argc, char *argv[] ) {
check_star_char ( getchar() ); /* getchar() で文字を読込み、それを出力 */
return 0;
}
A
$ ./sample-016.exe < sample-016.in A これは「*」ではありません $
/*
* 20200619-01-QQQQ.c
* 「Hello, World」+[改行] を画面に出力するプログラム
*/
#include <stdio.h>
/*
* main
*/
int main ( void ) {
printf ( "Hello, World\n" );
return 0;
}
#include <stdio.h>
int main(void) {
putchar ( getchar() );
/* getchar() を利用して、キーボードから『文字』を入力 */
/* その入力した『文字』を画面出力する */
return 0;
}
#include <stdio.h>
int main(void) {
putchar ( *"abc" ); /* 『文字列』"abc" の先頭の『文字』を出力 */
/* 「*"abc"」<->「'a'」 */
putchar ( *"XYZ" ); /* 『文字列』"XYZ" の先頭の『文字』を出力 */
/* 「*"XYZ"」<->「'X'」 */
return 0;
}
#include <stdio.h>
int main(void) {
/* 『文字列』"abc" の先頭の次の『文字』はどうやってとるか ? */
/* "abc" + 1 => "bc" */
/* 『文字列』に +1 すると、先頭の文字が一つ欠けて、
長さが 1 だけ短くなった『文字列』になる */
/* 先頭の次の『文字』は、「 *("abc" + 1)」 */
putchar ( *("abc"+1) ); /* *("abc"+1) => *("bc") => 'b' */
return 0;
}
#include <stdio.h>
int main(void) {
/*
「*(文字列+整数値)」 の代わりに「文字列[整数値]」が使える
*(何か+整数値) <=> 何か[整数値]
*/
putchar ( "abc"[1] );
/* "abc"[1] => *("abc"+1) => *("bc") => 'b' */
return 0;
}
#include <stdio.h>
/*
関数 hello の定義
関数「hello」の内容が「printf ( "Hello, World\n" );」であると定義
命令「printf ( "Hello, World\n" );」に「hello」という名前を付けた
*/
void hello(void) {
printf ( "Hello, World\n" );
}
int main(void) {
hello(); /* 関数「Hello」を呼び出す */
/* 関数の定義内容(関数本体)が実行される */
return 0;
}
#include <stdio.h>
/*
関数 hello の定義
関数「hello」の内容が「printf ( "Hello, World\n" );」であると定義
命令「printf ( "Hello, World\n" );」に「hello」という名前を付けた
*/
void hello(void) {
printf ( "Hello, World\n" );
}
#include <stdio.h>
/*
関数 hello が他のソースで
「void hello(void)」
の形で定義されている
*/
extern void hello(void); /* extern 宣言 */
int main(void) {
hello(); /* 関数「Hello」を呼び出す */
/* 関数の定義内容(関数本体)が実行される */
return 0;
}
#include <stdio.h>
int main(void) {
printf ( "Hello, World\n" );
}
#include <stdio.h>
int main(void) {
printf ( "こんにちは、皆さん\n" );
return 0;
}
/*
ノックアウト 1
printf() .. メッセージがでなくなった
*/
#include <stdio.h>
int main(void) {
return 0;
}
/* ノックアウト 2 コンパイルは OK / リンクでエラー */
/*
ノックアウト 3
「#include」.. コンパイル時に「警告」
*/
int main(void) {
printf ( "Hello, World\n" );
return 0;
}
/*
ノックアウト 3'
「#include」の代わりに extern 宣言
*/
extern int printf (const char *__restrict __format, ...);
/* #include <stdio.h> の代わりになる */
int main(void) {
printf ( "Hello, World\n" );
return 0;
}
/*
ノックアウト 4 :
「#include」と printf .. 1 と同じ
*/
int main(void) {
return 0;
}
/*
ノックアウト 5 :
「void」にして「return 0;」を削る .. 問題ない
*/
#include <stdio.h>
void main(void) {
printf ( "Hello, World\n" );
}
前回(2020/06/12)の内容
文字型 (小さな整数 : ASCII Code)
「文字」の入出力と操作 / 文字列と文字の関係
一周目のまとめ
C 言語における『文字』
『文字』の C 言語上での表現(リテラル)
二つの「'」で挟まれた文字 ( [とりあえず] ASCII コード表の文字 )
'a' => C 言語では、「a」という「文字」を表す表現
『文字』は、「小さな整数値」に対応 ( ASCII コード表の数値 )
コーディングされている
全角文字の「漢」という文字はどうやって扱うか ?
=> この講義では考えない
『文字』の順序 : (ASCII コード表によって) 対応する整数値の順序
'A' と 'B' では、
'A' が 'B' より小さい
'A' の一つ次が 'B'
となる事が
'A' に対応している ASCII コード (小さな整数値) が 65
'B' に対応している ASCII コード (小さな整数値) が 66
に基づいて、説明される
『文字』の計算 : (ASCII コード表によって) 対応する整数値の計算
'A' + 1 => 65 + 1 => 66 => 'B'
『文字』の C 言語上での入出力
getchar() : 『文字』の入力
putchar() : 『文字』の出力
『文字』と『文字列』の関係
『文字列』は、『文字』の並び
例 : "abc" => 「a」と「b」と「c」という三つ文字からなる
'a', 'b', 'c', '\0' (EOS : End Of String : null 文字)
EOS は『文字列』の「終わり」を表す『文字』として扱う
EOS は普通は扱わない文字なので、これに対し、特別な意味を持たせている
cf. 「\」も、C 言語では「特別な意味(エスケープシーケンスの開始)」
「\」は、普段も利用するので、「\」を'\\'で表現
EOS は、'\0'
複数 ( 0 個以上 ) の『文字』と、最後に '\0' (EOS : End Of String) からなる
"abc" <-> { 'a', 'b', 'c', '\0' }
『文字』が『文字列』の一部であるなら、一部を取り出したい
『文字列』の前に「*」を付けると、『文字列』の先頭の『文字』が取り出せる
『文字列』[整数値] <-> *(『文字列』+整数値)
一周目の内容
表現
プログラムの基本的な書き方(Hello World)
幾つかのライブラリ関数の利用 : printf, putchar, strcmp
関数の作り方(特に、引数付き関数)
命令に名前をつける
引数を利用して、同じ関数で、様々な事を行う
引数の内容によって振る舞いを変える
=> if 構文
同じ内容を繰り返すために : 再帰呼び出し
幾つかのデータ型とその扱い(計算)
文字列
文字列の表現 (「"」で挟む) / 出力 (printf) / 計算 (+1 すると短くなる )
文字
文字の表現 (「'」で挟む) / 出力 (putchar) / 計算 (+1 すると次の文字 )
操作
コンパイル、リンクの仕方
サクラエディタを利用ソースコードプログラム (*.c) を作る
cc -c ソースコードファイル(*.c)
=> コンパイル
=> オブジェクトファイル(*.o) が作られる
cc -o 実行ファイル(*.exe) オブジェクトファイル(*.o)
=> リンク
=> 実行ファイル(*.exe)が作られる
実行ファイルの実行方法は、
./実行ファイル
=> プログラムの実行結果が表示される
分割コンパイル
一つのプログラム(実行ファイル)を作成するために、
複数のソースコードファイルを作成し、
個々にコンパイル(して、リンク)する
[事例]
p-005.c
一つのファイルで、main と hello の二つの関数を定義
=> 単独で、一つのプログラム ( p-005.exe ) が作れる
p-006.c / p-006-01.c
=> p-005.c の内容を二つに分けた
=> p-006.exe を作るには、両方 (p-006.c / p-006-01.c)が必要
それぞれが異なるソースコードなので、
それぞれ別々にコンパイルが必要
=> 分割コンパイル
それぞれのオブジェクトファイル
例:
cc -c p-006.c
=> p-006.o
cc -c p-006-01.c
=> p-006-01.o
さらに実行ファイルを作るときには、
すべてのオブジェクトファイルを指定する必要がある
例:
cc -o p-006.exe p-006.o p-006-01.o
分割コンパイルの利点
C 言語のプログラムでは main 関数が一つ必要
もし、main 関数の中で他の関数を利用している場合は、
その関数の定義が必要
関数の定義を使いまわしたい場合にどうするか ?
ProgA : mainA + func => ProgA.c
ProgB : mainB + func => ProgB.c
func の定義が複数の場所に現れるので厄介
分割コンパイル
ソースコードの分割
mainA.c => mainA
mainB.c => mainB
func.c => func
リンクで
ProgA.exe : mainA.o func.o
ProgB.exe : mainB.o func.o
=> func の定義 (func.c) が共有できる
make / makefile
makefile の記述に従ってコマンドを実行するコマンドが make
分割コンパイル => 作業が増える
作業内容を makefile に記述し、make で作業を行う
1. 分割コンパイルによる作業の増加を軽減できる
2. コンパイル/リンク作業を記録して再利用できる
作成 : プログラムを構築する考え方
分割統治をおこなう ( 問題を複数の小さな副問題に分割する )
=> 問題をどのように分割するか ? ( => 数学 )
命令を組合せる三つの表現(この三つで万能になる)
順接 : 命令を並べると、その順に実行される
条件分岐 : if 構文を使い、条件によって二つの命令の一方だけを実行する
再帰呼出し : 関数内で自分を呼び出す事により、間接的に命令を繰り返す
<数学:一般の問題>
問題 Q => 複数の副問題 (Q1,Q2,..,Qn)に分割する
それぞれ、解決する
解決 A <= A1, A2, .., An
<情報:プログラムの作成>
問題 Q => 複数の副問題 (Q1,Q2,..,Qn)に分割する
それぞれ、解決するプログラム(ソースコード)
解決 P <= P1, P2, .., Pn
再利用 : ライブラリとサンプルの利用
再利用によるソフトウェア作成
スクラッチ(何もない所)から、作るのは効率が悪い
「在るモノ」は利用しよう (「車輪の再発明」は良くない)
創造性の原理 : 「在るモノ」は作るな
差分プログラミング : サンプルプログラムの利用
既に「*正しく* 動く」事が解っているプログラムを変更する
「動きが変に成った」なら、「最後に変更した所が変(元に戻してみよ)」
少しずつ、「作っては試す」を繰り返す (一度に完成しようとしない)
分割コンパイル : プログラムを複数のファイルに分割して実現する
ヘッダーファイル (*.h) の利用
コンパイル作業の軽減化 : Makefile と make を利用
[二周目]
「Hello, World」再び
サクラエディタ(テキストエディタ)で、ソースコードファイル(*.c)を作成
最も単純なプログラム : Hello, World
「完全」で、「解り易く」、「十分に役に立つ」プログラム
「完全」: これ単独で、「プログラム」になっている
「誤り」を含んでない
「解り易く」: Hello, World の振る舞いが(なんとなく..)予想できる
プログラム作成の「スタートポイント」
新しいプログラムを作成する場合
スクラッチ(最初)から作るより、
Hello, World を叩き台(出発点)として利用する
差分プログラミング
いまある(完全なプログラム)に対し、
変更(差分を加える)ことにより、新しいプログラム作成する
まず、「動く」事の大切さ
人間は「誤りを犯す」生物 : 変更部分に誤り
プログラムの『骨格』の理解
Hello World
先頭に、#include がある
main 関数が必ず含まれる
main 関数の頭の部分が 「int main(void)」
main 関数の本体が「{」から「}」まで
main 関数の最後の命令は「return 0;」である
他の言語の学習でも役立つ
その言語での「Hello, World」がわかれば
その言語は、(調べるだけで..) 利用できる
「Hello, World」の考え方(旧)
「{」?「}」の中にある「printf ( "Hello, World\n" );」だけに注目
この部分を書き換えれば良い
残りの部分は「御呪い」とすれば良い
「Hello, World」の謎
御呪い:「#include」/「int」/「return 0;」は何をしている ??
少し、「謎」を解いてみる
遺伝子ノックアウト
その「遺伝子」の役割が解らなければ、それを壊して、何が起きるか見る
「働かなくなった機能」があれば、「それが、壊した遺伝子の機能」に関係する
プログラム・ノックアウト
動くプログラム(Hello, World)から、一部分を取り除いた(ノックアウト)したら..?
ノックアウト 1
printf() .. メッセージがでなくなった
printf がメッセージ出力の役割を担っている
ノックアウト 2
コンパイルは OK
そもそもエラーになる部分がないので、当然
リンクでエラー
=> 「main 関数定義がない」
undefined reference to `main'
main という定義されていない関数への参照がある
# main を使いたいのに定義されていない
C のプログラムには、
必ず、「main 関数の定義」が含まれている必要がある
ノックアウト 3
「#include」.. コンパイル時に「警告」
warning: implicit declaration of function ‘printf’
関数 printf の宣言が「暗黙宣言」である
# printf は、すでに別な所で定義されている
# それを再利用している
# 自分で定義した関数を同じ
# => それがどんな関数かという情報無い
<= printf の宣言が無い
=> extern 宣言を入れれば、warnig が消せる
#include <stdio.h>
=> /usr/include/stdio.h をここに取り込む(include)という命令
ノックアウト 4 :
「#include」と printf .. 1 と同じ
#include <stdio.h> は printf を利用する場合に、
printf の宣言 ( /usr/include/stdio.h にある )が必要なので
ノックアウト 5 :
「void」にして「return 0;」を削る .. 問題ない
main の前 int と「return 0;」が関係する
=> 来週説明
大雑把なまとめ
main は必要 : 最も短いプログラム
「#include」 : extern 宣言と関係するらしい..
「return 0;」/「int」: 「void」に交換すると良いらしい..
「printf」: まだ、謎があるようだ(変な extern 宣言)
謎の解明
幾つかの謎は解けたが、まだ解明されない謎や、新しい謎が...
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20200619-01.c
/*
* 20200619-01-QQQQ.c
* 「Hello, World」+[改行] を画面に出力するプログラム
*/
#include <stdio.h>
/*
* main
*/
int main ( void ) {
/*
** この部分を完成させなさい
*/
return 0;
}
$ ./20200619-01-QQQQ.exe Hello, World $
Download : 20200619-02.c
/*
* CDATE FILENAME
*
* キーボードから一文字入力し、その文字によって異る国の挨拶をする
*/
#include <stdio.h>
/*
* hello ( char contry )
* char contry : 国を表す一文字
* j : 日本
* e : 英語
* c : 中国
* f : フランス語
* g : ドイツ語
*/
void hello ( char cmd ) {
if ( cmd == 'j' ) { /* 'j' の時は、日本語にする */
printf ( "こんにちは\n" );
} else if ( cmd == 'e' ) { /* 'e' の時は、英語にする */
/*
** この部分を完成させなさい
*/
} else if ( cmd == 'c' ) { /* 'c' の時は、中国語にする */
printf ( "ニイハオ\n" );
} else if ( cmd == 'f' ) { /* 'f' の時は、フランス語にする */
printf ( "Bonjour\n" );
/*
** この部分を完成させなさい
*/
printf ( "Guten tag\n" );
} else { /* どれでもなければ.. */
printf ( "???\n" );
}
}
/*
* main
*/
int main( void )
{
printf ( "国を表す文字を入力してください\n" );
printf ( "\tj\t日本\n" );
printf ( "\te\t英語\n" );
printf ( "\tc\t中国\n" );
printf ( "\tf\tフランス\n" );
printf ( "\tg\tドイツ\n" );
printf ( "> " );
hello ( getchar() ); /* getchar() で文字を入力し、それに対応する結果を出す */
return 0;
}
f
$ ./20200619-02-QQQQ.exe 国を表す文字を入力してください j 日本 e 英語 c 中国 f フランス g ドイツ > f Bonjour $
Download : 20200619-03.c
/*
* CDATE FILENAME
*
* キーボードから一行(改行まで..)文字列を読込み、それを逆順に出す
*/
#include <stdio.h>
/*
* reverse ( char contry )
* char cmd : どのメッセージにするかを表す文字
*
*/
void reverse_line ( char ch ) {
if ( ch == '\n' ) { /* 改行ならば.. */
/* なにもしなくてよい */
} else { /* そうでなければ.. */
/*
** この部分を完成させなさい
*/
}
}
/*
* main
*/
int main( void )
{
reverse_line ( getchar() ); /* 最初の文字を読み込んで .. */
putchar ( '\n' ); /* 改行を出力 */
return 0;
}
abc123
$ ./20200619-03-QQQQ.exe 321cba $