ポインタ④(バイト単位の処理) 解答ページ | Programming Place Plus C言語編 第34章

トップページC言語編第34章

問題① 🔗

問題① 次のプログラムの実行結果はどうなるでしょうか?

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

int main(void)
{
    int a[5];
    
    memset(a, 0xff, sizeof(a));
    
    for (int i = 0; i < 5; ++i) {
        printf("%u ", a[i]);
    }
    printf("\n");
}


配列 a を構成する各バイトに 0xff が書き込まれます。これはビットの並びでいえば「11111111」ということです。

配列 a の要素は int型で、ここでは 32ビットであるとします。すると、たとえば a[0] の状態は「11111111 11111111 11111111 11111111」となります。a[1]、a[2] など、他の要素も同様です。

printf関数で要素の値を出力するとき、“%u” 変換指定子を使っているので、巨大な正の数として出力されることになります。結果はこうです。

実行結果:

4294967295 4294967295 4294967295 4294967295 4294967295 

問題② 🔗

問題② memcmp関数を自作してください。


本編で、memchr関数の自作を行いました。基本的な考え方は同じで、voidポインタを char型のポインタに変換し、1バイトずつの操作を行えばよいです。

また、文字列特化版の strcmp関数の自作を、第33章の練習問題で行いました。ここではできるだけ、strcmp関数のときに似せた形で実装した例を挙げておきます。

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

int my_memcmp(const void* s1, const void* s2, size_t n)
{
    const char* p1 = s1;  // 1バイトずつ操作するため char型のポインタを用意
    const char* p2 = s2;  // 1バイトずつ操作するため char型のポインタを用意

    for (size_t i = 0; *p1 == *p2; ++i) {
        
        // 比較する範囲が終わっても、*p1 == *p2 を満たし続けたのなら、
        // 両範囲は一致している
        if (i >= n) {
            return 0;
        }
        
        // 1バイトずつ進める
        ++p1;
        ++p2;
    }
    
    // 途中で一致しなくなったなら、その位置で大小比較して、適切な戻り値を返す
    return (*p1 < *p2) ? -1 : 1;
}

int main(void)
{
    int a1[] = {0, 1, 2, 3, 4};
    int a2[] = {0, 1, 2, 4, 8};

    printf("%d\n", my_memcmp(a1, a2, sizeof(a1)));

    memcpy(a1, a2, sizeof(a1));
    printf("%d\n", my_memcmp(a1, a2, sizeof(a1)));

    a1[0] = 100;
    printf("%d\n", my_memcmp(a1, a2, sizeof(a1)));
}

実行結果:

-1
0
1

問題③ 🔗

問題③ memcpy関数を自作してください。


たとえば、次のようになります。

#include <stdio.h>

#define SIZE_OF_ARRAY(arrary)  (sizeof(array)/sizeof(array[0]))

void* my_memcpy(void* s1, const void* s2, size_t size);

int main(void)
{
    int array[] = {0, 1, 2, 4, 8};
    int result[5];

    my_memcpy(result, array, sizeof(array));

    for (int i = 0; i < SIZE_OF_ARRAY(result); ++i) {
        printf("%d\n", result[i]);
    }
}

/*
    メモリ上のデータをコピーする。
    引数
        s1: コピー先のメモリアドレス。
        s2: コピー元のメモリアドレス。
        size:   コピーするデータの大きさ。
    戻り値
        s1 と同じメモリアドレス。
*/
void* my_memcpy(void* s1, const void* s2, size_t size)
{
    char* dest = s1;
    const char* src = s2;

    for (size_t i = 0; i < size; ++i) {
        *dest = *src;
        ++dest;
        ++src;
    }

    return s1;
}

実行結果:

0
1
2
4
8

これも、voidポインタを char型のポインタに型変換して、1バイトずつ処理を行います。

【上級】本物の標準ライブラリの memcpy関数は、大抵は高度な最適化が施されており、もはや、C言語のコードで書かれていないことが多いです。たとえば、CPU が用意している命令を直接呼び出すアセンブリのコードになっているなどします。


参考リンク 🔗


更新履歴 🔗

≪さらに古い更新履歴≫

 第38章から練習問題⑬を移動してきて、練習問題③とした。

 この章にあったものを第35章へ移動して統合。

 章のタイトルを変更(「ポインタ④ 動的なメモリ割り当て①」->「ポインタ④(バイト単位の処理)」)

 全面的に文章を見直し、修正を行った。
章のタイトルを変更(「動的なメモリ」->「動的なメモリ割り当て」)

 「*演算子」を「間接演算子」に統一。

 C言語編全体で表記を統一するため、「“%d”フォーマット」のような表現に「変換指定子」を使うように改めた。

 calloc関数の実引数の順番が逆になっていたのを修正。

 新規作成。



第34章のメインページへ

C言語編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る