第6章 配列

第9章
ポインタ
配列変数とポインタ
静的確保と動的確保
ポインタ配列
2次元配列
時間計測
第1回レポートの課題
ポインタ変数(ex_9_3_5.c)
#include <stdio.h>
#define PRINT(x,y,z) printf(#x"= %d, "#y"= %d, "#z"= %d\n",x,y,z);
int main(void)
{
int x = 1, y = 100;
int *p; PRINT(x,y,p);
p = &x; PRINT(x,y,p);
y = *p; PRINT(x,y,p);
p = &y; PRINT(x,y,p);
*p = 0; PRINT(x,y,p);
PRINT(&x,&y,&p);
return 0;
}
x=1, y=100, p=4199225
x=1, y=100, p=1245044
x=1, y=1, p=1245044
x=1, y=1, p=1245048
x=1, y=0, p=1245048
&x=1245044, &y=1245048, &p=1245052
ポインタ変数(ex9_3_5.c)
int x = 1, y = 100;
int *p;
p = &x;
y = *p;
p = &y;
*p = 0;
1245044 x
1
1245048 y
10010
1245052 p
4199225
1245048
1245044
9.4 配列変数とポインタ (再掲)
配列(array)とは
int a[5]={11,12,13,14,15};
←
プログラム例9.4.1
と宣言すると、メモリ上に右図
配列要素
のように並んだ int 型変数 5 個
分の領域が確保され、その領域
が a と名付けられる。
11
12
13 14 15
各々の配列要素には指定された
a[0] a[1] a[2] a[3] a[4]
値が入る。
添字
9.4 配列変数とポインタ
(再掲)
C では、配列変数はポインタ変数
を用いてアクセスすることが多い
&a[0] は1000、 &a[1] は1004
int *a_p;
a_p = &a[0];
a_p は、整数型変数のアドレスが
入るポインタ型変数で(1行目)、
配列 a の先頭の要素の番地を
代入する(2行目)
1000
1004
1008
1012
1016
11
12
13
14
15
a[0]
a[1]
a[2]
a[3]
a[4]
1000
a_p
配列変数の正体
コンパイラは a[i] を *(a+i) と解釈
&a[i] = &(*(a+i)) = a+i
特に、 &a[0] = a
配列 a の名前自体が配列の先頭要素
の番地を表すポインタとみなせる
ただし、a が a_p のようなポインタ変数
として独立に存在するわけではない
実は、
a[i] = *(a+i) = *(i+a) = i[a]
が成り立つ。→ ex9_4_1a.c
1000
1004
1008
1012
1016
11
12
13
14
15
a[0]
a[1]
a[2]
a[3]
a[4]
ポインタと配列変数の関係
#include <stdio.h>
ex9_4_1a.c
int main(void)
{
int i, a[]={11,12,13,14,15};
printf(" a[i]= ");
for (i=0; i<5; i++) printf("%d ",a[i]); putchar('\n');
printf("*(a+i)= ");
for (i=0; i<5; i++) printf("%d ",*(a+i)); putchar('\n');
printf(" i[a]= ");
for (i=0; i<5; i++) printf("%d ",i[a]); putchar('\n');
printf("\n &a=%u, a=%u, *a=%d\n",&a, a, *a);
}
実行結果(静的確保)
Z:\nyumon2>ex9_4_1a
a[i]= 11 12 13 14 15
*(a+i)= 11 12 13 14 15
i[a]= 11 12 13 14 15
aが指し示す
先のデータ
= a[0]
&a=1245036, a=1245036, *a=11
みなしポインタa
のアドレス
(スタック領域)
みなしポインタaの
中身(スタック領域
のアドレス)
1245036
11
a[0]
1245040
12
a[1]
アドレスがループしており、配列名aはポインタ変数ではない
動的確保の場合のポインタと配列
#include <stdio.h>
ex9_4_1b.c
#define N 5
int main(int argc, char *argv[])
{
int i, n=N, *a;
if (argc > 1) sscanf(argv[1], "%d", &n);
if ((a = (int *)calloc(n, sizeof(int))) == NULL) {
perror("I can't allocate a[]."); abort();
}
for (i=0; i<n; i++) a[i]=11+i; printf(" a[i]= ");
for (i=0; i<n; i++) printf("%d ", a[i]); putchar('\n');
printf("\n &a=%u, a=%u, *a=%d\n",&a, a, *a);
}
実行結果(動的確保)
Z:\nyumon2>ex9_4_1b
a[i]= 11 12 13 14 15
aが指し示す
先のデータ
= a[0]
&a=1245052, a=4260528, *a=11
ポインタ変数a
のアドレス
(スタック領域)
ポインタ変数aの
中身(ヒープ領域
のアドレス)
動的確保した配列a[]の実体は
ヒープ領域に確保
1245052 4260528
a
4260528
11
a[0]
4260532
12
a[1]
ポインタ配列
ポインタを要素に持つ配列
 文字列配列や2次元配列で用いる
