アフィリエイト広告を利用しています

広告

この広告は30日以上更新がないブログに表示されております。
新規記事の投稿を行うことで、非表示にすることが可能です。
posted by fanblog

2017年11月29日

《その156》 クイックソートを行う汎用関数


 クイックソートを行う汎用関数

 次のプログラムは、新版明解C++中級編 p.126 にあるクイックソートを行う汎用関数 quicksort のコードを少しだけ変更して、それに、動作確認用のプログラムを付け足したものです。

// ------------------------------------
#include <random>
#include <string>
#include <iostream>
using namespace std;

// x, yの指すnバイトの領域を交換する関数。
void memswap(void* x, void* y, size_t n)
{
unsigned char* a
= reinterpret_cast<unsigned char*>(x);
unsigned char* b
= reinterpret_cast<unsigned char*>(y);

for (; n--; a++, b++) {
unsigned char c = *a;
*a = *b;
*b = c;
}
}

void quicksort(
void* base, // 配列へのポインタ
size_t nmemb, // 要素の個数
size_t size, // 要素1つ分の大きさ

// 比較関数用の仮引数
int(*compar)(const void*, const void*))
{
if (nmemb > 1) {
// 配列 base の先頭要素へのポインタを
// キャストして、char* v に代入

const char* v = reinterpret_cast<const char*>(base);

size_t pl = 0; // 検索範囲左端
size_t pr = nmemb - 1; // 検索範囲右端
size_t pt = (pl + pr) / 2; // 基準値の位置

do {
// &v[pt * size] は基準値への
// ポインタ &base[pt] に対応します。

while (
compar(
reinterpret_cast<const void*>(&v[pl * size]),
&v[pt * size]
) < 0
) {
// 検索範囲左端からチェックしていき、
// 基準値のほうが大きければ、
// 検索範囲左端の位置 pl を
// インクリメント。
// 基準値以上の値を見つけたら、pl
// はその位置で停止。

pl++;
}

while (
compar(
reinterpret_cast<const void*>(&v[pr * size]),
&v[pt * size]
) > 0
) {
// 検索範囲右端からチェックしていき、
// 基準値のほうが小さければ、
// 検索範囲右端の位置 pr を
// デクリメント。
// 基準値以下の値を見つけたら、pr
// はその位置で停止。

pr--;
}

if (pl <= pr) {
// 検索範囲の左端と右端が逆転していないとき。
pt = (pl == pt) ? pr : (pr == pt) ? pl : pt;
// 検索範囲の左端が基準値の位置に達している
// 場合は、基準値の位置を現時点での
// 右端の位置に更新。
// 検索範囲の右端が基準値の位置に達している
// 場合は、基準値の位置を現時点での
// 左端の位置に更新。
// それ以外なら、基準値の位置は更新しません。
// ※以上の操作は、関数 memswapが値を交換し
// ても基準値の値が変化しないように
// するため。

// 配列要素 base[pl], base[pr] の値を交換

memswap(
const_cast<void*>
(reinterpret_cast<const void*>(&v[pl * size])),
const_cast<void*>
(reinterpret_cast<const void*>(&v[pr * size])),
size
);

pl++;
if (pr == 0) // 符号無し整数 0 からのデクリメントを避ける。
goto LABEL;
pr--;
}
} while (pl <= pr);

if (0 < pr)
// 左側 0、要素数 pr + 1 として、quicksort関数を再帰呼出し。
quicksort(
const_cast<void*>
(reinterpret_cast<const void*>(&v[0])),
pr + 1,
size,
compar
);
LABEL:
if (pl < nmemb - 1)
// 左側 pl、要素数 nmemb - pl として、quicksort関数を再帰呼出し。
quicksort(
const_cast<void*>
(reinterpret_cast<const void*>(&v[pl * size])),
nmemb - pl,
size,
compar
);
}
}


int acmp(char* x, char* y) { // 比較関数
return strcmp(x, y);
}

int pcmp(char** x, char** y) { // 比較関数
return strcmp(*x, *y);
}

int ncmp(int* x, int* y) { // 比較関数
return *x < *y ? -1 : *x > *y ? 1 : 0;
}


