学生向けプログラミング入門 | 無料

学生向けにプログラミングを無料で解説。Java、C++、Ruby、PHP、データベース、Ruby on Rails, Python, Django

C++ | 24 | ポインターの応用

↓↓クリックして頂けると励みになります。



23 | 構造体の中の構造体】 << 【ホーム】 >> 【25 | リストとファイル入出力


ポインターの応用
ポインターの応用



C++のポインターは、メモリのアドレスを格納する変数であり、そのアドレスに格納されたデータを間接的に参照するために使用されます。
ポインターの応用は、さまざまな方法で行われますが、主な用途は以下のとおりです。

動的メモリ割り当て: ポインターを使用して、実行時にメモリを動的に割り当てることができます。
動的メモリ割り当ては、必要なメモリサイズが実行時にわかる場合や、大きなメモリブロックが必要な場合に便利です。

動的なデータ構造の作成: ポインターを使用して、動的なデータ構造(例えば、リスト、木構造、グラフなど)を作成することができます。動的なデータ構造は、実行時にサイズが決まるか、サイズが大きすぎてスタックには収まらない場合に使用されます。

関数へのポインターの渡し方: 関数にポインターを渡すことで、関数内で引数の値を直接変更することができます。これにより、関数呼び出しのオーバーヘッドを削減し、効率的なプログラミングが可能となります。

クラスとオブジェクトへのポインターの使用: クラスやオブジェクトへのポインターを使用することで、動的にオブジェクトを作成し、プログラムの動作を制御することができます。また、ポインターを使用して、複数のオブジェクトが同じデータを共有することもできます。

ポインターの算術演算: ポインターの算術演算を使用することで、配列やバッファなどの連続したメモリ領域を効率的に操作することができます。これは、メモリ内のデータへの効率的なアクセスやデータ構造の操作に役立ちます。

ポインターのこれらの応用は、C++プログラミングで非常に一般的であり、効率的なメモリ管理や柔軟なデータ構造の実装に重要です。
しかし、ポインターを適切に使用するためには、メモリの解放を忘れないようにするなど、いくつかの注意点があります。


Visual Studio Codeで以下のcppファイルを作成して下さい。


新規作成 【PointerTest6.cpp】

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

int  main(){

    char *s, buf[512];
    std::cout << "文字列を入力してください" << std::endl;
    std::cin >> buf;
    s = (char *)malloc( strlen(buf) + 1 );
    strcpy(s, buf);
    std::cout << s << std::endl;
}



少し大規模なプログラムにおいては扱うデータが不定量になる場合が多いです。
別の言い方をすれば、あらかじめどれだけのメモリを必要とするかわからないと言うこともできます。
そこで必要なメモリを必要なときに必要な分だけ用意することが問題になってきます。
例えば、

char buf[512];

などとして、一行が512文字までであると仮定して宣言します。
問題は次々とユーザーが入力した文字列を失わずに保持することで、そのためにはbuf[]で格納されたデータを別の場所にコピーして保存しなければなりません。
しかしbuf[512]に用意したのと同じように512の大きさの配列を別に用意するのはメモリの無駄遣いになります。
実際ユーザーは一行に1文字しか入力しないかもしれないからです。
従ってユーザーが入力しただけの文字数分のメモリを用意できれば良いのです。
その方法を次に説明します。


必要なときに必要なだけメモリを確保する方法として、C言語では標準関数でmalloc()が用意されています。
malloc()を使う場合はプログラムの先頭で「#include」が必要です。
malloc()は確保したメモリのアドレスを返します。

#include<stdlib.h>
・・・・
    char *s;
    s = malloc(20);

この例では20byteの大きさのメモリを確保し、確保してメモリの先頭アドレスをポインタsに格納しています。
このように確保したメモリはそのままでは行方不明になってしまうので、この場所を覚えておくためにポインタを使います。


malloc()はメモリの大きさを具体的に示す必要がありますが、例えばfloat型が何byteかということはシステムによって違う場合があります。
したがってプログラム中でこうした大きさについて知ることができなければなりません。
このために用意されている演算子が「sizeof」です。


