Document

プログラミング入門2
第12回
データ型
関数のプロトタイプ宣言
動的な記憶域確保
芝浦工業大学情報工学科
青木 義満
データ型
 データ型

整数型,文字型,浮動小数点数,倍精度型
データ型
意味
整数型
整数を表現する
文字型
文字を表現する
浮動小数点数
実数を表現する
倍精度型
精度の高い浮動小数点数を表現する
C言語で扱うことのできるデータ型
プログラミング入門2
2
各データが扱える数値範囲
 変数を用意する際,目的にあった(扱うデータの値の取りうる範囲,必要と
される精度)データ型を以下から選択して使用
データ型
ビット長
扱える数値の範囲
short
16
-32768 ~ +32767
int
32
-2147483648 ~ 2147483648
long
32
-2147483648 ~ 2147483648
unsigned short
16
0 ~ 65535
unsigned int
32
0 ~ 4294967295
unsigned long
32
0 ~ 4294967295
unsigned (符号なし)
→ 0と正の数のみ扱える
char
8
(-128 ~ 127)
unsigned char
8
(0 ~ 255)
float
32
3.4 x 10-38 ~ 3.4 x 10+38
double
64
1.7 x 10-308 ~1.7 x 10+308
※実際のデータサイズは,処理系によって異なる(特にint)
プログラミング入門2
3
各データ型の変換指定子
 標準入出力のための変換指定子
指定子
意味
%d(%ld)
整数の10進数として出力 (longの場合,%ld)
%u (%lu)
整数の符号なし10進数として出力
(unsigned longの場合,%lu)
%f
浮動小数点表示(float, double共通)
%c
1文字を出力
%s
文字列を出力
%p
ポインタの値(アドレス)を出力
※scanfの場合,float は%f, double は %lf で読み込み
プログラミング入門2
4
データ型の値の範囲を出力
 ソースファイル名: data.c
 各データ型の値の取りうる範囲を出力