int main() {
char a[][5]
= { "cba", "cabd", "abc", "bcd",
"cab", "b", "a", "cba", "aa" };
const char* p[]
= { "cba", "cabd", "abc", "bcd",
"cab", "b", "a", "cba", "aa" };

// ------------------------------------
cout << "◆ ";
for (int i = 0; i < 9; i++)
cout << a[i] << " ";
cout << '\n';

quicksort(a, sizeof(a) / sizeof(a[0]), sizeof(a[0]),
reinterpret_cast<int(*)(const void*, const void*)>(acmp));

cout << "→ ";
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
std::cout << a[i] << " ";
std::cout << "\n\n";


// ------------------------------------
cout << "\n◆ ";
for (int i = 0; i < 9; i++)
cout << p[i] << " ";
cout << '\n';

quicksort(p, sizeof(p) / sizeof(p[0]), sizeof(p[0]),
reinterpret_cast<int(*)(const void*, const void*)>(pcmp));

cout << "→ ";
for (int i = 0; i < sizeof(p) / sizeof(p[0]); i++)
std::cout << p[i] << " ";
std::cout << "\n\n";


// ------------------------------------
random_device rd;

int num[20];
cout << "\n◆ ";
for (int i = 0; i < 20; i++) {
num[i] = rd() % 90 + 10;
cout << num[i] << ' ';
}
cout << '\n';

quicksort(num, sizeof(num) / sizeof(num[0]), sizeof(num[0]),
reinterpret_cast<int(*)(const void*, const void*)>(ncmp));

cout << "→ ";
for (int i = 0; i < sizeof(num) / sizeof(num[0]); i++)
std::cout << num[i] << ' ';
std::cout << '\n';
}


e03_001902.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





2017年11月28日

《その155》 「 2分探索関数 」 の改良


 「 2分探索関数 」 の改良

 新版明解C++中級編 p.124 にある改良版2分探索関数 binsearchx のコードは、少し誤植があり、そのままではうまく動作しないので、修正した binsearchx関数と、それを利用するプログラムを以下に書きました。

// ------------------------------------
#include <string>
#include <iostream>

void* binsearchx(
const void* key, // キー値へのポインタ
const void* base, // 配列へのポインタ
size_t nmemb, // 要素の個数
size_t size, // 要素1つ分の大きさ
int(*compar)(const void*, const void*)) {
if (nmemb > 0) {
// 検索対象の配列の先頭要素へのポインタ
// の値を const char*型の x に代入

const char* x
= reinterpret_cast<const char*>(base);

// 最初の検索左端は base[0]
size_t pl = 0;

// 最初の検索右端は base[nmemb - 1]
size_t pr = nmemb - 1;

// 比較関数は、&x[pc * size]
// すなわち &base[pc] の指す base[pc]
// の値とkeyの指す値とを比較すること
// になります。

size_t pc;

while (1) {
// pc の値を決めます。
pc = (pl + pr) / 2;

// ポインタ key と
// ポインタ &x[pc * size] を
// 比較関数に渡して、
// 調査結果を int型の comp
// で受け取ります。
// ポインタ &x[pc * size] は
// ポインタ &base[pc] と同じ
// アドレスなので、
// 比較関数内で keyの指す値
// と base[pc] の値とを比較
// でます。

int comp = compar(
key,
reinterpret_cast<const void*>
(&x[pc * size])
);

// comp == 0 は検索成功。
if (comp == 0) {

// base[pc] が keyの指す値と一致
// することが分かりましたが、
// もっと先頭寄りに同じ値が
// あるかも知れないので、
// 添字の値をデクリメントし
// ながら調べてみます。

for (; pc > pl; pc--) {
if (compar(
key,
reinterpret_cast<const void*>
(&x[(pc - 1) * size])
)
)
// compar関数から、0 以外
// の値が返ってきたの
// で、base[pc]より前
// にはもう同じ値は
// ないことが判明。
// よって、この時点で
// break;

break;
}

// ポインタ &x[pc * size]すなわち
// base[pc] を指すポインタ
// を void型にキャストして
// 返却します。

return const_cast<void*>(
reinterpret_cast<const void*>
(&x[pc * size])
);
}

// 探索範囲の先頭と末尾が一致したら、
// 見つからなかったということなの
// で break; し、
// 関数の末尾に飛んで、
// return NULL;

else if (pl == pr)
break;

// comp > 0 ということは、
// keyの指す値 > base[pc]
// よって、探索範囲を後半に
// 絞り込みます。

else if (comp > 0)
pl = pc + 1;

// この行まで来たということは、
// comp < 0
// つまり、
// keyの指す値 < base[pc]
// よって、探索範囲を前半に絞り
// 込みます。

else if (pc > 0)
pr = pc - 1;

// この行まで来たということは、
// comp < 0 && pc == 0
// これ以上、探索範囲を前方にで
// きないので、
// break; し、
// 関数の末尾まで進んで、
// return NULL;

else
break;
}
}
return NULL;
}


