ジャンル雑多なゲーム・ゲーム制作関連の色々な情報を取り扱っているブログ。最近はBlenderについてが中心。
[C, C++学習]UE4少し触りつつ、C, C++言語再学習ノート-7日目- –ポインタとメモリブロックの動的確保の組み合わせ、文字列応用編、exit()関数、文字列→数値へ変換

[C, C++学習]UE4少し触りつつ、C, C++言語再学習ノート-7日目- –ポインタとメモリブロックの動的確保の組み合わせ、文字列応用編、exit()関数、文字列→数値へ変換

UE4UMGUIエディタ触った続きの話と、ポインタとメモリブロック動的確保を組み合わせる、 文字列をより使いやすく便利にする関数、プログラム即終了させるexit()関数、文字列→数値へ変換する便利な関数 勉強しなおしの記録というか学習ノート-7日目-

Unreal Engin4すごい!(語彙力)ってなったので、少しいじりつつ、むかーしかじった事あるC,C++の学びなおしの学習ノート。この連載記事の詳しい趣旨と注意事項は1日目をご覧ください。

目次

少しだけUE4の話

とりあえず機能中途半端で終わってしまったUE4のUMG UIを触って、ポーズ画面に移行、ゲーム終了、ゲーム再開を公式のドキュメント睨めっこしつつできたので、ご報告。

それと、あまりプログラマがこういう系統のところを序盤に触り過ぎるといけないと以下の記事で見たので、しばらくC, C++習得に専念しようと思います!

外部ページリンク:「ゲームエンジン」と「極め本」の教育活用における落とし穴を避けるには

メモリブロックの動的確保とポインタ変数

メモリブロック(記憶領域)のお話は、以前の記事で書かせていただきましたので割愛いたします。

※もし、メモリブロックについてご存じない方がいらっしゃいましたら、よろしければ過去に書きました記事を参考になさってください。

何かしらの変数などを生成する時、もれなくコンピューター内のどこかしらのメモリブロックを確保してもらうわけなのです。

そして、その割り当てられたメモリブロックのアドレスを参照する方法を一つ前のC,C++学習の記事にて書かせていただきました、アドレスを扱うための特殊な変数『ポインタ変数』です。

※ポインタについても、よくご存じない方はよろしければ過去記事で書いておりますので、ご参考までに。

ということで、このポインタ変数を利用して、メモリブロックの動的確保や解放をする方法について書かせていただきたいと思います!

メモリブロックを動的に確保・再確保・解放する関数

malloc()

書式:型 *変数 = (型 *)malloc(要素数 * sizeof(型));

引数で指定したバイト数だけ動的にメモリブロックを確保します。また、このmalloc()の引数に「NULL」を指定すると、何も動作しません。NULLは「ヌルポインタ」と呼ばれる特殊なポインタで、0であることを意味します。デフォルトでマクロ定義されている文字です。

calloc()

書式:型 *変数 = (型 *)calloc(要素数, sizeof(型));

基本的にはmalloc()と同じですが、確保されたメモリブロックは全て0で初期化されます。

realloc()

書式:型 *変数 = (型 *)realloc(元のポインタ変数, 要素数 * sizeof(型));

一度確保したメモリブロックを違うサイズで確保しなおします。確保し直す部分は可能な限り古いメモリブロックの値を引き継ぎます。全く新しく確保される部分は初期化されません。また、こちらもmalloc()と同様、第一引数にNULLを指定すると何も動作しません。

実は、各メモリ確保の関数の引数でsizeof()使ってサイズを指定しているのは、そこにちゃんと理由があります。それはsize_t型という型で指定しなければならないからなのですが、このsize_t型は、sizeof()関数の返り値の型です。ですから、メモリブロックを確保する際は、sizeof()関数でサイズを指定しての確保を行いましょう。それと、 各メモリブロック確保の関数のデフォルトの型が「void *」なので、確保したいデータ型にキャストしましょう。