#include <stdio.h>
#include <limits.h>
各データ型の値の範囲が定義(#define)されている
int main(void)
{
printf("char : %d to %d\n", CHAR_MIN, CHAR_MAX);
printf("unsigned char : %d to %d\n", 0, UCHAR_MAX);
printf("short : %d to %d\n", SHRT_MIN, SHRT_MAX);
printf("int : %d to %d\n", INT_MIN, INT_MAX);
printf("long : %ld to %ld\n", LONG_MIN, LONG_MAX);
printf("unsigned short : %u to %u\n", 0, USHRT_MAX);
printf("unsigned int : %u to %u\n", 0, UINT_MAX);
printf("unsigned long int: %lu to %lu\n", 0, ULONG_MAX);
return(0);
}
プログラミング入門2
5
データ型のサイズ(バイト数)を表示
 ソースファイル名: datasize.c
 各データ型の大きさ(サイズ)を表示
#include <stdio.h>
int main(void)
{
printf("sizeof(char) = %u\n", sizeof(char) );
printf("sizeof(short) = %u\n", sizeof(short) );
printf("sizeof(int) = %u\n", sizeof(int) );
printf("sizeof(long) = %u\n", sizeof(long) );
printf("sizeof(float) = %u\n", sizeof(float) );
printf("sizeof(double) = %u\n", sizeof(double) );
return(0);
}
sizeof( データ型 ) → データ型の大きさ(byte数)
プログラミング入門2
※p.180に詳しい解説
6
関数のプロトタイプ宣言
 プログラムの冒頭に,使用する関数の仕様を先に宣言しておく
→ 関数のプロトタイプ宣言
#include <stdio.h>
int main(void)
{
int x, y, z;
x = 5;
y = 10;
z = func( x, y );
printf( "x+y = %d\n", z);
定義されていない(not defined)とエラー!
return(0);
}
int func(int x, int y)
{
return (x+y) ;
}
関数をmain関数の後に定義すると…
プログラミング入門2
7
関数のプロトタイプ宣言
#include <stdio.h>
int func(int x, int y);
変数の型のみプログラムの冒頭に記述
(どんな引数を何個受け取り,どんな値を返すのか)
int main(void)
{
int x, y, z;
関数のプロトタイプ宣言
(書く習慣をつけておいた方が良い)
x = 5;
y = 10;
z = func( x, y );
printf( "x+y = %d\n", z);
return(0);
}
int func(int x, int y)
{
return (x+y) ;
}
プログラミング入門2
8
動的記憶域確保の必要性
 これまでのプログラム



配列の要素数は固定
あらかじめ大きめの配列を確保しておく
#defineマクロで、要素数を定数として宣言し、その値を変更す
ることで対応
→ 静的な配列(記憶域)の確保
 問題に応じて、適切なサイズの配列(記憶域)を確保す
る方法は?


メモリの節約
プログラムの柔軟性向上
動的な記憶域確保の必要性!
プログラミング入門2
9
calloc関数 : 記憶域の確保
 必要になったら記憶域を動的(ダイナミック)に確保し、
不要になったら解放する機能を提供
ヘッダ
#include <stdlib.h>
形式
void *calloc(size_t n, size_t size );
解説
大きさがsizeであるn個のオブジェクト(記憶域)の領域を確保す
る。
返却値
領域確保に成功した場合は、その領域の先頭へのポインタを返
し、失敗した場合は、空ポインタ(NULL)を返す。
プログラミング入門2
10
Callc関数による記憶域の確保(1)
 ソースファイル名:calloc1.c
 整数1個分の記憶域を動的に確保
#include <stdio.h>
#include <stdlib.h>
stdlib.hのインクルードを忘れずに!
int main(void)
{
int *p;
p = (int *)calloc( 1, sizeof(int) );
/*整数を1個分動的に確保*/
if( p == NULL )
puts(“記憶域の確保に失敗しました");
else {
*p = 15;
printf("*p = %d\n", *p );
}
return(0);
}
プログラミング入門2
11
Calloc関数の動作イメージ
 Calloc関数による記憶域の動的な確保
p = (int *)calloc( 1, sizeof(int) );
int型ポインタ変数を用意
500番地
*p
sizeof( int )
p
p
Intのデータ1個を動的に確保
プログラミング入門2
12
void へのポインタ
(void *)calloc(size_t n, size_t size );
p = (int *)calloc( 1, sizeof(int) );
Calloc関数の返却値 → void * 型 (voidへのポインタ型)
動的確保の対象: int, char, double, 構造体 など、様々なオブジェクト
・ 特定の型へのポインタを返す使用 → ×
・ 融通の利く万能なポインタ void * → ○
プログラムでは、確保する変数の型に合わせて、(void *)型のポインタを
任意のポインタ型にキャスト
※上の例では、int * 型
プログラミング入門2
13
free関数 : 記憶域の解放
 動的に確保した記憶域は、不要になった時点で必ず解放
ヘッダ
#include <stdlib.h>
形式
void free( void *p );
解説
Pが指す領域を開放する。但しpがNULLであれば何も
行わない。
プログラミング入門2
14
記憶域の解放
 ソースファイル:calloc2.c
 記憶域の解放
記憶域解放
#include <stdio.h>
#include <stdlib.h>
free(p);
int main(void)
{
int *p;
p = (int *)calloc( 1, sizeof(int) );
/*整数を1個分動的に確保*/
if( p == NULL )
puts(“記憶域の確保に失敗しました");
else {
*p = 15;
printf("*p = %d\n", *p );
free(p);
/* 確保していた領域を解放 */
}
p = (int *)calloc( 1, sizeof(int) );
記憶域確保
return(0);
}
プログラミング入門2
15
確保した領域への値の書き込み
 ソースファイル名:calloc3.c
 記憶域を1個動的に確保し、キーボードから値を読込み
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *p;
p = (int *)calloc( 1, sizeof(int) );
/*整数を1個分動的に確保*/
if( p == NULL )
puts(“記憶域の確保に失敗しました");
else {
printf(“整数を入力して下さい:”);
scanf(“%d”, p);
printf("*p = %d\n", *p );
}
return(0);
}
プログラミング入門2
16
1次元配列の動的確保
 配列宣言の例
int x[10];
※もしくは、#defineで宣言した定数
配列の要素数は定数式でなければならない
→ 要素数を変数とすることは不可能
→ 開発時に要素数を決定する必要
配列の動的確保 → 実行時に要素数を決定!
実行時に、扱うデータのサイズによって最適な配列を用
意することが可能
プログラミング入門2
17
1次元配列の確保
 ソースファイル名:calloc4.c
 int型の配列を動的に確保
#include <stdio.h>
#include <stdlib.h>
p[0]
int main(void)
{
int no;
/* 配列の要素数 */
int i;
int *p;
sizeof(int) * 5
printf(“確保する配列の要素数:”);
scanf(“%d”, &no);
p = (int *)calloc( no, sizeof(int) );
p[1]
p[2]
p[3]
p[4]
/*整数を1個分動的に確保*/
if( p == NULL )
puts(“記憶域の確保に失敗しました");
else {
for(i=0; i < no; i++)
p[i] = i;
for(i=0; i< no; i++)
printf(“p[%d] = %d¥n”, i, p[i] );
free(p);
}
return(0);
p
あたかも
int p[5];
と宣言された配列が存在する
かのように処理できる!
}
プログラミング入門2
18
realloc : 確保した領域の大きさの変更
 allocで確保した記憶域の大きさを必要に応じて変更
ヘッダ
#include <stdlib.h>
形式
void *realloc( void *ptr, size_t size );
*ptr : 大きさを変更したい領域へのポインタ
size: 新しい大きさ
解説
ptrが指す記憶域の大きさをsizeバイトに変更する。変
更前後の小さい方までのオブジェクトの内容は変わら
ない。
プログラミング入門2
19
記憶域の大きさを変更


ソースファイル名:calloc5.c
確保した記憶域の大きさを途中で変更
printf("Before----\n");
for(i=0;i<no;i++)
printf("p[%d] = %d\n", i, p[i]);
printf("Input new array size : ");
scanf("%d", &no2);
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int no, no2;
int i;
int *p;
int *temp;
temp = (int *)realloc(p, no2 * sizeof(int) );
領域サイズの変更
if(temp==NULL)
puts("Cannot allocate memory.\n");
else{
p = temp;
for(i=no; i < no2; i++)
p[i] = i;
printf("After----\n");
for(i=0;i<no2;i++)
printf("p[%d] = %d\n", i, p[i]);
}
free(p); 領域の解放
printf("Input size: ");
scanf("%d", &no);
p = (int *)calloc(no, sizeof(int));
1回目の領域確保
if(p==NULL)
puts("Cannot allocate memory.\n");
else {
for(i=0;i<no;i++)
p[i] = i;
}
return(0);
}
プログラミング入門2
20
reallocの動作イメージ
p
p[0]
p[1]
p[2]
p[3]
p[4]
要素数はno
←0
←1
←2
←3
←4
p
p[0]
p[1]
p[2]
realloc関数
新たに確保
した領域
※realloc関数で確保済みの領域の大きさを変更する際には、
realloc関数の返却値が、空ポインタでないことを確認!
プログラミング入門2
p[3]
p[4]
p[5]
p[6]
p[7]
←5
←6
←7
要素数はn2
21
二次元配列の動的確保(1)


ソースファイル名:calloc6.c
height行3列の2次元配列を確保(行数固定)
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int height;
int (*p)[3];
int i, j;
要素型がintで要素数が3の配列へのポインタ
printf("Gyou :");
scanf("%d", &height);
p = (int (*)[3])calloc(height * 3, sizeof(int));
}
if(p==NULL)
puts("Cannot allocate memory.\n");
else{
for(i=0;i<height;i++)
for(j=0;j<3;j++)
p[i][j]=0;
for(i=0;i<height;i++)
for(j=0;j<3;j++)
printf("p[%d][%d] = %d\n", i, j, p[i][j]);
free(p);
}
return(0);
プログラミング入門2
22
2次元配列の動的確保 イメージ
p[0][0]
p[0]
p[0][1]
p[0][2]
p[1][0]
p[1]
p[1][1]
p[1][2]
4(height) * 3 * sizeof(int)
p[2][0]
p[2]
p[2][1]
p[2][2]
p[3][0]
p[3]
p[3][1]
p[3][2]
※n次元配列を動的に確保する際は、最高位のn次元の
要素数のみが可変。それ以外の次元の要素数は定数でなければならない
プログラミング入門2
23
二次元配列の動的確保(2)
上級者向け
ソースファイル名:calloc7.c
 ダブルポインタを用いた2次元配列の動的確保(行数列数共に可変)

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int height, width;
int i,j;
int **p;
for(i=0;i<height;i++)
for(j=0;j<width;j++)
p[i][j]=0;
for(i=0;i<height;i++)
for(j=0;j<width;j++)
printf("p[%d][%d]=%d\n", i, j, p[i][j]);
Free:
for(i=0;i<height;i++)
free(p[i]);
free(p);
}
return(0);
printf("Gyou: "); scanf("%d", &height);
printf("Retsu: "); scanf("%d", &width);
p= (int **)calloc(height, sizeof(int *));
if(p==NULL)
printf("Cannot allocate memory.\n");
else{
for(i=0;i<height;i++)
p[i]=NULL;
for(i=0;i<height;i++){
p[i]=(int *)calloc(width, sizeof(int));
if(p[i]==NULL){
puts("Cannot allocate memory.\n");
goto Free;
}
}
}
プログラミング入門2
24
演習課題
 構造体配列の動的確保(kadai11-1.c)

以下のpoint構造体について、




プログラム中で扱える点の数を可変にしたい。
実行時にキーボードから点の数を入力し、それにより動的に
構造体の配列を確保するプログラムを作成せよ。
確保後、全ての座標値を0.0で初期化し、その結果を表示せよ。
最後に確保した領域を解放するのを忘れずに!
typedef struct {
double x;
double y;
} point;
プログラミング入門2
25
演習課題
 2次元配列の動的確保(kadai11-2.c)

N x 3の行列を動的に確保し、2つの行列の和を表示するプ
ログラムを作成せよ。
課題提出方法
・ソースファイル2つをメール添付にて
・期限: 12/24(月)13:00まで
・題目: pro2-11 学籍番号 苗字
プログラミング入門2
26