int acmp(char* x, char* y) { // 比較関数
return strcmp(x, y);
}

int pcmp(char* x, char** y) { // 比較関数
return strcmp(x, *y);
}

int ncmp(int* x, int* y) { // 比較関数
return *x < *y ? -1 : *x > *y ? 1 : 0;
}

void existence(int n) {
std::cout << n << "番目\n";
}

void non_existence() {
std::cout << "該当なし\n";
}


int main() {
char a[][5]
= { "aaa", "ab", "bab", "bab",
"bab", "bab", "bbba" };
const char* p[]
= { "aaa", "ab", "bab", "bab",
"bab", "bab", "bbba" };
int num[]
= { 11, 25, 25, 25, 25, 25,
25, 57, 65, 77, 80 };

// ------------------------------------
std::cout << "◆";
for (int i = 0; i < 7; i++)
std::cout << a[i] << ' ';
std::cout << '\n';

char key1[10];
std::cout << " 探索する文字列 : ";
std::cin >> key1;
std::cout << " " << key1 << " → ";

void* f = binsearchx(
key1,
a,
sizeof(a) / sizeof(a[0]),
sizeof(a[0]),
reinterpret_cast<int(*)(
const void*,
const void*)
> (acmp)
);

if (f == 0) non_existence();
else existence((char(*)[5])f - a + 1);

// ------------------------------------
std::cout << "\n◆";
for (int i = 0; i < 7; i++)
std::cout << p[i] << ' ';
std::cout << '\n';

char key2[10];
std::cout << " 探索する文字列 : ";
std::cin >> key2;
std::cout << " " << key2 << " → ";

f = binsearchx(
key2,
p,
sizeof(p) / sizeof(p[0]),
sizeof(p[0]),
reinterpret_cast<int(*)(
const void*,
const void*)
>(pcmp)
);

if (f == 0) non_existence();
else existence((char**)f - p + 1);

// ------------------------------------
std::cout << "\n◆";
for (int i = 0; i < 11; i++)
std::cout << num[i] << ' ';
std::cout << '\n';

int key;
std::cout << " 探索する整数値 : ";
std::cin >> key;
std::cout << " " << key << " → ";

f = binsearchx(
&key,
num,
sizeof(num) / sizeof(num[0]),
sizeof(num[0]),
reinterpret_cast<int(*)(
const void*,
const void*)
>(ncmp));

if (f == 0) non_existence();
else existence((int*)f - num + 1);
}


e03_001802.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





2017年11月27日

《その154》 「 int型配列の2分探索関数 」 のプログラム例


 「 int型配列の2分探索関数 」 のプログラム例

 新版明解C++中級編 p.123 にあるint型配列用2分探索関数 binsearch のコードは、1箇所だけ誤植があるので、修正した binsearch関数と、それを利用するプログラムを以下に書きます。

 修正箇所は不等号の部分(赤文字の箇所)だけです。


// ------------------------------------
#include <iostream>

int binsearch(const int *a, int n, int key) {
int pl = 0; // 探索範囲先頭の添字
int pr = n - 1; // 探索範囲末尾の添字
do {
int pc = (pl + pr) / 2;
if (a[pc] == key) // 探索成功
return pc;
else if (a[pc] < key)
pl = pc + 1; // 探索範囲を後半に絞り込む
else
pr = pc - 1; // 探索範囲を前半に絞り込む
} while (pl <= pr);
// "<=" を "<" とすると、このプログラムの例でいえば、
// "9"を見つけることができません

return -1; // 探索失敗
}

void existence(int n) { std::cout << n << "番目\n"; }
void non_existence() { std::cout << "該当なし\n"; }

int main() {
while (1) {
int num[] = { 5, 9, 10, 15, 17 };
std::cout << "◆ 5, 9, 10, 15, 17\n";
int key;
std::cout << " 探索する整数値は?(\"999\"で終了) : "; std::cin >> key;
if (key == 999) break;
std::cout << " → " << key << " … ";
int subscript = binsearch(num, sizeof(num) / sizeof(num[0]), key);
if (subscript == -1) non_existence();
else existence(subscript + 1);
}
}
// ------------------------------------

e03_0017.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その153》 2分探索関数のプログラム例


 2 ブン
 2分探索関数のプログラム例

 新版明解C++中級編 p.122 にある汎用2分探索関数 binsearch のコードには、一部に誤植があると思うので、修正した binsearch関数と、それを利用するプログラムを以下に書きます。

 修正箇所は、binsearch関数の赤文字の部分です。
