web-dev-qa-db-ja.com

char *(char配列)の実際の全長を取得する方法は?

char []の場合、次の方法で簡単に長さを取得できます。

char a[] = "aaaaa";
int length = sizeof(a)/sizeof(char); // length=6

ただし、次の方法でchar *の長さを取得するためにこのようにすることはできません。

char *a = new char[10];
int length = sizeof(a)/sizeof(char);

なぜなら、a hereはポインターであり、length hereは常に4(または異なるシステムの他の何か)になるためです。

私の質問は、後でどのようにしてchar *の長さを取得できるかということです。 作成したばかりであるため、あなたはすでにその10を知っていると誰かが私に挑戦するかもしれないことを知っています。この数値を確認するために、はるか昔に戻りたいと思っています。さらに、実際の長さも知りたいです。

もう少し詳しく言うと

  • 本当のlength=5を取得するにはどうすればよいですか?
  • 合計length=10を取得するにはどうすればよいですか?

次の例:

char *a = new char[10]; 
strcpy(a, "hello");
39
herohuyongtao

できません。とにかく、100%の精度ではありません。ポインターには長さ/サイズはありませんが、独自のものです。それが行うことは、charを保持するメモリ内の特定の場所を指すことだけです。その文字が文字列の一部である場合、strlenを使用して、現在ポイントされている文字の後に続く文字を決定できますが、それはarrayを意味しませんあなたの場合、それは大きいです。
基本的に:

pointerarrayではないため、そうではありませんneed配列のサイズを知る必要があります。ポインターは単一の値を指すことができるため、ポインターは配列がなくても存在できます。それが指すメモリがどこにあるかさえ気にしません(読み取り専用、ヒープまたはスタック...は関係ありません)。ポインターはそれ自身以外の長さを持ちません。ポインタはただ...
このことを考慮:

char beep = '\a';
void alert_user(const char *msg, char *signal); //for some reason
alert_user("Hear my super-awsome noise!", &beep); //passing pointer to single char!

void alert_user(const char *msg, char *signal)
{
    printf("%s%c\n", msg, *signal);
}

ポインターは、配列の先頭、末尾、または中間だけでなく、単一の文字にすることができます...
構造体としての文字を考えてください。ヒープ上に単一の構造体を割り当てることがあります。それも、配列なしでポインターを作成します。

ポインターのみを使用して、ポインターが指している配列の大きさを判別することは不可能です。一番近いのは、callocを使用して、ポインターから連続する\ 0文字の数を数えることです。もちろん、それをその配列のキーに割り当て/再割り当てすると機能しません。また、配列のメモリoutside\0も。したがって、この方法を使用することは信頼できず、危険であり、一般的に愚かなことです。しないでください。行う。それ。

別のアナロジー:
ポインターを道路標識と考えて、Town Xを指します。サインは、その町がどのように見えるかを知らず、そこに住んでいる人を知らないか、気にしません(または気にすることができます)。仕事はTown Xの場所を伝えることです。それはその町がどれだけ遠くにあるかを伝えることができますが、どれほど大きいかはわかりません。その情報は、道路標識とは無関係であると見なされます。それはあなたがその方向にあなたを指している道路標識ではなく、町自体を見ることによってのみ見つけることができるものです

したがって、できるのはポインターを使用することだけです。

char a_str[] = "hello";//{h,e,l,l,o,\0}
char *arr_ptr = &a_str[0];
printf("Get length of string -> %d\n", strlen(arr_ptr));

ただし、これはもちろん、配列/文字列が\ 0で終了している場合にのみ機能します。

余談として:

int length = sizeof(a)/sizeof(char);//sizeof char is guaranteed 1, so sizeof(a) is enough

実際にsize_tsizeofの戻り型)をintに割り当てています。

size_t length = sizeof(a)/sizeof(*a);//best use ptr's type -> good habit