こちらのページによると、malloc、calloc、reallocしたら必ずif(ポインタ変数)みたいなチェックを入れるのが基本らしいです。

free()

書式:free(メモリブロックを保有するポインタ);

引数で指定したメモリブロックを開放します。NULLを引数に渡すと何も動作しません。

動的に確保したメモリブロックは、必ずプログラム終了前にfree()使って解放しましょう。

これらの関数を使ってみたもののソースコードが↓になります。

#include <stdio.h>
#include <stdlib.h>

#define SIZE 3

void main() {
	//配列の生成
	int* p1 = (int*)malloc(SIZE * sizeof(int));
	double* p2 = (double*)calloc(SIZE, sizeof(double));
	int* p3;

	if (!p1) {// メモリ確保失敗
		exit(EXIT_FAILURE);
	}
	else {
		for (int i = 0; i < SIZE; i++) {
			// malloc()の場合初期化されないので既に何かしら値が格納されている
			printf("p1[%d] = %d\n", i, p1[i]);
		}
		printf("\n");

		// realloc()でメモリ再確保
		p3 = (int*)realloc(p1, sizeof(int) * 2);
		if (p3) {
			for (int i = 0; i < 2; i++) {
				// 可能な限り開放する前の古いメモリブロックの内容を引き継ぐ
				printf("p3[%d] = %d\n", i, p3[i]);
			}
			free(p3);
		}
		else {//メモリ確保失敗した時、p1のメモリは解放されていないので解放
			free(p1);
		}
		printf("\n");
	}

	if (!p2) {// メモリ確保失敗
		exit(EXIT_FAILURE);
	}
	else {
		for (int i = 0; i < SIZE; i++) {
			// calloc()の場合0で初期化される
			printf("p2[%d] = %lf\n", i, p2[i]);
		}
		free(p2);
	}
}

実行結果↓

p1[0] = 11141312
p1[1] = 11141312
p1[2] = 5439520

p3[0] = 11141312
p3[1] = 11141312

p2[0] = 0.000000
p2[1] = 0.000000
p2[2] = 0.000000

補足:いきなり出してしまったexit()は、プログラム終了の関数です。次の項で解説します。

プログラムを即終了させる関数 –exit()関数

書式: exit(数値);

プログラムを終了させる関数です。

終了ということで、returnを思い浮かべるかと思われますが、違いがあります。

  • return

returnは、その関数を終了し、呼び出し元の関数へ戻ります。(※その関数がmain()である場合は、プログラム終了。)

  • exit

exit()の場合は、その関数がmain()関数であろうとなかろうと、そこで元の関数に戻らずにプログラムを終了させるという点です。

また、exit()関数に入れる引数によって、コマンドプロンプトなどで、実行した際の正常終了・異常終了を、プログラムの終了ステータスとして知ることが出来ます。

引数に”0″が渡されている場合は、正常終了を意味し、引数が”1″以上であれば、基本的にそれは異常終了を意味します。

上の項の「メモリブロックを動的に確保・再確保・解放する関数 –malloc(), calloc(), realloc(), free()」の例に挙げた中で使われているexit()関数の引数に指定されている”EXIT_FAILURE“は、デフォルトでマクロ定義されている定数で、”1″を意味します。

文字列操作応用編

ヘッダーファイル「string.h」をインクルードすることによって利用可能になる、便利な文字列操作・検索関数をいくつかご紹介します。

文字列内の文字を調べる –strchr()

書式: *p = strchr(文字列, 探したい文字);

文字列の先頭から指定した探したい文字を探し、最初に見つかった位置をポインタで返します。見つからなければNULLを返します。文字列終了コード(‘\0’)も文字列とみなされ、strchr(文字列, 0)ということも可能です。ただし、’\0’を超えた場合は検索できません。

char s[] = "abcde", * p;
int len;
if ((p = strchr(s, 'c')) != NULL) {
	printf("cは文字列sの%d番目にあります。", p - s); //実行結果:cは文字列sの2番目にあります。
}