元のコードのままだと、pr が負になった状態で while文が実行される場合があり得ます。

  comp < 0 で pl=0, pr=1 の状況のとき、
  pc = (pl + pr) / 2 より pc = 0 ですから、
元のコードのままでは、次の
  pr = pc - 1
の計算で、pr が負になります。そして、そのまま while文に戻ることになってしまいます。


// ------------------------------------
#include <string>
#include <iostream>

void* binsearch(
const void* key,
const void* base,
size_t nmemb, size_t size,
int(*compar)(const void*, const void*)
) {
if (nmemb > 0) {
const char* x
= reinterpret_cast<const char*>(base);
size_t pl = 0; // 探索範囲先頭の添字
size_t pr = nmemb - 1; // 探索範囲末尾の添字
size_t pc; // 探索範囲中央の添字

while (true) {
pc = (pl + pr) / 2;
int comp
= compar(
key,
reinterpret_cast<const void*>(&x[pc * size])
);
if (comp == 0) // 探索成功
return const_cast<void*>(
reinterpret_cast<const void*>(&x[pc * size])
);
else if (pl == pr) // 探索範囲がなくなった
break;
else if (comp > 0)
pl = pc + 1; // 探索範囲を後半に絞り込む
else if (pc > 0) // comp < 0
pr = pc - 1; // 探索範囲を前半に絞り込む
else // comp < 0 && pc == 0
break;
}
}
return NULL; // 探索失敗
}

int acmp(char* x, char* y) { // 比較関数
return strcmp(x, y);
}
int pcmp(char* x, char** y) { // 比較関数
return strcmp(x, *y);
}
int ncmp(int* x, int* y) { // 比較関数
return *x < *y ? -1 : *x > *y ? 1 : 0;
}
void existence(int n) { std::cout << n << "番目\n"; }
void non_existence() { std::cout << "該当なし\n"; }

int main() {
char a[][5]
= { "aaa", "aab", "abaa", "abb",
"bab", "bab", "bab", "bbb" };
const char* p[]
= { "aaa", "aab", "abaa", "abb",
"bab", "bab", "bab", "bbb" };
int num[]
= { 11, 21, 25, 31, 37, 44, 54,
57, 65, 65, 65, 71, 88, 99 };

// ------------------------------------
std::cout << "◆";
for (int i = 0; i < 8; i++)
std::cout << a[i] << ' ';
std::cout << '\n';

char key1[10];
std::cout << " 探索する文字列 : ";
std::cin >> key1;
std::cout << " " << key1 << " → ";

void* f = binsearch(
key1,
a,
sizeof(a) / sizeof(a[0]),
sizeof(a[0]),
reinterpret_cast<int(*)(
const void*,
const void*
)>(acmp)
);

if (f == 0) non_existence();
else
existence((char(*)[5])f - a + 1);

// ------------------------------------
std::cout << "\n◆";
for (int i = 0; i < 8; i++)
std::cout << p[i] << ' ';
std::cout << '\n';

char key2[10];
std::cout << " 探索する文字列 : ";
std::cin >> key2;
std::cout << " " << key2 << " → ";


f = binsearch(
key2,
p,
sizeof(p) / sizeof(p[0]),
sizeof(p[0]),
reinterpret_cast<int(*)(
const void*,
const void*
)>(pcmp)
);

if (f == 0) non_existence();
else
existence((char**)f - p + 1);

// ------------------------------------
std::cout << "\n◆";
for (int i = 0; i < 14; i++)
std::cout << num[i] << ' ';
std::cout << '\n';

int key;
std::cout << " 探索する整数値 : ";
std::cin >> key;
std::cout << " " << key << " → ";

f = binsearch(
&key,
num,
sizeof(num) / sizeof(num[0]),
sizeof(num[0]),
reinterpret_cast<int(*)(
const void*,
const void*
)>(ncmp)
);

if (f == 0) non_existence();
else
existence((int*)f - num + 1);
}

e03_001602.png
 元のコードのままだと、最後の探索( 10 を探索 )で、プログラムが暴走してしまいます。



新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その152》 「配列へのポインタを void* で受け取る関数」 の動作 と 返却値の処理


 ◆ 「配列へのポインタを void* で受け取る関数」 の動作 と 返却値の処理 ◆

 たとえば、
 char s[][3] = { "ab", "bc", "ca" ,"d" } と定義すると、下図のように文字が記憶されます。
図にはビットの区切りは書いてありませんが、1文字を表すのに 8ビット使っています。