size_tは符号なしの型なので、sizeofがより大きな値を返す場合、lengthの値は予期しないものになる可能性があります...

43

char *が0で終了する場合、strlenを使用できます

そうでなければ、その情報を決定する方法はありません

14
Olotiar

次の2つの方法しかありません。

  • char *が指すメモリポインターがC文字列を表す場合(つまり、末尾に0バイトの文字が含まれている場合)、strlen(a)を使用できます。

  • それ以外の場合は、長さをどこかに保存する必要があります。実際、ポインターはonecharのみを指します。しかし、配列の最初の要素を指しているかのように扱うことができます。その配列の「長さ」がわからないため、その情報をどこかに保存する必要があります。

4
DarkDust

ポインタだけを考えると、できません。 new[]に渡した長さを保持するか、std::vectorを使用して長さを追跡し、終了したらメモリを解放する必要があります。

注:この回答はC++ではなくC++のみを対象としています。

3
Mike Seymour
  • C++の場合:

(動的な)サイズを維持するstd::vector<char>を使用するだけです。 (ボーナス、メモリ管理は無料)。

または、(静的)サイズを保持するstd::array<char, 10>

  • 純粋なCの場合:

次のような情報を保持する構造を作成します。

typedef struct {
    char* ptr;
    int size;
} my_array;

my_array malloc_array(int size)
{
    my_array res;
    res.ptr = (char*) malloc(size);
    res.size = size;
    return res;
}

void free_array(my_array array)
{
    free(array.ptr);
}
3
Jarod42

これはEvil™のように聞こえるかもしれませんが、テストしていませんが、'\0'への割り当て時に配列内のすべての値を初期化し、strlen()を使用する方法はありますか?これは、最初の'\0'でカウントを停止するため、いわゆる実際の値を提供します。

さて、今私は考えていますが、Ever™でこれをしないでください。汚れたメモリの山に着地したい場合を除きます。

また、割り当てられたメモリまたは合計メモリについては、環境で提供されている場合、次の関数を使用できます。

2
Siddharth

char * a =新しいchar [10];

私の質問は、どのように文字の長さを取得できるかということです*

それは非常に簡単です。:) 1つのステートメントを追加するだけで十分です。

size_t N = 10;
char *a = new char[N];

これで、割り当てられた配列のサイズを取得できます

std::cout << "The size is " << N << std::endl;

多くの人がここでC標準関数std :: strlenに言及しました。ただし、文字配列の実際のサイズは返しません。格納されている文字列リテラルのサイズのみを返します。

違いは次のとおりです。コードスニペットを例に取る場合

char a[] = "aaaaa";
int length = sizeof(a)/sizeof(char); // length=6

次に、std :: strlen(a)は、コードのように6ではなく5を返します。

したがって、結論は簡単です。文字配列を動的に割り当てる必要がある場合は、クラスstd::stringの使用を検討してください。 methofサイズとその同義語長により、いつでも配列のサイズを取得できます。

例えば

std::string s( "aaaaa" );

std::cout << s.length() << std::endl;

または

std::string s;
s.resize( 10 );

std::cout << s.length() << std::endl;
2

独自のnew関数とdelete関数、および追加のget-size関数を実装できます。

#define CEIL_DIV(x,y) (((x)-1)/(y)+1)

void* my_new(int size)
{
    if (size > 0)
    {
        int* ptr = new int[1+CEIL_DIV(size,sizeof(int))];
        if (ptr)
        {
            ptr[0] = size;
            return ptr+1;
        }
    }
    return 0;
}

void my_delete(void* mem)
{
    int* ptr = (int*)mem-1;
    delete ptr;
}

int my_size(void* mem)
{
    int* ptr = (int*)mem-1;
    return ptr[0];
}

または、同様の方法でnewおよびdelete演算子をオーバーライドできます。

2
barak manos

sizeof演算子を使用すると、オペランドを格納するために必要なストレージの量(バイト単位)が返されます。

