C言語教室 第12回(続) - 回答提出
こちらの記事の課題回答です。
以下、
・引数チェックなし
・const なし
・エラー処理なし
です!
では、順番に。
課題1
コード
int area_rect(int side1, int side2)
{
return (side1 * side2);
}
テストと結果
int area = area_rect(4, 5);
printf("area of rectangle (4, 5) = %d\n", area);
area = area_rect(65535, 65535);
printf("area of rectangle (65535, 65535) = %d\n", area);
area of rectangle (4, 5) = 20
area of rectangle (65535, 65535) = -131071
コメント
オーバーフローあり。
課題2
コード
#define M_AREA_RECT(side1, side2) ((side1) * (side2))
テストと結果
area = M_AREA_RECT(4, 5);
printf("area of rectangle (4, 5) = %d\n", area);
area = M_AREA_RECT(65535, 65535);
printf("area of rectangle (65535, 65535) = %d\n", area);
area of rectangle (4, 5) = 20
area of rectangle (65535, 65535) = -131071
コメント
コンパイル時にワーニングあり。
演算するとオーバーフローするよって。
c_12.c:159:12: warning: overflow in expression; result is -131071 with type 'int' [-Winteger-overflow]
area = M_AREA_RECT(65535, 65535);
^
c_12.c:5:44: note: expanded from macro 'M_AREA_RECT'
#define M_AREA_RECT(side1, side2) ((side1) * (side2))
^ 1 warning generated.
課題3
コード
#define PI 3.14159265359
float area_cycle(int radius)
{
return (PI * radius * radius);
}
テストと結果
float farea = area_cycle(4);
printf("area of cycle (4) = %f\n", farea);
farea = area_cycle(65535);
printf("area of cycle (65535) = %f\n", farea);
area of cycle (4) = 50.265484
area of cycle (65535) = 13492626432.000000
コメント
float になったらオーバーフローしなくなった。
2023.1.31 改訂
(2 * PI * radius * radius) → (PI * radius * radius)
ちなみに、計算結果が変かも。
電卓計算結果
π×4×4=50.265482457437
π×65,535×65,535=13,492,625,932.83132
半径が「65535」のときの計算結果が大きくずれすぎ。
う~ん。
課題4
コード
void clear_array(int a[], int len)
{
int i = 0;
for (i = 0; i < len; ++i)
{
a[i] = 0;
}
}
テストと結果
#define MAX_NUMBERS 256
int a[MAX_NUMBERS] = {255, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
print_array(a, MAX_NUMBERS);
clear_array(a, MAX_NUMBERS);
print_array(a, MAX_NUMBERS);
00ff 0001 0002 0003 0004 0005 0006 0007
0008 0009 000a 000b 000c 000d 000e 000f
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
コメント
標準ライブラリ「memset」を使うのもありかも。
課題5
コード
void get_rand(int a[], int len, int max)
{
int i = 0;
int seed = time(NULL) + clock();
srand(seed);
for (i = 0; i < len; ++i)
{
a[i] = rand() % (max+1);
}
}
テストと結果
#define MAX_NUMBERS 256
get_rand(a, MAX_NUMBERS, 255);
print_number(a, MAX_NUMBERS);
printf("\n");
get_rand(a, MAX_NUMBERS, 255);
print_number(a, MAX_NUMBERS);
printf("\n");
33 74 1 63 27 35 77 83 118 2 13 231
181 209 71 124 38 56 53 50 182 191 166 197
(後略)
165 38 253 166 64 92 57 47 238 137 56 228
166 234 10 165 148 121 142 130 27 180 170 91
(後略)
コメント
あまりにも大きな数値ばかりが並ぶので、勝手に上限設定。
(1)「srand」なしだと、実行する度に毎回同じ値になる。
$ ./a.out
103 198 105 115 81 255 74 236 41 205 186 171
(後略)
8 112 212 178 138 41 84 72 154 10 188 213
(後略)
$ ./a.out
103 198 105 115 81 255 74 236 41 205 186 171
(後略)
8 112 212 178 138 41 84 72 154 10 188 213
(後略)
(2)「srand(time(NULL))」だと、2回の実行結果が同じ値になる。
$ ./a.out
188 201 206 245 20 184 104 163 196 117 217 176
(後略)
188 201 206 245 20 184 104 163 196 117 217 176
(後略)
とかく、コンピューターは乱数が不得手である。
まずは標準ライブラリを眺めてみる。
「clock」。
なんだ、これは。
「使用したプロセッサ時間」。
少なくとも、連続で2回実行した場合、1回目と2回目で違う値になりそう。プラットホーム依存しそうな気もするけど。とりあえず、今の環境では使えそうなのでこれにする。
後は。
「time」に、足す? 掛ける? それとも Xor ?
Xor も面白そうではあるけど、シンプルに「足す」ことにした。
課題6
コード
int get_min(int a[], int len)
{
int i = 0;
int min = a[0];
for (i = 1; i < len; ++i)
{
if (a[i] < min)
{
min = a[i];
}
}
return min;
}
テストと結果
int min = get_min(a, MAX_NUMBERS);
printf("min = %d\n", min);
min = 2
コメント
len = 0 のケースは考慮しない。
課題7
コード
int count_min(int a[], int len, int* min)
{
int i = 0;
int count = 0;
*min = a[0];
for (i = 1; i < len; ++i)
{
if (a[i] == *min)
{
count++;
continue;
}
if (a[i] < *min)
{
*min = a[i];
count = 1;
continue;
}
}
return count;
}
テストと結果
int count = count_min(a, MAX_NUMBERS, &min);
printf("min = %d (%d times)\n", min, count);
min = 2 (1 times)
コメント
2つ目の「continue」はいらないか。
1つ目の「continue」は、2つ目の「if」文を「else」ブロックにするのでもいいのだけど・・・。どちらがいいだろうか。単純に好き嫌いで選んで「continue」。明白な根拠もなし。
課題8
コード
int count_char(char str[], char* max_char)
{
int i = 0;
int max_count = 0;
int count[0x100] = {0};
for (i = 0; str[i] != '\0'; ++i)
{
++(count[str[i]]);
}
max_count = count[0];
*max_char = 0;
char c = 0;
for (c = 1; c < 0x80; ++c)
{
if (max_count < count[c])
{
max_count = count[c];
*max_char = c;
}
}
return max_count;
}
テストと結果
char max_char = 0;
int max_count = 0;
char str[] = "#define M_AREA_RECT(side1, side2) ((side1) * (side2))";
max_count = count_char(str, &max_char);
printf("str = %s\n", str);
printf("mac_char = %c (%d times)\n", max_char, max_count);
コメント
「count」は「0x100」個。
最大個数のチェックは「count[0]~count[0x7f]」。
「count」の配列要素数を「0x100」としたのは、文字の範囲チェックをしたくなかったから。「count[0x80]~count[0xff]」がカウントされることはまずないだろうけど、もしあってもオーバーランしないように。
個数のチェックは0x7f以下を全て対象にした。
制御コードも含む。
1つの関数にループが2つも出てくるのは好きではない。関数分けるのが理想。サボってしまった。
課題9
コード
char *str_ra_cat(char* s1, char* s2)
{
size_t l1 = 0;
size_t l2 = 0;
if (s1 != NULL) {l1 = strlen(s1);}
if (s2 != NULL) {l2 = strlen(s2);}
char* s0 = realloc(s1, (l1 + l2 + 1));
strncat(s0, s2, l2);
return s0;
}
char *str_ra_catcat(char* s1, char* s2, char* s3)
{
char* s1_new = str_ra_cat(NULL, s1);
char* s2_new = str_ra_cat(s1_new, s2);
char* s3_new = str_ra_cat(s2_new, s3);
return s3_new;
}
テストと結果
char s1[] = "abcdef";
char s2[] = "ABCDEF";
char s3[] = "012345";
printf("s1 = %s\n", s1);
printf("s2 = %s\n", s2);
printf("s3 = %s\n", s3);
char* s0 = str_ra_catcat(s1, s2, s3);
printf("s0 = %s\n", s0);
free(s0);
s1 = abcdef
s2 = ABCDEF
s3 = 012345
s0 = abcdefABCDEF012345
コメント
『2つの文字列を連結する関数を書いて使っ』たら、面白いコードになった。
「realloc」の第1引数は NULL でもいいらしい。
でも、「strlen」は NULL はダメのようなので、結局は NULL チェックが必要。
演習
コード
int main(int argc, char** argv)
{
int i = 0;
for (i = 0; i < argc; ++i)
{
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
テストと結果
$ ./a.out aaa bbb ccc ddd 91234 -p
argv[0] = ./a.out
argv[1] = aaa
argv[2] = bbb
argv[3] = ccc
argv[4] = ddd
argv[5] = 91234
argv[6] = -p
$
コメント
特になし。
感想
疲れた~(笑)。
追記
2023/1/30
いちゃもんもつけにくいだろうから、ソースファイルをつけます。
あまり褒められたコードでもないけど。
この記事が気に入ったらサポートをしてみませんか?