e03_001502.png
1文字(8ビット)分を大きさ 1 と考えるので、この場合は、大きさ 3 のデータが 4個並んでいます。

 この状況で、main関数から、関数 func を

char* ptr = reinterpret_cast<char*>(func(201, 4, 3)); ・・・

と、呼び出したとします。
3つの引数は 配列 s の「最初の番地へのポインタ 201」、「データの個数 4」、「データの大きさ 3」です。

 ここでは、関数 func は次のように宣言されているものとしました。

void* func(const void* base, size_t nmemb, size_t size);


 関数 func は、受け取った配列が本当は何型なのか知らなくても、例えば、2番目の要素へのポインタなら、受け取った情報から、
    201 + 3 × (2 − 1)
を計算して 204番地であると知ることができます。


 今、関数 func が、関数内での処理の結果、207番地を返却することになったとします。

 main関数は、受け取った 207 を char*型に変換してから、ptr に代入します( 印の式 )。

 ここで
    ptr − s
を計算すると、
    207 − 201 = 6 ですから、
6文字分離れていることがわかります。

 この配列は、要素の大きさが 3文字分ですから、6文字分離れているということは、配列の添字でいえば 2 だけ離れていることになります。
先頭の s[0] から 2 つ先は s[2] です。

 以上のような感じで、main関数は、関数 func が s[2]へのポインタを返してきてくれたことを知ることができます。


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





2017年11月26日

《その151》 配列へのポインタを void* で受け取る関数


 配列へのポインタを void* で受け取る関数

 汎用性を持たせるために配列へのポインタを void*型 で受け取るようにした関数について、次のプログラムでチェックしてみます。
 関数 rtrv_3rd は、受け取った配列 x の第3要素 x[2] へのポインタを返すだけの、単純で実用性のないものですが、void*型を利用して関数に汎用性を持たせる仕組みを見ることができます。


// ------------------------------------------
#include <iostream>
using namespace std;

void* rtrv_3rd(const void* base, size_t nmemb, size_t size) {
// ↑配列の第3要素へのポインタを void*型で返す関数。
// base … 配列の先頭要素へのポインタ
// nmemb … 配列要素の個数
// size … 配列要素の大きさ

if (nmemb < 3) return NULL;
// 配列の要素数が 3未満のときは NULL を返す。
const char* x = reinterpret_cast<const char*>(base);
// 配列の先頭要素を指すポインタ base を型変換して
// const char* x に代入。

return const_cast<void*>(
// 返却値は void*型
reinterpret_cast<const void*>(&x[2 * size])
// char型配列の要素の大きさは 1、配列 base の要素
// の大きさは size なので、
// &base[0] は &x[0 * size] に、
// &base[1] は &x[1 * size] に、
// &base[2] は &x[2 * size] に、それぞれ
// 対応します。

);
}

int main() {
int a[] = { 10, 11, 12, 13, 14, 15 }; // @
double b[] = { 0.0, 1.1, 2.2, 3.3 }; // A
char c[][3] = { "ab", "bc", "ca" }; // B
const char* d[] = { "xy", "yz", "zx" }; // C

// @
int* p =
reinterpret_cast<int*>(
rtrv_3rd(
a,
// 配列の先頭要素へのポインタ
sizeof(a) / sizeof(a[0]),
// 配列要素の個数
sizeof(int)
// 配列要素の大きさ
)
);
if (p != NULL)
cout << a[p - a] << '\n';
// int*型の p, a の差は、int型配列 a の添字の差。
// この場合は p - a = 2 になり、
// a[p - a] は a[2] のことになります。

else
cout << "無し" << '\n';

// A
double* q =
reinterpret_cast<double*>(
rtrv_3rd(
b,
sizeof(b) / sizeof(b[0]),
sizeof(double)
)
);
if (q != NULL)
cout << b[q - b] << '\n';
else
cout << "無し" << '\n';

// B
char* r =
reinterpret_cast<char*>(
rtrv_3rd(
c,
sizeof(c) / sizeof(c[0]),
sizeof(c[0])
)
);
if (r != NULL)
cout << c[(r - c[0]) / sizeof(c[0])] << '\n';
// char*型の r, c[0] の差は
// 「char[3][3]型配列の添字の差」であり、
// それは「c[3]型配列の添字の差」の
// sizeof(c[0])倍です。
// ※sizeof(c[0]) の値は 3。

else
cout << "無し" << '\n';

// C
char** s =
reinterpret_cast<char**>(
// rtrv_3rd関数からの返却値は void*型ですが、実際
// には文字列へのポインタのポインタな
// ので、char**型にキャストして受け取
// ります。

rtrv_3rd(
d,
// 配列 d の要素 d[0], d[1], ・・・ は
//  文字列へのポインタなので、
// d は文字列へのポインタの
// ポインタです。

sizeof(d) / sizeof(d[0]),
sizeof(d[0])
)
);
if (s != NULL)
cout << d[s - d] << '\n';
else
cout << "無し" << '\n';
}
// ------------------------------------------