int *a[N];


int *a[0],*a[1],...,*a[N-1];
char *argv[]


コマンドラインの文字列配列
Z:\nyumon2>ex_9_4_1b 10


argv[0] = "ex_9_4_1b"
argv[1] = "10"
2次元配列の静的確保(自動変数)
#include <stdio.h>
ex6_7_2c.c
#define N 4
int main(void)
{
int i, j, k=0;
スタック領域に確保
int a[N][N];
for (i = 0; i < N; i++)
for (j = 0; j < N; j++) a[i][j] = ++k;
printf("&a=%u, a=%u, *a=%u, **a=%d\n", &a, a, *a, **a);
return 0;
}
2次元配列の静的確保(静的変数)
#include <stdio.h>
ex6_7_2d.c
#define N 4
int main(void)
{
int i, j, k=0;
データ領域に確保
static int a[N][N];
for (i = 0; i < N; i++)
for (j = 0; j < N; j++) a[i][j] = ++k;
printf("&a=%u, a=%u, *a=%u, **a=%d\n", &a, a, *a, **a);
return 0;
}
2次元配列の静的確保(大域変数)
#include <stdio.h>
ex6_7_2e.c
#define N 4
データ領域に確保
int a[N][N];
int main(void)
{
int i, j, k=0;
for (i = 0; i < N; i++)
for (j = 0; j < N; j++) a[i][j] = ++k;
printf("&a=%u, a=%u, *a=%u, **a=%d\n", &a, a, *a, **a);
return 0;
}
実行結果(静的確保)
Z:\nyumon2>ex6_7_2c
&a=1310528, a=1310528, *a=1310528, **a=1
Z:\nyumon2>ex6_7_2d
&a=4221348, a=4221348, *a=4221348, **a=1
Z:\nyumon2>ex6_7_2e
&a=4221792, a=4221792, *a=4221792, **a=1
スタック領域
データ領域
(静的領域)
データ領域
1310528
1
4221348
1
4221792
1
1310532
2
4221352
2
4221796
2
2次元配列の動的確保(1)
#include <stdio.h>
#include <stdlib.h>
ex6_7_2f.c
#define N 4
int main(void)
{
int i, j, k=0;
スタック領域にポインタ配列
int *a[N];
for (i = 0; i < N; i++) {
ヒープ領域に確保
a[i] = (int *)calloc(N, sizeof(int));
for (j = 0; j < N; j++) a[i][j] = ++k;
}
printf("&a=%u, a=%u, *a=%u, **a=%d\n", &a, a, *a, **a);
return 0;
}
2次元配列の動的確保(2)
#include <stdio.h>
#include <stdlib.h>
ex6_7_2g.c
#define N 4
int main(void)
{
int i, j, k=0;
スタック領域にポインタのポインタ
int **a;
ヒープ領域に
a = (int **)calloc(N, sizeof(int *));
ポインタ配列
for (i = 0; i < N; i++) {
a[i] = (int *)calloc(N, sizeof(int));
ヒープ領域に確保
for (j = 0; j < N; j++) a[i][j] = ++k;
}
printf("&a=%u, a=%u, *a=%u, **a=%d\n", &a, a, *a, **a);
return 0;
}
2次元配列の動的確保(3)
#include <stdio.h>
#include <stdlib.h>
ex6_7_2h.c
#define N 4
#define IDX(i,j) (i)*N+(j)
int main(void)
{
int i, k=0;
スタック領域にポインタ
int *a;
a = (int *)calloc(N*N, sizeof(int));
ヒープ領域に確保
for (i = 0; i < N*N; i++) a[i] = ++k;
printf("&a=%u, a=%u, *a=%u, **a=%d\n", &a, a, *a, **a);
printf("%d, %d, %d\n",a[IDX(1,1)],a[IDX(2,2)],a[IDX(3,3)]);
return 0;
}
実行結果(動的確保)
スタック
1310576 a[0] 4260480
1310580 a[1] 4260448
Z:\nyumon2>ex6_7_2f
&a=1310576, a=1310576, *a=4260480, **a=1
ヒープ
1
2
4260480 **a
4260484
Z:\nyumon2>ex6_7_2g
&a=1310588, a=4260480, *a=4260448, **a=1
Z:\nyumon2>ex6_7_2h
&a=1310588, a=4260480, *a=1
6, 11, 16
ヒープ
スタック
4260448 **a
4260452
1
2
1310588 a 4260480
4260480 *a
4260448
スタック
1310588 a
4260480
ヒープ
4260480 *a
4260484
1
2
時間計測:ミリ秒単位(time2.c) 再掲
#include <stdio.h>
← 乱数用ヘッダファイル
#include <stdlib.h>
← Windows用ヘッダファイル
#include <windows.h>
#pragma comment(lib,"winmm.lib") ← 音楽用Windowsライブラリ
int main(void)
{
int i, N=100000;
int t1, t2;
計測開始
t1 = timeGetTime();
for (i=0; i<N; i++) printf(" %d", rand()%6 + 1);
printf("\n");
計測終了
t2 = timeGetTime();
printf("経過時間: %d msec\n", t2-t1);
return 0;
実行時間
}
「エラトステネスの篩」改良版(prime2.c)
配列 a をデータ領域または動的に確保する
(char 型にすると上限が4倍になる)
2. 動的確保の場合、コマンドラインオプションで素
数の上限 n を入力できるようにする
3. (n-1000)から上の素数を(整数10桁で)表示
4. 素数の個数を数えあげて表示
5. 経過時間を計測して表示
5
6. n のデフォルト値は1億または10億とする
1.
エラトステネスの篩(ふるい)改良版
t1
コマンドラインから n の入力
100,000,000
or 500,000,000
エラー処理つき配列 a[ ] 確保&初期化 ←callocで動的確保
i=2,...,N/2
prime2
j ← i+i
a[j] ← 1
j≦N
j ← j+i
!a[i]
k←0
i の倍数
を除く
k ← k+1
i=2,...,N
t2
!a[i]
素数の数 k の出力
経過時間(t2-t1)の出力
i の10桁出力
i > n-1000
後ろの素数を
出力
第1回 レポート(必須)

課題:「エラトステネスの篩」改良版(prime2.c)
(コマンドライン入力&動的確保)


提出期限:2010年10月29日(金) 12:50
提出場所:ネットワーク実験室(1)の入口近くの箱
今回のレポートでは以下の項目をいれること。
1. 学籍番号、氏名
2. 課題名
レポートのファイルは
3. ソースリスト
保存しておくこと
4. 実行結果
5. 感 想(5行以上)
レポートサンプルを参照のこと
本日のパズル
次のプログラムは何が出力されるか?
#define PRINT3(x,y,z) printf("%d\t%d\t%d\n",x,y,z)
main()
{
int x, y, z;
x =
++x
x =
++x
x =
++x
}
y = z = 1;
|| ++y && ++z; PRINT3(x,y,z);
y = z = 1;
&& ++y || ++z; PRINT3(x,y,z);
y = z = 1;
&& ++y && ++z; PRINT3(x,y,z);
1
2
3