このsizeofを使うと使用しているOSなどに依存しないプログラムが書けるとともに、構造体などのメモリにしめるサイズが不明な型の大きさも取得できます。
従ってmalloc()とともに用いる場合は通常次のように使います。

/* char 20個のメモリ */

char *s;
 s = malloc( sizeof(char) * 20 );

 /* struct Point 10個分のメモリ */

struct Point {
    float x;
    float y;
 };
 struct Point *p;

 p = malloc( sizeod(struct Point) * 10 );



C言語では自動的な型の変換というものがあります。
例えば次のプログラムでは、整数3は代入時に自動的に実数3.0に変換されています。

float x;
x = 3;

このように型を別の型に自動的に変換する機能は便利ですが、ときとしてバグの原因にもなります。
そこでC言語では明示的に型を変換するための単項演算子が用意されており、一般にこの機能をキャストと呼んでいます。


値や変数の前に(型)を置くことによって、希望の型に変換できます。

例
     int y = 3;
    float x;
    x = (float)y;

この例では整数変数yに入っている値3を実数にキャストしていますが、y自体が変化しているわけではない点に注意してください。


メモリの割り当てはmalloc()で行いますが、必要がないメモリをいつまでも放って置いては使えるメモリが少なくなっていきます。
必要がなくなった時点で使ったメモリをシステムに返却する必要があります。
これを行う関数がfree()です。
free()はvoid型ですので何も結果は返しません。
使い方は次のようにします。

/* char 20個分のメモリの確保と開放 */

char *s;
 s = (char *)malloc(sizeof(char) *20 );
・・・・
free(s);

ここで注意しなければならないのは、一度free()してしまった領域を参照したり利用したりしてはいけないという点です。
malloc()でアドレスを返すとき、実際はこの例のようにキャストを行います。
そうすれば「std::cout」などで出力するときアドレスではなく文字列での出力が可能になります。
これはmalloc()を使用するときの基本となりますので覚えておいてください。


malloc()をするにはそのメモリサイズを決める必要がありましたが、文字列では実際の文字列の長さを数える必要があります。
このための関数が「strlen()」です。
strlen()は引数に文字列へのポインタをとり、「¥0」までの文字数を計算して結果として返します。
ただし「¥0」自体は勘定しませんのでmalloc()で利用する場合は1つ多めにしておかなければなりません。

char buf[512];
char *s;
int n;

std::cin >> buf;
n = strlen(buf) +1;
malloc(sizeof(char) * n);
・・・・

上の例では標準入力から読み込んだ文字列の長さ+ ¥0をnに代入し、char型の大きさをかけた分のメモリを確保しています。
C言語ではcharは1byteであると決まっていますので、下記のようにしてもかまいません。

char buf[512] 
char *s;

std::cin >> buf;
(char *)malloc(strlen(buf) + 1);

こうして確保されたメモリ領域にbuf []に入っている文字列を移します。

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

int main() {

    char *s, buf[512];
    std::cin >> buf;

    s = (char *)malloc( strlen(buf) +1);
    strcpy(s, buf);
    std::cout << s << std::endl;
}



標準関数strcpy()について説明します。
この関数は文字列をコピーする関数です。
ファイルの先頭に「#include 」を追加しておかなければなりません。

#define _CRT_SECURE_NO_WARNINGS
        #include <string.h>

     char str1[50];
    char str2[50];
    ・・・・
    strcpy( str1, "yamada" );
    strcpy( str2, str1 );

この例では配列str[]に文字列"yamada"がコピーされます。
さらに次の行でstr1からstr2へのコピーも行っていますが、str1にはその前のコピーで"yamada"がコピーされているので、結局str2にも"yamada"がコピーされます。
ただしstrcpy()は文字数などは数えないので、コピーされる配列は充分な大きさを自分で注意して確保しなければなりません。


実行結果です。

~/Desktop/Programming/CPP $ cd "/Users/**/Desktop/Programming/CPP/" && g++ PointerTest6.cpp -o PointerTest6 && "/Users/**/Desktop/Programming/CPP/"PointerTest6

文字列を入力してください
あいうえお
あいうえお




23 | 構造体の中の構造体】 << 【ホーム】 >> 【25 | リストとファイル入出力





↓↓クリックして頂けると励みになります。