e03_0014.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





2017年11月25日

《その150》 配列による文字列・ポインタによる文字列


 配列による文字列ポインタによる文字列 に関して、少しだけですが、再確認してみました。

// ------------------------------------------
// 配列による文字列
#include <iostream>
using namespace std;

int main() {
cout << "@\n";
char a[] = "abcdef";
// 配列による文字列を定義します。
cout << a << '\n';
// 文字列"abcdef"が表示されます。
cout << (void*)a << "\n\n";
// a[0] のアドレスです。
// char*型の場合 cout << a とすると、文字列を表示して
// しまうので、void*型に変換してあります。


cout << "A\n";
cin >> a;
// 文字列(文字数 6 以内)を入力します。
cout << a << '\n';
// 入力した文字列が表示されます。
cout << (void*)a << "\n\n";
// a[0]のアドレスです。アドレスに変化がないことから、最初の文字
// 列が、そのアドレス位置のまま書き替えられたことがわ
// かります。

}
// ------------------------------------------

e03_00121.png


// ------------------------------------------
// ポインタによる文字列
#include <iostream>
using namespace std;

int main() {
cout << "@\n";
const char *p = "abcdef";
// ポインタによる文字列を定義します。
cout << p << '\n';
// 文字列"abcdef"が表示されます。
cout << (void*)p << "\n\n";
// p は、const char型の文字列"abcdef"の先頭文字 'a'のアドレスを
// 保持しています。
// char*型の場合 cout << p とすると、文字列を表示して
// しまうので、void*型に変換してあります。


cout << "A\n";
const char *q = p;
// ポインタ q に、pをコピーします。p が保持するアドレスと同じ値
// を q も持つことになります。

cout << q << '\n';
// q が指す先は pが指す先と同じなので、同じ文字列が表示されます。
cout << (void*)q << "\n\n";
// q が保持するアドレス(指す先)は、q が保持するアドレス(指す先)
// と同じです。


cout << "B\n";
p = "PQRSTU";
// 文字列 p を"PQRSTU"に変更します。
cout << p << '\n';
// 文字列"PQRSTU"が表示されます。
cout << (void*)p << "\n\n";
// p は、const char型の文字列"PQRSTU"の先頭文字 'P'のアドレスを
// 保持しています。
// 指す先が変わったので、違うアドレスになりました。


cout << "C\n";
cout << q << '\n';
// 文字列"abcdef"が表示されます。
cout << (void*)q << '\n';
// q が保持するアドレス(指す先)は、前のままです。
}
// ------------------------------------------

e03_00131.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





2017年11月24日

《その149》 汎用ユーティリティ関数 qsort & p.117演習3-7


 qsort関数

// qsort関数(あらゆる型の配列をソートすることができる。)
// #include <cstdlib>
// @ A B
// ↓ ↓ ↓
// void* qsort(void* base, size_t nmemb, sizt_t size,
// int (*compar)(const void*, const void*));
// ↑
// C 比較関数


@は配列の先頭要素へのポインタです。void型として受け取ります。
  (void型ポインタは、どのようなポインタ型でも受け取る汎用ポインタ)
Aは配列要素の個数です。
Bは配列要素の大きさです。
・ 比較関数C 例えば int_cmp を qsort関数に渡す際には、キャストが必要です。
    reinterpret_cast<int(*)(const void*, const void*)>(int_cmp);
   比較関数は、配列要素の大小の判定方法を qsort関数 に教えます。
   比較関数は二つの引数を比較して、第1引数の値が小さければ負の値
   を、大きければ正の値を、二つが等しければ 0 を返す仕様にします。


新版明解C++中級編 p.117 演習3-7
 qsort関数を用いて、以下の二つの配列を昇順にソートするプログラムを作成せよ。
    char a[][7] = {"LISP", "C", "Ada", "Pascal"};
    char* p  = {"LISP", "C", "Ada", "Pascal"};
 ソートを行う部分は、それぞれ独立した関数として実現すること。

// p117_演習3-7
#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace std;

int acmp(char* x, char* y) { // 比較関数
return strcmp(x, y);
}

int pcmp(char** x, char** y) { // 比較関数
return strcmp(*x, *y);
}