文字を格納するために必要なストレージの量は常に1バイトです。したがって、sizeof(char)は常に1を返します。

char a[] = "aaaaa";

int len1 = sizeof(a)/sizeof(char); // length = 6
int len2 = sizeof(a);              // length = 6;

これはlen1len2の両方で同じです。1のこの除算は方程式に影響しないからです。

len1len2の両方が値6を保持する理由は、文字列の終了文字'\0'に関係しています。これは、長さに別の文字を追加する文字でもあります。したがって、長さは予想した5から6になります。

char *a = new char[10];
int length = sizeof(a)/sizeof(char);

長さはここで4であることが判明しましたが、これは正しいです。繰り返しますが、sizeof演算子は、オペランドのストレージ量を返します。この場合、ポインターaです。ポインターは4バイトのストレージを必要とするため、この場合の長さは4です。おそらく32ビットバイナリにコンパイルするからです。 64ビットバイナリを作成した場合、結果は8になります。

この説明はすでにここにあるかもしれません。ちょうど私の2セントを共有したいです。

1
Montaldo

newがコンパイラに応じて配列を割り当てると(私はgnu c ++を使用します)、配列の前のWordには割り当てられたバイト数に関する情報が含まれます。

テストコード:

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

int
main ()
{
    int arraySz;
    char *a;
    unsigned int *q;

    for (arraySz = 5; arraySz <= 64; arraySz++) {

        printf ("%02d - ", arraySz);

        a = new char[arraySz];
        unsigned char *p = (unsigned char *) a;

        q = (unsigned int *) (a - 4);
        printf ("%02d\n", (*q));

        delete[] (a);

    }
}

私のマシン上でダンプ:

05 - 19
06 - 19
07 - 19
08 - 19
09 - 19
10 - 19
11 - 19
12 - 19
13 - 27
14 - 27
15 - 27
16 - 27
17 - 27
18 - 27
19 - 27
20 - 27
21 - 35
22 - 35
23 - 35
24 - 35
25 - 35
26 - 35
27 - 35
28 - 35
29 - 43
30 - 43
31 - 43
32 - 43
33 - 43
34 - 43
35 - 43
36 - 43
37 - 51
38 - 51
39 - 51
40 - 51
41 - 51
42 - 51
43 - 51
44 - 51
45 - 59
46 - 59
47 - 59
48 - 59
49 - 59
50 - 59
51 - 59
52 - 59
53 - 67
54 - 67
55 - 67
56 - 67
57 - 67
58 - 67
59 - 67
60 - 67
61 - 75
62 - 75
63 - 75
64 - 75

この解決策はお勧めしません(ベクトルの方が優れています)が、本当に必死なら、関係を見つけて、ヒープから割り当てられたバイト数を結論付けることができます。

0
sak

C++17(またはそれ以降)では、文字列リテラルのゼロオーバーヘッドラッパーとして std::string_view を使用できます。

0
bobah

次のようなchar *文字列の長さを見つけることができます。

char* mystring = "Hello World";
int length = sprintf(mystring, "%s", mystring);

sprintf()は、mystringをそれ自体に印刷し、印刷された文字数を返します。

0
DevonJohn

バックトラッカー文字を作成できます。たとえば、文字列の末尾に「%」という特殊文字を追加して、その文字の出現を確認できます。
しかし、これは非常に危険な方法です。なぜなら、そのキャラクターはchar *の他の場所にいる可能性があるからです。

char* stringVar = new char[4] ; 
stringVar[0] = 'H' ; 
stringVar[1] = 'E' ; 
stringVar[2] = '$' ; // back-tracker character.
int i = 0 ;
while(1)
{
   if (stringVar[i] == '$')
     break ; 
   i++ ; 
}
//  i is the length of the string.
// you need to make sure, that there is no other $ in the char* 

それ以外の場合、カスタム構造を定義して長さを追跡し、メモリを割り当てます。

0
Pratik Singhal