4

私は C を学ぼうとしています。手始めに、自分の練習用に strcpy を書き始めました。ご存知のように、元の strcpy ではセキュリティ上の問題が発生しやすいため、「安全な」strcpy を作成する作業を自分自身に課しました。

私が選択したパスは、ソース文字列 (文字配列) が実際に宛先メモリに収まるかどうかを確認することです。私が理解しているように、C の文字列は文字配列へのポインタに過ぎず、0x00 で終了します。

だから私の課題は、コンパイラが宛先文字列のために実際に予約したメモリの量を見つける方法ですか?

私は試した:

sizeof(dest)

しかし、それは機能しません。(後でわかったように)実際にはポインターである dest のサイズを返し、私の 64 ビット マシンでは常に 8 を返すためです。

私も試しました:

strlen(dest)

ただし、最初の 0x0 が検出されるまでの長さを返すだけであり、予約されている実際のメモリを必ずしも反映していないため、これも機能しません。

したがって、これはすべて次の質問に要約されます。目的の「文字列」のためにコンパイラが予約したメモリの量を見つけるにはどうすればよいですか???

例:

char s[80] = "";
int i = someFunction(s); // should return 80

「someFunction」とは?

前もって感謝します!

4

8 に答える 8

4

作成中の関数に char ポインターを渡すと、s に割り当てられているメモリの量がわかりません。このサイズを引数として関数に渡す必要があります。

于 2013-01-27T14:07:53.813 に答える
2

したがって、これはすべて次の質問に要約されます。目的の「文字列」のためにコンパイラが予約したメモリの量を見つけるにはどうすればよいですか???

どのくらいのメモリが割り当てられているかを調べる移植可能な方法はありません。自分で追跡する必要があります。

実装は、ポインターに渡されたメモリの量を追跡する必要がmallocあり、何かを見つけられるようにする場合があります。たとえば、glibc のmalloc.hエクスポーズ

size_t malloc_usable_size (void *__ptr)

これにより、大まかにその情報にアクセスできますが、要求した量はわかりませんが、使用可能な量はわかりません。もちろん、それはあなたがmalloc(そして友達から)得たポインターでのみ機能します。sizeof配列の場合、配列自体がスコープ内にある場合にのみ使用できます。

于 2013-01-27T14:13:02.927 に答える
2

sizeof を使用して、コンパイル時にチェックできます。

char s[80] = "";
int i = sizeof s ; // should return 80

s がポインターの場合、これは失敗することに注意してください。

char *s = "";
int j = sizeof s;  /* probably 4 or 8. */

配列はポインターではありません。ポインターに割り当てられたサイズを追跡するには、プログラムは単純にそれを追跡する必要があります。また、配列を関数に渡すことはできません。関数への引数として配列を使用すると、コンパイラはそれを最初の要素へのポインターに変換するため、呼び出された関数でサイズを使用できるようにする場合は、パラメーターとして渡す必要があります。例えば:

char s[ SIZ ] = "";
foo( s, sizeof s );
于 2013-01-27T14:08:48.210 に答える
1
char s[80] = "";
int i = someFunction(s); // should return 80

sでは、配列の最初の要素へのポインターsです。最初の要素へのポインターの値の情報だけでは、配列オブジェクトのサイズを推測することはできません。できる唯一のことは、配列を宣言した後に配列のサイズの情報を格納し (ここではsizeof s)、この情報を必要とする関数に渡すことです。

于 2013-01-27T14:11:19.683 に答える
1

それを行うポータブルな方法はありません。ただし、実装は、この情報を内部的に知る必要があります。Linux や OS X などの Unix ベースの OS は、このタスクのための機能を提供します。

// OS X
#include <malloc/malloc.h>

size_t allocated = malloc_size(somePtr);

// Linux
#include <malloc.h>

size_t allocated = malloc_usable_size(somePtr);


// Maybe Windows...

size_t allocated = _msize(somePtr);
于 2013-01-27T14:13:34.673 に答える
0

配列とポインターは互換性のあるように見えますが、重要な点が 1 つあります。配列にはサイズがあります。ただし、配列が関数に渡されるとポインターに「劣化」するため、サイズ情報は失われます。

ポイントは、ある時点でオブジェクトのサイズ知っているということです。これは、オブジェクトを割り当てたか、特定のサイズであると宣言したためです。C 言語では、必要に応じてその情報を保持および配布する責任があります。だからあなたの例の後:

char s[80] = "";  // sizeof(s) here is 80, because an array has size
int i = someFunction(s, sizeof(s)) ; // You have to tell the function how big the array is.

内の配列のサイズを決定する「魔法の」方法はありません。someFunction()その情報は破棄されるためです (パフォーマンスと効率の理由から - C はこの点で比較的低レベルであり、明示的でないコードやデータを追加しません)。 ; 情報が必要な場合は、明示的に渡す必要があります。

文字列を渡してサイズ情報を保持し、参照ではなくコピーで文字列を渡す方法の 1 つは、次のように文字列を構造体でラップすることです。

typedef struct
{
    char s[80] ;

} charArray_t ;

それから

charArray_t s ;
int i = someFunction( &s ) ;

次のような定義でsomeFunction():

int someFunction( charArray_t* s ) 
{
    return sizeof( s->s ) ; 
}

ただし、それを行っても実際にはあまり得られません。追加のパラメーターは避けてください。実際には、任意の配列ではなく、someFunction()によって定義された固定長の配列のみを使用するようになったため、ある程度の柔軟性が失われます。charrArray_tこのような制限が役立つ場合があります。このアプローチの特徴は、次のことができることですpass by copy

int i = someFunction( s ) ;

それから

int someFunction( charArray_t s ) 
{
    return sizeof( s.s ) ; 
}

配列とは異なり、構造体はこの方法で渡すことができるためです。コピーでも同様に返却できます。ただし、やや非効率的である可能性があります。ただし、利便性と安全性が非効率性を上回る場合もあります。

于 2013-01-27T16:05:10.413 に答える
0

malloc によって返されるメンバーにタグを付ける方法は、常に余分な sizeof(size_t) バイトを malloc することです。それを malloc が返すアドレスに追加すると、実際の長さを格納するためのストレージ スペースができます。malloced サイズ - sizeof (size_t) をそこに保存すると、新しい関数セットの基礎ができます。

これらの種類のポインターのうちの 2 つを新しい特別な strcpy に渡すと、ポインターから sizeof(size_t) を差し引いて、サイズに直接アクセスできます。これにより、メモリを安全にコピーできるかどうかを判断できます。

strcat を実行している場合、2 つのサイズと strlens の計算は、strcat の結果がメモリをオーバーフローするかどうかを確認するために同じ種類のチェックを実行できることを意味します。

それは実行可能です。それはおそらく価値があるよりも多くのトラブルです。

誤って割り当てられていない文字ポインターを渡すとどうなるかを考えてみましょう。サイズはポインターの前にあると想定されています。その仮定は誤りです。その場合にサイズにアクセスしようとすると、未定義の動作になります。運が良ければ合図が届くかも。

この種の実装のもう 1 つの意味は、メモリを解放するときに、malloc が返す正確なポインタを渡す必要があることです。これを正しく行わないと、ヒープが破損する可能性があります。

簡単に言えば...そのようにしないでください。

于 2013-01-27T14:47:10.873 に答える
0

プログラムで文字バッファーを使用している状況では、スモークとミラーを実行して、必要な効果を得ることができます。このようなもの。

char input[] = "test";
char output[3];

if (sizeof(output) < sizeof(input))
{
    memcpy(output,input,sizeof(input) + 1);
}
else
{
    printf("Overflow detected value <%s>\n",input);
}

コードをマクロでラップすることにより、エラー メッセージを改善できます。

#define STRCPYX(output,input)                                        \
if (sizeof(output) < sizeof(input))                                  \
{                                                                    \
    memcpy(output,input,sizeof(input) + 1);                          \
}                                                                    \
else                                                                 \
{                                                                    \
    printf("STRCPYX would overflow %s with value <%s> from %s\n",    \
                                   #output,       input,   #input);  \
}                                                                    \

char input[] = "test";
char output[3];
STRCPYX(output,input);

これにより、必要なものが得られますが、同じ種類のリスクが適用されます.

char *input = "testing 123 testing";
char output[9];
STRCPYX(output,input);

入力のサイズが 8、出力が 9 の場合、出力の値は "Testing" になります。

C 言語は、プログラマーが間違ったことをするのを防ぐようには設計されていません。上流でパドルしようとしているようなものです :) 考えるのに良い練習になります。

于 2013-01-27T15:07:41.893 に答える