この記事のポイント
C言語でプログラムを書く際、メモリサイズを正確に把握することは効率的なコード作成につながります。ここでは、sizeof演算子の基礎知識から実践的な使い方まで詳しく説明していきます。
- データ型やオブジェクトのメモリサイズを取得する演算子の機能
- コンパイル時に計算される演算子の特徴と基本文法
- 配列や構造体でのメモリ管理における活用方法
これらのポイントを押さえることで、より安全で効率的なプログラム開発が可能になります。
sizeof演算子とは?
sizeof演算子は、C言語においてデータ型や変数のメモリサイズをバイト単位で取得するための演算子です。
この演算子は実行時ではなくコンパイル時に値が決まるため、プログラムの実行速度に影響を与えません。主にメモリ管理や配列の要素数計算で使用され、プログラマーが明示的にサイズを計算する必要がなくなります。異なるシステム間での移植性も高まり、環境に依存しないコードが書けます。
動的メモリ確保や構造体のメモリ配置を理解する上でも欠かせない機能といえるでしょう。
【関連】
C言語をもっと詳しく学ぶならpaizaラーニング
基本構文
sizeof演算子の基本的な使い方として、データ型に対して適用する場合と変数に対して適用する場合があります。データ型を指定する場合は括弧が必要で、変数名を指定する場合は括弧を省略できます。戻り値はsize_t型で、符号なし整数として扱われます。以下のコード例で基本的な使用方法を確認してみましょう。
#include <stdio.h>
int main() {
int number = 10;
char character = 'A';
printf("int型のサイズ: %zu bytes\n", sizeof(int));
printf("char型のサイズ: %zu bytes\n", sizeof(char));
printf("変数numberのサイズ: %zu bytes\n", sizeof(number));
printf("変数characterのサイズ: %zu bytes\n", sizeof(character));
return 0;
}
出力結果
int型のサイズ: 4 bytes
char型のサイズ: 1 bytes
変数numberのサイズ: 4 bytes
変数characterのサイズ: 1 bytes
配列に対してsizeof演算子を使用すると、配列全体のメモリサイズが取得できます。この特性を利用して配列の要素数を計算することもできます。
#include <stdio.h>
int main() {
int numbers[5] = {1, 2, 3, 4, 5};
double values[3] = {1.1, 2.2, 3.3};
printf("配列numbersのサイズ: %zu bytes\n", sizeof(numbers));
printf("配列numbersの要素数: %zu\n", sizeof(numbers) / sizeof(numbers[0]));
printf("配列valuesのサイズ: %zu bytes\n", sizeof(values));
return 0;
}
出力結果
配列numbersのサイズ: 20 bytes
配列numbersの要素数: 5
配列valuesのサイズ: 24 bytes
実用例
sizeof演算子は実際のプログラム開発において、さまざまな場面で活用されます。メモリ確保時のサイズ計算、配列の要素数取得、構造体のメモリ配置確認など、具体的な使用例を通して理解を深めていきましょう。
以下のサンプルコードでは、実践的な場面でどのようにsizeof演算子が使われるかを示します。各例では、コードの動作を理解しやすくするため、動物名を使った変数や配列を使用しています。これらの例を参考に、自分のプログラムでもsizeof演算子を効果的に活用してください。
配列要素数の自動計算
配列の要素数を動的に取得する際、sizeof演算子を使用することで配列のサイズ変更時にもコードの修正が不要になります。
#include <stdio.h>
int main() {
char animals[] = {'C', 'a', 't', 'D', 'o', 'g'};
int array_length = sizeof(animals) / sizeof(animals[0]);
printf("配列の要素数: %d\n", array_length);
for(int i = 0; i < array_length; i++) {
printf("animals[%d] = %c\n", i, animals[i]);
}
return 0;
}
出力結果
配列の要素数: 6
animals[0] = C
animals[1] = a
animals[2] = t
animals[3] = D
animals[4] = o
animals[5] = g
構造体のメモリサイズ確認
構造体を定義する際、各メンバーのサイズと構造体全体のサイズを把握することで、メモリ効率を考慮した設計が可能になります。
#include <stdio.h>
struct Animal {
char name[10];
int age;
double weight;
};
int main() {
struct Animal cat;
printf("char[10]のサイズ: %zu bytes\n", sizeof(cat.name));
printf("intのサイズ: %zu bytes\n", sizeof(cat.age));
printf("doubleのサイズ: %zu bytes\n", sizeof(cat.weight));
printf("構造体全体のサイズ: %zu bytes\n", sizeof(struct Animal));
return 0;
}
出力結果
char[10]のサイズ: 10 bytes
intのサイズ: 4 bytes
doubleのサイズ: 8 bytes
構造体全体のサイズ: 24 bytes
動的メモリ確保でのサイズ計算
malloc関数を使用した動的メモリ確保において、sizeof演算子を使うことで可読性の高いコードが書けます。
#include <stdio.h>
#include <stdlib.h>
int main() {
int count = 5;
int *dog_ages = malloc(count * sizeof(int));
if(dog_ages != NULL) {
printf("確保したメモリサイズ: %zu bytes\n", count * sizeof(int));
for(int i = 0; i < count; i++) {
dog_ages[i] = i + 1;
printf("dog_ages[%d] = %d\n", i, dog_ages[i]);
}
free(dog_ages);
}
return 0;
}
出力結果
確保したメモリサイズ: 20 bytes
dog_ages[0] = 1
dog_ages[1] = 2
dog_ages[2] = 3
dog_ages[3] = 4
dog_ages[4] = 5
ポインタと配列のサイズ比較
配列とポインタではsizeof演算子の結果が異なることを理解することで、バグの発生を防ぐことができます。
#include <stdio.h>
void print_array_size(int bird_array[], int length) {
printf("関数内での配列要素数: %d\n", length);
}
int main(void) {
int birds[4] = {1, 2, 3, 4};
int *bird_ptr = birds;
printf("配列のサイズ: %zu bytes\n", sizeof(birds));
printf("ポインタのサイズ: %zu bytes\n", sizeof(bird_ptr));
print_array_size(birds, sizeof(birds) / sizeof(birds[0]));
return 0;
}
出力結果
配列のサイズ: 16 bytes
ポインタのサイズ: 8 bytes
関数内での配列要素数: 4
文字列のメモリサイズ確認
文字配列と文字列リテラルでのメモリ使用量の違いを把握することで、効率的な文字列処理が可能になります。
#include <stdio.h>
#include <string.h>
int main() {
char rabbit_name1[] = "Rabbit";
char rabbit_name2[20] = "Rabbit";
char *rabbit_name3 = "Rabbit";
printf("rabbit_name1のサイズ: %zu bytes\n", sizeof(rabbit_name1));
printf("rabbit_name2のサイズ: %zu bytes\n", sizeof(rabbit_name2));
printf("rabbit_name3のサイズ: %zu bytes\n", sizeof(rabbit_name3));
printf("文字列の長さ: %zu\n", strlen(rabbit_name1));
return 0;
}
出力結果
rabbit_name1のサイズ: 7 bytes
rabbit_name2のサイズ: 20 bytes
rabbit_name3のサイズ: 8 bytes
文字列の長さ: 6
異なるデータ型のサイズ比較
さまざまなデータ型のメモリサイズを比較することで、使用するデータ型を適切に選択できるようになります。
#include <stdio.h>
int main() {
printf("char: %zu bytes\n", sizeof(char));
printf("short: %zu bytes\n", sizeof(short));
printf("int: %zu bytes\n", sizeof(int));
printf("long: %zu bytes\n", sizeof(long));
printf("float: %zu bytes\n", sizeof(float));
printf("double: %zu bytes\n", sizeof(double));
return 0;
}
出力結果
char: 1 bytes
short: 2 bytes
int: 4 bytes
long: 8 bytes
float: 4 bytes
double: 8 bytes
構造体配列でのメモリ計算
構造体配列において、個々の構造体のサイズと配列全体のサイズを把握することで、メモリ使用量を正確に計算できます。
#include <stdio.h>
struct Fish {
char species[15];
float length;
};
int main() {
struct Fish aquarium[3] = {
{"Goldfish", 5.2},
{"Guppy", 2.1},
{"Tetra", 3.5}
};
printf("Fish構造体のサイズ: %zu bytes\n", sizeof(struct Fish));
printf("配列全体のサイズ: %zu bytes\n", sizeof(aquarium));
printf("配列の要素数: %zu\n", sizeof(aquarium) / sizeof(aquarium[0]));
return 0;
}
出力結果
Fish構造体のサイズ: 20 bytes
配列全体のサイズ: 60 bytes
配列の要素数: 3
まとめ
sizeof演算子は、C言語プログラミングにおいてメモリ管理を効率的に行うために欠かせない機能です。コンパイル時に値が決定されるため、プログラムの実行速度に影響を与えずにメモリサイズを取得できます。基本的なデータ型から複雑な構造体まで、あらゆるオブジェクトのサイズを正確に把握することで、より安全で効率的なコードが書けるようになります。
sizeof演算子の活躍する場面
- 配列の要素数を自動計算する処理での活用
- 動的メモリ確保時の正確なサイズ指定での利用
- 構造体のメモリ配置を確認する際の使用
重要なポイント
- コンパイル時に値が決まる演算子の特徴を理解すること
- 配列とポインタでは結果が異なることに注意すること
- 移植性を高めるためにハードコードされたサイズを避けること
sizeof演算子を適切に活用することで、プラットフォームに依存しない堅牢なプログラムが作成でき、メモリ関連のバグを未然に防ぐことができます。実際の開発では、今回紹介した例を参考にして、自分のプログラムに最適な使い方を見つけてください。
レベルを更に上げたい方はpaizaプログラミングスキルチェックへ