void print_a(char a[][7], int na) {
for (int i = 0; i < na; i++)
cout << a[i] << " ";
cout << '\n';
}

void print_p(const char** p, int np) {
for (int i = 0; i < np; i++)
cout << p[i] << " ";
cout << '\n';
}

int main() {
char a[][7] = { "LISP", "C", "Ada", "Pascal" };
const char* p[] = { "LISP", "C", "Ada", "Pascal" };

int na = sizeof(a) / sizeof(a[0]);
int np = sizeof(p) / sizeof(p[0]);

qsort(a, na, sizeof(a[0]),
reinterpret_cast<int(*)(const void*, const void*)>(acmp));
print_a(a, na);

qsort(p, np, sizeof(p[0]),
reinterpret_cast<int(*)(const void*, const void*)>(pcmp));
print_p(p, np);
}

e03_07.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





2017年11月23日

《その148》 汎用ユーティリティ関数 bsearch & p.111演習3-6


 bsearch関数

// bsearch関数(あらゆる型の配列からの探索を行うことができる。)
// #include <cstdlib>
// @ A B C
// ↓ ↓ ↓ ↓
// void* bsearch(const void* key,
// const void* base, size_t nmemb, sizt_t size,
// int (*compar)(const void*, const void*));
// ↑
// D 比較関数


・ bsearch関数は、キー値@と一致する配列要素へのポインタを返します。
  (一致する要素が見つからなければ、空ポインタを返します。)
Aは配列の先頭要素へのポインタです。
・ bsearch関数は、引数@,Avoid型として受け取ります。
  (void型ポインタは、どのようなポインタ型でも受け取る汎用ポインタ)
Bは配列要素の個数です。
Cは配列要素の大きさです。
比較関数D 例えば int_cmp を bsearch関数に渡す際には、キャストが必要です。
    reinterpret_cast<int(*)(const void*, const void*)>(int_cmp);
   比較関数は、配列要素の大小の判定方法を bsearch関数 に教えます
   比較関数は二つの引数を比較して、第1引数の値が小さければ負の値
   を、大きければ正の値を、二つが等しければ 0 を返す仕様
にします。
bsearch関数に渡す探索対象の配列は、ソート済みでなければなりません


 bsearch関数が返す配列要素へのポインタは void*型です。
そのため、次のプログラムでは、それを int*型にキャストしています(void型ポインタをint型にする際は、明示的な型キャストが必要です)。
このプログラムでは、x が int型配列の先頭要素へのポインタ、p がキー値と一致するint型配列要素へのポインタです。
二つのポインタ x, p は同じ int型配列の要素を指す int型ポインタですから、その差、
   p - x
は、p が指す配列要素の添字の値と一致します。

// ------------------------------------------
#include <cstdlib>
#include <iostream>
using namespace std;

int int_cmp(const int* a, const int* b) { // 比較関数
return *a < *b ? -1 : *a > *b ? 1: 0;
}

int main()
{
int x[] = { 15, 18, 18, 23, 39, 57, 68, 72 };
int nx = sizeof(x) / sizeof(x[0]); // 配列の要素数
for (int i = 0; i < nx; i++)
cout << "x[" << i << "]…" << x[i] << " ";
cout << '\n';


int no;
cout << "探索する値 : ";
cin >> no; // キー値が格納されたオブジェクト

int* p = reinterpret_cast<int*>(
bsearch(
&no, // @ キー値が格納されているオブジェクトへのポインタ
x, // A 配列の先頭要素へのポインタ
nx, // B 配列の要素数
sizeof(int), // C 配列要素の大きさ
// D 比較関数へのポインタ(bsearch関数が受け取れる型にキャスト)
reinterpret_cast<int(*)(const void*, const void*)>(int_cmp)
)
);

if (p != NULL)
cout << "x[" << p - x << "]が一致\n";
else
cout << "見つかりません。\n";
}
// ------------------------------------------

e03_00092.png

e03_00102.png



新版明解C++中級編 p.111 演習3-6
 bsearch関数を用いて、文字列の配列からの探索を行うプログラムを作成せよ。
 (@) 「2次元配列で実現された文字列の配列からの探索を行うプログラム」と、
 (A) 「文字列の先頭文字へのポインタの配列で実現された文字列からの探索を行うプログラム」の二つを作ること。

//  (@)
#include <cstring>
#include <cstdlib>
#include <iostream>
using namespace std;

int charstr_cmpr(const char* a, const char* b) {
return strcmp(a, b); // strcmp関数 … ヘッダ<cstring>
}

