2016-05-10 18 views
-4

私はmemset()を実装しようとしています。私のコードは機能的ですが、キャストの使用が良いか悪いかを知りたいのです。タイプコードを減らすためのキャスト

void* __memset(void *b, int c, size_t len){ 
    while (len--) 
     *((unsigned char*)(b++)) = (unsigned char)c; 
    return (b); 
} 

私のコードは長かったが、私はそれを短くするvoid*をキャストすることを決めました。これは大丈夫ですか、コードが破損しますか?

+1

なぜmemsetを実装していますか?それが存在する、それを使用する –

+4

'C'または' C++ 'を選択してください – jboockmann

+4

' void * 'の算術はCの制約違反です – EOF

答えて

1

はのタイプです。+++は有効なCまたはC++コードでもありません。したがって、コードは標準準拠のコンパイラでコンパイルされないため、100%悪いことです。

たとえば、gccには標準でない拡張があり、voidポインターの算術演算が可能です。 gccを正しいコンパイラオプションで標準準拠のコンパイラにすることができます:

gcc -std=c11 -pedantic-errors

Cでのmemsetの典型的な実装は、そうでない場合は、次のようになります。

void* memset(void* s, int c, size_t n) 
{ 
    uint8_t* ptr = (uint8_t*) s; 

    while(n != 0) 
    { 
    *ptr = (uint8_t)c; 
    ptr++; 
    n--; 
    } 

    return s; 
} 

そして、いや、それ不快なワンライナーの混乱にしようとしません。読み取り可能なコードを書く。いずれにせよ、生成されたバイナリは同じになります。

(このコードは、まともなコンパイラがポインタエイリアシングの目的のために、文字型としてuint8_tを扱うことを前提としています。)

+4

'uint8_t'は文字型であることが保証されていません。 'unsigned char'をつけてください。 – EOF

+0

@EOFあなたがコメントを投稿したのと同じように編集でサイドノートを追加していました:)標準でそのような保証はありませんが、uint8_tを文字タイプとして扱わないコンパイラは絶望的に壊れていますとにかく実質的に役に立たない。無駄なコンパイラとの互換性のためにコードを修正するのではなく、コンパイラが役に立たないことを明らかにする方がよいでしょう。 – Lundin

+5

'unsigned char'を使用する理由はありませんか?私は何の欠点も見当たりません。 – EOF

0

どのようにコードを削減し、一般的な機能の恩恵を受けてはどうですか?

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

void *g_memset(void *dst, size_t dstSize, void *val, size_t valSize); 

int main(void) 
{ 
    int val = 4; 
    int dst[5]; 

    size_t valSize = sizeof(val); 
    size_t dstSize = sizeof(dst)/sizeof(dst[0]); 

    g_memset(dst, dstSize, &val, sizeof(val)); 

    for (size_t n = 0; n < dstSize; n++) { 
     printf("%d ", dst[n]); 
    } 

    putchar('\n'); 
    return 0; 
} 


void *g_memset(void *dst, size_t dstSize, void *val, size_t valSize) 
{ 
    char *ptr = (char *)dst; 
    while (dstSize-- > 0) { 
     memcpy(ptr, val, valSize); 
     ptr += valSize; 
    } 
    return dst; 
} 
+0

OPは標準のCライブラリを再開発しようとしているので、それは 'memset'関数の定義方法ではありません。 – user3386109

+0

@ user3386109私は自分のためにそれを保つつもりです:) –

+0

@EOF私は 'g_memset'に名前を変更しました。あなたは今幸せですか? –

2
警告

(これは実際には非常に面白いです):

標準名を再宣言は、それは未定義の動作」を起動しますので、悪い考えです。これは単なる羊毛のコンセプトではありません。 は実際には悪いです。コンパイラは未定義の動作を呼び出さないという前提で決定を下すためです。

memset: 
     testq %rdx, %rdx 
     je  .L6 
     subq $8, %rsp 
     movzbl %sil, %esi 
     call memset 
     addq $8, %rsp 
     ret 
.L6: 
     movq %rdi, %rax 
     ret 

結果:BOOM

は、このアセンブラコードを生成:

#include <stdlib.h> 

void* memset(void *b, int c, size_t len){ 
    unsigned char* p = (unsigned char*)b; 
    while (len--) { 
    *p++ = c; 
    } 
    return b; 
} 

は今gcc5.3 -O3でコンパイルします。

は、このCファイルを考えてみましょう!

+0

うん、私は派生したコードを見たときに笑っていた。同じこと、関数を書き直すためにこのすべての作業を行い、コンパイラは単に組み込み関数をLOLと呼びます。これを除いて、無限再帰を使用するとさらに優れています。 – user3386109

+0

多くのコンパイラは、memset(および他のキー関数)に似たパターンを認識し、関数呼び出しを生成します。これは '-fno-tree-loop-distribute-patterns'でgccで避けることができます。そうしなければ、インラインアセンブリを使わずにCで汎用memsetを書くことは決してできません。 – technosaurus

関連する問題