Download : sample-001.c ( SJIS 版 )
/*
* 2013/12/20 sample-001.c
*/
/*
*
*/
#include <stdio.h>
#include "s_print.h"
#include "s_input.h"
/*
*
*/
#define EOS '\0'
/*
* 複数の整数を書式指定して文字列の中に埋め込む
*/
void print_with_format ( char *msg, ... ) {
int i;
char *pvalue = (char *)&msg + sizeof( char * );
for ( i = 0; msg[i] != EOS; i++ ) {
if ( msg[i] == '%' ) { /* 文字列の中に '%' があったら特別処理する */
i++; /* 次の文字をみる */
switch ( msg[i] ) {
case 'd': /* 10 進数 */
s_print_int ( *((int *)pvalue) ); // s_print_int ( *pvalue++ );
pvalue += sizeof ( int );
break;
case 'o': /* 8 進数 */
s_print_int ( *((int *)pvalue) );
pvalue += sizeof ( int );
break;
case 'x': /* 16 進数 */
s_print_int ( *((int *)pvalue) );
pvalue += sizeof ( int );
break;
case 'c': /* 文字 */
s_print_char ( *((char *)pvalue) );
pvalue += sizeof ( int ); /* char は自動的に int にされる */
break;
case 's': /* 文字列 */
s_print_string ( *((char **)pvalue) );
pvalue += sizeof ( char * );
break;
case 'f': /* 浮動小数点数 */
s_print_double ( *((double *)pvalue) );
pvalue += sizeof ( double );
break;
case '%': /* '%' が重なったら.. */
s_print_char ( '%' ); /* '%' を出力 */
break;
default: /* その他 : よくわからないので読み飛ばす.. */
break;
}
} else { /* そうでなけれ .. */
s_print_char ( msg[i] ); /* そのままその文字を出力 */
}
}
}
/*
* 色々な数値の出力
*/
int main ( void ) {
/*
* データの出力 (Output)
*/
print_with_format (
"整数値(%%d) : %d, 文字(%%c) : '%c', 文字列(%%s) : \"%s\", 浮動小数点数(%%f) : \
%f\n",
123, (int)'a', "xyz", 1.23 );
/*
*
*/
return 0;
}
/*
*
*/
C:\usr\c>sample-001
整数値(%d) : 123, 文字(%c) : 'a', 文字列(%s) : "xyz", 浮動小数点数(%f) : \
1.230000
C:\usr\c>
#include <stdio.h>
int main(int argc, char *argv[] ) {
int i;
scanf ( "%d", &i ); /* キーボードから整数値を
一つ読込み、変数 i に代入 */
printf ( "i=%d\n", i );
/* 変数 i の値(キーボードから入力した値) を
表示する */
return 0;
}
#include <stdio.h>
int main(int argc, char *argv[] ) {
printf ( "sizeof(char)=%d\n", sizeof(char) );
printf ( "sizeof(int)=%d\n", sizeof(int) );
printf ( "sizeof(double)=%d\n", sizeof(double) );
}
#include <stdio.h>
int main(int argc, char *argv[] ) {
int i; /* int 型なので char 型 4 つ分 */
i = 'A'; /* i に 'A' ( ASCII Code で 65 ) */
printf ( "i = %d\n", i ); /* 65 が表示される */
printf ( "&i = %s\n", &i ); /* 何がおきるか ? */
i = 'A' * 256 + 'B';
printf ( "&i = %s\n", &i ); /* 何がおきるか (2) ? */
i = ('A' * 256 + 'B') * 256 + 'C';
printf ( "&i = %s\n", &i ); /* 何がおきるか (3) ? */
return 0;
}
#include <stdio.h>
int main(int argc, char *argv[] ) {
int i = 10;
int j = 100;
char a = 'a';
char b = '0';
printf ( "&i = %x\n", &i );
/* %x は整数値(アドレス値)を 16 進数で表示 */
printf ( "&j = %x\n", &j );
printf ( "&a = %x\n", &a );
printf ( "&b = %x\n", &b );
return 0;
}
#include <stdio.h>
int main(int argc, char *argv[] ) {
int i = 10;
int j = 100;
char a = 'a';
char b = '0';
double f = 3.14;
printf ( "&i = %x\n", &i );
printf ( "&i + 1 = %x\n", &i + 1 );
printf ( "sizeof (int)=%d\n", sizeof ( int ) );
printf ( "&a = %x\n", &a );
printf ( "&a + 1 = %x\n", &a + 1 );
printf ( "sizeof (char)=%d\n", sizeof ( char ) );
printf ( "&f = %x\n", &f );
printf ( "&f + 1 = %x\n", &f + 1 );
printf ( "sizeof (f)=%d\n", sizeof ( f ) );
/* sizeof では、型名だけでなく、変数名もかける */
return 0;
}
#include <stdio.h>
int main(int argc, char *argv[] ) {
int i = 10;
int *ip; /* 整数へのポインター型変数 */
ip = &i; /* 変数 ip に i へのポインターが入る */
printf ( "*ip = %d\n", *ip );
/* 「*ip」 == 「*(&i)」 == 「i」== 「10」*/
return 0;
}
#include <stdio.h>
int main(int argc, char *argv[] ) {
int a = 100;
int b = 200;
int c = 300;
int *ip;
ip = &b;
printf ( "&a = %x\n", &a );
printf ( "&b = %x\n", &b );
printf ( "&c = %x\n", &c );
printf ( "ip = %x\n", ip );
printf ( "ip + 1 = %x\n", ip + 1 ); /* アドレス値が sizeof (int) だけ増える */
printf ( "*ip = %d\n", *ip ); /* *ip == *(&b) == b == 200 */
printf ( "*(ip+1) = %d\n", *(ip+1) ); /* ??? */
/* ip + 1 の意味
ポインター値
型情報は ip ( &b )と同じ
アドレス値 (&b + 1) 、アドレスとしては sizeof(int) だけ変化
*/
printf ( "*(ip-1) = %d\n", *(ip-1) ); /* ??? */
return 0;
}
#include <stdio.h>
int main(int argc, char *argv[]) {
int i = 'A' * 256 + 'B';
printf ( " &i = %d\n", *( &i) );
/* *(&i) == i -> */
printf ( "(char *)&i = %d\n", *((char *)&i) );
/* *((char *)&i) == ? */
/*
キャストとは
式の前に 「(型)」とかく事により
式の値を強制的に「型」に変化させてしまう
という表現
*/
/*
(char *)&i
^ 整数型の変数
^^ 整数型へのポインター値
^^^^^^ 文字型へのポインター型
^^^^^^^^^ 文字型へのポインター型へのキャスト
^^^^^^^^^^ 整数型の変数アドレス値をもちながら
型情報は、文字型へのポインター型のポインター値になる
*/
}
#include <stdio.h>
void func001 ( int i ) {
/* [1.2] i に 100 が入る */
printf ( "[2] i = %d\n", i ); /* [2] i = 100 */
i = i + 1; /* [2.1] i <- 101 */
printf ( "[3] i = %d\n", i ); /* [3] i = 101 */
}
int main ( int argc, char *argv[] ) {
int j = 100;
printf ( "[1] j=%d\n", j ); /* [1] j=100 */
func001 ( j ); /* [1.1] func001 に 100 が渡される */
printf ( "[4] j=%d\n", j ); /* [4] j=100 */
return 0;
}
#include <stdio.h>
void func002 ( int *i ) { /* i はポインタ型変数 */
/* [1.2] i には j へのポインタ値が入る */
printf ( "[2] *i = %d\n", *i ); /* [2] *i = *(&j) = j = 100 */
*i = *i + 1; /* [2.1] *i == j <- 101 */
/*^^ 左側に式 ( *i ) がきている : 左辺値 */
printf ( "[3] *i = %d\n", *i ); /* [3] *i = 101 */
}
int main ( int argc, char *argv[] ) {
int j = 100;
printf ( "[1] j=%d\n", j ); /* [1] j=100 */
func002 ( &j ); /* [1.1] j ではなく &j (ポインタ値) */
/* func002 の中で、変数 j の値が書き変わっている */
printf ( "[4] j=%d\n", j ); /* [4] j=100 ではなく j=101 */
return 0;
}
#include <stdio.h>
void func ( int n, ... ) { /* ,... 引数がさらにあるも.. */
int i;
int *ip = &n;
/*
一度目
ip -> 1
10
二度目
ip -> 5
100
110
120
130
140
150
*/
for ( i = 1; i <= n; i++ ) {
/* ip[i] -> *(ip + i) */
/* 一回目 i = 1 〜 1 : ip[i] = ip[1] = 10 */
/* ニ回目 i = 1 〜 5 : io[i] = ip[1] 〜 ip[5] = 100 〜 140 */
printf ( "%d's arg = %d\n", i, ip[i] );
}
}
int main(int argc, char *argv[] ) {
func ( 1, 10 );
/*
1 <- n
10
*/
func ( 5, 100, 110, 120, 130, 140, 150 );
/*
5 <- n
100
110
120
130
140
150
*/
return 0;
}
メモリ
セルの集まり
セル
アドレスと、値 ( 0 〜 255 : 1 byte 分 ) を記憶
変数
メモリの連続した複数のセルからなる
例 : char 型は 1 つのセル
例 : int 型は 4 つのセル
例 : T 型は sizeof(T) 個のセル
ポインター
変数名の前に '&' を付けると「ポインター値」が得られる
ポインター値 ---+-- アドレス値(番地) +------+-- メモリ
| |
+-- 型情報 +--- 型のサイズ -+
| |
+--- 計算の種類 -+-- 変数
int i;
&i <- 整数型へのポインター値
これの型は「整数型へのポインター型」
「int *」
で表現する。
int *ip;
ip を「整数型へのポインター値」を保持する変数として宣言
int *ip; --> (*ip) が、int 型
ip = &i; という形で利用
*ip が int 型になる
左辺値
1 = 10;
^ ここには、定数がくるのは駄目
^ ここにきて欲しいのは、「普通」は、変数名
a = 10;
^ これはただしい
代入文の左側に「式(値を持つもの)」をかきたい
a = a + 1;
^ ^
おなじ表現なのに、意味が違う
左は 変数名から左辺値にかわり、そのまま
右は 変数名から左辺値にかわり、さらに右辺値に変る
[ポイント] 左辺値を持つ式がかけると嬉しい
int i;
&i : ポインター値
*(&i) : 変数 i の左辺値になる
*(ポインター値) は代入文の左にかいてよい
<- 代入文の左にかける「式」がある
!! ポインター値というしくみがないと、
!! これができない
scanf ( "%d", &i ); /* キーボードから数値を入力し
その値を i に代入する */
/* 関数の中で、呼出し元の変数(i) の
値を変更したい
-> ポインター値を渡す必要がある
-> 変数の前に & が必要
*/
まとめ
C 言語の変数は、メモリ内の(いくつかの)セル並び
char 型は、一個のセル ( sizeof(char)=1 )
T 型は sizeof(T) 個のセルからなる
セルなので、アドレスがある
変数名の前に & をつけるとポインター値がえられる
アドレス + 型情報
アドレスは、整数を加えると変更できる
型情報は、キャストで変更できる
ポインター値に * をつけると、左辺値がでてくる
左辺値から右辺値が作られるので、「変数の値」がとれる
左辺値を利用すると、代入 (変数の値の変更) ができる
「ポインター値」そのものは、右辺値なので、関数の引数に渡せる
関数の呼び元の変数の値を、呼び先の関数の中で変更ができる
scanf はこの機能を利用して実現されている
===
1: func(x,y) {
2: }
3: main() {
4: func(1,2);
5: return 0;
6: }
振舞
a. main 関数が開始される (3)
b. func(1,2) が呼び出される(4)
b-1. 2, 1 をスタックに積む
b-2. 5 の場所をスタックに積む
b-3. 1 の場所に行く
c. func(x,y) が呼び出される (1)
c-1. x は スタックの上から 2 番目を表している
c-2. y は スタックの上から 3 番目を表している
[Stack]
a +-----+
b-1.
1
+----+
2
+----+
b-2.
(5) <- 5 の場所に戻る
+----+
1
+----+
2
+----+
c.
(5) <- 5 の場所に戻る
+----+
1 <- x
+----+
2 <- y
+----+
Download : 20140110-01.c ( SJIS 版 )
/*
* DATE-DIR-QQQQ.c
* Point2D 型に対応した myprintf を拡張して作る
*/
#include <stdio.h>
#include "s_print.h"
#include "s_input.h"
/*
*
*/
typedef struct {
int x;
int y;
} Point2D;
/*
* 複数の引数を書式指定して文字列の中に埋め込む
*/
void myprintf ( char *msg, ... ) {
int i;
char *pvalue = (char *)&msg + sizeof( char * );
Point2D pv;
for ( i = 0; msg[i] != EOS; i++ ) {
if ( msg[i] == '%' ) { /* 文字列の中に '%' があったら特別処理する */
i++; /* 次の文字をみる */
switch ( msg[i] ) {
case 'd': /* 10 進数 */
s_print_int ( *((int *)pvalue) );
pvalue += sizeof ( int );
break;
case 'o': /* 8 進数 */
s_print_int ( *((int *)pvalue) );
pvalue += sizeof ( int );
break;
case 'x': /* 16 進数 */
s_print_int ( *((int *)pvalue) );
pvalue += sizeof ( int );
break;
case 'c': /* 文字 */
s_print_char ( *((char *)pvalue) );
pvalue += sizeof ( int ); /* char は自動的に int にされる */
break;
case 's': /* 文字列 */
s_print_string ( *((char **)pvalue) );
pvalue += sizeof ( char * );
break;
case 'f': /* 浮動小数点数 */
s_print_double ( *((double *)pvalue) );
pvalue += sizeof ( double );
break;
case 'D': /* Point2D 型 */
pv = *((Point2D *)pvalue);
/* pv の値を (%d,%d) の形式で出力する */
/*
** この部分を完成させなさい
*/
/* 次のデータを処理するために pvalue 変更 */
/*
** この部分を完成させなさい
*/
break;
case '%': /* '%' が重なったら.. */
s_print_char ( '%' ); /* '%' を出力 */
break;
default: /* その他 : よくわからないので読み飛ばす.. */
break;
}
} else { /* そうでなけれ .. */
s_print_char ( msg[i] ); /* そのままその文字を出力 */
}
}
}
/*
* 色々な数値の出力
*/
int main ( void ) {
Point2D pv;
pv.x = 2;
pv.y = -3;
/*
* データの出力 (Output)
*/
myprintf (
"整数値(%%d) : %d, 文字(%%c) : '%c', 文字列(%%s) : \"%s\", 浮動小数点数(%%f) : %f, \
点の座標 (%%D) : %D\n",
123, (int)'a', "xyz" , 1.23, pv );
/*
*
*/
return 0;
}
/*
*
*/
C:\usr\c\> 20140110-01
整数値(%d) : 123, 文字(%c) : 'a', 文字列(%s) : "xyz", 浮動小数点数(%f) : \
1.230000, 点の座標 (%D) : (2,-3)
C:\usr\c\>