int main() {
const char s[][10] = { "abc", "abcd", "ac", "bca", "cab", "cba" };

for (int i = 0; i < sizeof(s) / sizeof(s[0]); i++)
cout << s[i] << " ";
cout << '\n';

while (1) {
char key[20];
cout << "◆探索文字列 : key = "; cin >> key;

char* p = reinterpret_cast<char*>(
bsearch(
key, s, sizeof(s) / sizeof(s[0]), sizeof(s[0]),
reinterpret_cast<int(*)(const void*, const void*)>(charstr_cmpr)
)
);
if (p != NULL)
cout << "s[" << (p - s[0]) / sizeof(s[0]) << "]が一致\n";
else
cout << "無し\n";
int cont;
cout << "継続(1) or 終了(0) : "; cin >> cont;
if (!cont) break;
else cout << '\n';
}
}

e03_1101.png


//  (A)
#include <cstring>
#include <cstdlib>
#include <iostream>
using namespace std;

int cmpr(const char* a, const char** b) {
return strcmp(a, *b); // strcmp関数 … ヘッダ<cstring>
}

int main() {
const char* s[] = { "abc", "abcd", "ac", "bca", "cab", "cba" };
for (int i = 0; i < sizeof(s) / sizeof(s[0]); i++)
cout << s[i] << " ";
cout << '\n';

while (1) {
char key[100];
cout << "キー値 : "; cin >> key;

char** p = reinterpret_cast<char**>(
bsearch(
key, s, sizeof(s) / sizeof(s[0]), sizeof(char*),
reinterpret_cast<int(*)(const void*, const void*)>(cmpr)
)
);

if (p != NULL)
cout << "s[" << p - s << "]が一致\n";
else
cout << "無し\n";
int cont;
cout << "継続(1) or 終了(0) : "; cin >> cont;
if (!cont) break;
else cout << '\n';
}
}

e03_1102.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





2017年11月22日

《その147》 関数へのポインタを返す関数


 関数へのポインタを返す関数

 次のプログラムをみてください。赤文字の部分、ちょっと分かりにくいと感じる人もいるのではないでしょうか。自分はその一人なんですが・・・´・ω・`;)


// ------------------------------------------
#include <iostream>
using namespace std;

int a(int n) { // 2乗を計算
return n*n;
}

int b(int n) { // 2倍を計算
return 2 * n;
}

int (*c())(int) {
int tmp;
cout << "1(2乗) or 0(2倍) : "; cin >> tmp;
if (tmp) return a;
else return b;
}


int main() {
while (1) {
int num;
cout << "整数を入力(99 で終了) : "; cin >> num;
if (num == 99) break;
cout << (c())(num) << "\n\n";
}
}
// ------------------------------------------

e03_0008.png

 関数 a, b は、int型の仮引数を受取り int型の値を返却します。
 この関数 a, b へのポインタ型を fp型と名付ける場合、

typedef int (*fp)(int);

とすれば、fp という typedef名を登録できます。


 分かりにくいのは、

int (*c())(int) { ・・・・・・ }

の箇所です。

これは、

int (*☆)(int) c() { ・・・・・・ }

と解釈されるようです。
つまり、

fp c() { ・・・・・・ }

ということです。

 関数 c は、「引数無し」「返却値は関数へのポインタ」です。
関数 c 内で、関数 a, b を選択させて、選ばれたほうを返却します。



 main関数内からの関数 c 呼出しは

(c())(num);

となっています。

 c() の返却値は a, b のどちらかです。
a が返ってきた場合なら

a(num);

となって、num の2乗を求めることになります。


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)






 たまに、クリック お願いします m(_ _)m

 AA にほんブログ村 IT技術ブログ C/C++へ

こうすけ:メール kousuke_cpp@outlook.jp

【1】★★C++ 記事目次★★ ← 利用可能です。
・新版明解C++入門編 / 新版明解C++中級編
・その他 C++ 関連記事

【2】★★こうすけ@C#★★
・C# の初歩的な記事


検索
<< 2017年11月 >>
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
プロフィール
こうすけさんの画像
こうすけ

 たまに、クリック お願いします m(_ _)m

 AA にほんブログ村 IT技術ブログ C/C++へ

こうすけ:メール kousuke_cpp@outlook.jp

【1】★★C++ 記事目次★★ ← 利用可能です。
・新版明解C++入門編 / 新版明解C++中級編
・その他 C++ 関連記事

【2】★★こうすけ@C#★★
・C# の初歩的な記事


×

この広告は30日以上新しい記事の更新がないブログに表示されております。