文字列内の文字列を調べる –strstr()

書式:strstr(文字列1, 文字列2);

文字列1から文字列2を探します。もし見つかれば、その場所のアドレスを返します。見つからなければNULLを返します。文字列2がNULLの場合は常に文字列1の先頭アドレスを返します。

char* s = "abcde1123445ccbaa";
char* p = "45c";
char* sp;

if ((sp = strstr(s, p)) != NULL) {
	printf("%sの中に%sは%d番目の場所にありました。", s, p, (sp - s));//実行結果:abcde1123445ccbaaの中に45cは10番目の場所にありました。
}

文字列を連結する –strcat()

書式:strcat(文字列1, 文字列2);

文字列1の後ろに文字列2を連結します。戻り値は連結された後の文字列1と同じものになります。

char s[10] = "abcde";
int len;
// 文字列の連結
strcat(s, "fghij");
printf("s = %s\n", s);// s = abcdefghij

文字列の比較をする –strcmp()

書式:strcmp(文字列1, 文字列2);

文字列1と文字列2を比較し、返り値は、s1とs2が等しければ0、等しくなければそれ以外の整数値が返されます。

char *s = "abcde", * p="abcde";
// 文字列の比較
if (strcmp(s,p) ==0) {
	printf("文字列1と文字列2は等しいです。");// 実行結果:文字列1と文字列2は等しいです。
}

文字列をコピーする –strcpy()

書式:strcpy(文字列1, 文字列2);

文字列1に文字列2の’\0’までコピーします。’\0’もコピーするので、s1はその分も考えて大きさを宣言する必要があります。戻り値はコピー後の文字列。

char s[6];

// 文字列のコピー
strcpy(s, "abc");
printf("s = %s\n", s);// 実行結果:s = abc

文字列の長さを調べる –strlen()

書式:size_t strlen(文字列);

文字列の長さを調べることが出来ます。取得する値は文字列終了コード(‘\0’)を除いた文字列の長さで、文字列の中に終了コードがない場合、以降の領域を冒してでも終了コードを探すことになるので注意してください。戻り値はsize_t型。

char s[5] = "abcde";
int len;
len = strlen(s);
printf("文字列sの長さ: %d\n", len);//文字列sの長さ: 5

文字列→数値へ変換する便利な関数

ヘッダーファイル「stdlib.h」をインクルードすることによって利用することが出来る便利な関数で、文字列を数値へ変化することのできるものをご紹介します。

変換不能である場合には”0″が返されます。

文字列→int型の整数–atoi()

書式:atoi(文字列);

char* s = "112344";
int n = atoi(s);
printf("%d", n);//実行結果:112344

文字列→double型の実数–atof()

書式:atof(文字列);

char* s = "4.195";
double n = atof(s);
printf("%lf", n);//実行結果:4.195000

文字列→long型の整数–atol()

書式:atol(文字列);

char* s = "1234567890";
long n = atof(s);
printf("%ld", n);//実行結果:1234567890

あとがき

前半の動的メモリ確保の話と、exit()の話以外が細々としたもの寄せ集め感がすごい。たらたらと文字の羅列になり気味になってしまったんですが、あとでやる構造体、ストリームとファイルの読み込み書き込み辺りのことのために、結構焦って書き込んでしまったのが多分原因ですね。

押さえておきたいところが、結構固まりとして残ってるんですが、そこに行くために細々としたもの詰め込み回的なものになってしまったので、多分これ、補足記事や解説記事が追加されるんじゃないかという予感がしてます。

ご意見・ご感想・ご質問、また、ここ間違ってるよとか、もっといい方法あるよといったご指摘などございましたら、お手数ですがコメント欄やtwitterアカウントほろほろり(@_horo_horori)へお願いしますm(_ _)m

参考にさせていただいたページ・サイト一覧

Pocket