wake-up-neo.com

Wie kann ich die tatsächliche Länge und Gesamtlänge von char * (char array) ermitteln?

Für einen char [] kann ich seine Länge leicht ermitteln durch:

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

Ich kann dies jedoch nicht tun, um die Länge eines char * zu erhalten:

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

ich weiß, dass a hier ein Zeiger ist, so dass length hier immer 4 (oder etwas anderes in verschiedenen Systemen) ist.

Meine Frage ist, wie kann ich danach die Länge eines char * bekommen? Ich weiß, jemand kann mich herausfordern, dass Sie bereits 10 wissen, weil Sie es gerade erstellt haben. Ich möchte das wissen, weil dieser Schritt des Erlangens seiner Länge möglicherweise einen langen Weg von seiner Entstehung hat und ich möchte nicht lange zurückkommen, um diese Zahl zu überprüfen. Außerdem möchte ich auch die tatsächliche Länge erfahren.

Um genauer zu sein

  • wie kann ich seinen echten length=5 bekommen?
  • wie kann ich den gesamten length=10 bekommen?

für das folgende Beispiel:

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

Du kannst nicht Jedenfalls nicht mit 100% Genauigkeit. Der Zeiger hat keine Länge/Größe, sondern eine eigene. Alles, was es tut, ist auf einen bestimmten Ort im Speicher zu zeigen, der ein Zeichen enthält. Wenn dieses Zeichen Teil einer Zeichenfolge ist, können Sie strlen verwenden, um zu bestimmen, welche Zeichen dem Zeichen folgen, auf das aktuell gezeigt wird. Dies bedeutet jedoch nicht, dass array in Ihrem Fall so groß ist.
Grundsätzlich gilt:

Einpointerist keinarray, daher braucht [ nicht, um die Größe des Arrays zu kennen. Ein Zeiger kann auf einen einzelnen Wert zeigen, sodass ein Zeiger existieren kann, ohne dass es sogar ein Array gibt. Es ist nicht einmal wichtig, wo sich der Speicher befindet, auf den er verweist (schreibgeschützt, Heap oder Stack ... spielt keine Rolle). Ein Zeiger hat keine andere Länge als sich selbst. Ein Zeiger ist nur ...
Bedenken Sie:

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);
}

Ein Zeiger kann ein einzelnes Zeichen sowie Anfang, Ende oder Mitte eines Arrays sein.
Zeichen als Strukturen betrachten. Manchmal weisen Sie dem Heap eine einzelne Struktur zu. Auch das erzeugt einen Zeiger ohne Array.

Es ist unmöglich, nur einen Zeiger zu verwenden, um festzustellen, wie groß ein Array ist, auf das es zeigt. Am nächsten kommen Sie mit calloc und der Anzahl aufeinanderfolgender\0-Zeichen, die Sie durch den Zeiger finden können. Das funktioniert natürlich nicht, wenn Sie den Schlüsseln dieses Arrays Dinge zugewiesen/neu zugewiesen haben, und es schlägt auch fehl, wenn der Speicher nur außerhalb des Arrays zufällig auch \0 enthält. Die Verwendung dieser Methode ist also unzuverlässig, gefährlich und generell dumm. Nicht Tun. Es.

Eine andere Analogie:
.__ Stellen Sie sich einen Zeiger als Straßenschild vor, er zeigt auf Town X. Das Schild weiß nicht, wie diese Stadt aussieht, und es weiß nicht, oder kümmert sich nicht, wer dort lebt. Es ist Aufgabe, Ihnen zu sagen, wo Sie Town X finden. Es kann Ihnen nur sagen, wie weit die Stadt ist, aber nicht, wie groß sie ist. Diese Information wird für Verkehrszeichen als irrelevant angesehen. Das können Sie nur beim Blick auf die Stadt selbst feststellen, nicht auf die Straßenschilder, die Sie in ihre Richtung weisen

Mit einem Zeiger können Sie also nur Folgendes tun:

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));

Dies funktioniert aber natürlich nur, wenn das Array/der String\0-terminiert ist.

Als Hilfsmittel:

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

weist size_t (den Rückgabetyp von sizeof) tatsächlich einer int zu, schreiben Sie am besten:

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

Da size_t ein vorzeichenloser Typ ist, wenn sizeof größere Werte zurückgibt, könnte der Wert von length etwas sein, das Sie nicht erwartet hätten ...

39

Wenn der char * 0-terminiert ist, können Sie strlen verwenden.

Ansonsten gibt es keine Möglichkeit, diese Informationen zu ermitteln

13
Olotiar

Es gibt nur zwei Möglichkeiten:

  • Wenn der Speicherzeiger von char * eine C-Zeichenfolge darstellt (dh Zeichen enthält, deren Ende ein 0-Byte-Zeichen enthält), können Sie strlen(a) verwenden.

  • Andernfalls müssen Sie die Länge irgendwo speichern. Tatsächlich zeigt der Zeiger nur auf one char. Wir können es jedoch so behandeln, als würde es auf das erste Element eines Arrays zeigen. Da die Länge dieses Arrays nicht bekannt ist, müssen Sie diese Informationen irgendwo speichern.

4
DarkDust

Nur mit dem Zeiger können Sie nicht. Sie müssen die Länge, die Sie an new[] übergeben haben, behalten, oder besser, verwenden Sie std::vector, um sowohl die Länge zu verfolgen als auch den Speicher freizugeben, wenn Sie damit fertig sind.

Hinweis: Diese Antwort bezieht sich nur auf C++, nicht auf C.

3
Mike Seymour
  • In C++:

Verwenden Sie einfach std::vector<char>, um die (dynamische) Größe für Sie zu behalten. (Bonus, Speicherverwaltung kostenlos).

Oder std::array<char, 10>, der die (statische) Größe beibehält.

  • In reinem C:

Erstellen Sie eine Struktur, um die Informationen zu behalten, etwa:

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

Das mag Evil ™ klingen, und ich habe es nicht getestet, aber wie wäre es, alle Werte in einem Array bei der Zuordnung zu '\0' zu initialisieren und dann strlen() zu verwenden? Dies würde Ihnen Ihren sogenannten real value geben, da er beim ersten '\0', auf den er trifft, nicht mehr gezählt wird.

Nun, wenn ich darüber nachdenke, tun Sie das bitte nie. Wenn Sie nicht in einem Haufen schmutziger Speicher landen möchten.

Für den zugewiesenen Speicher oder den total memory können Sie die folgenden Funktionen verwenden, wenn diese von Ihrer Umgebung bereitgestellt werden:

2
Siddharth

char * a = neues Zeichen [10];

Meine Frage ist, wie bekomme ich die Länge eines Zeichens?

Es ist sehr einfach. :) Es reicht aus, nur eine Aussage hinzuzufügen

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

Jetzt können Sie die Größe des zugewiesenen Arrays ermitteln

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

Viele hier erwähnte C-Standardfunktionen std :: strlen. Die tatsächliche Größe eines Zeichen-Arrays wird jedoch nicht zurückgegeben. Es wird nur die Größe des gespeicherten String-Literal zurückgegeben.

Der Unterschied ist folgender. Wenn Sie Ihr Code-Snippet als Beispiel nehmen möchten

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

dann gibt std :: strlen (a) 5 statt 6 zurück, wie in Ihrem Code.

Die Schlussfolgerung ist also einfach: Wenn Sie ein Zeichenarray dynamisch zuweisen müssen, sollten Sie die Verwendung der Klasse std::string berücksichtigen. Es hat eine Methof-Größe und sein Synonym, mit dem die Größe des Arrays jederzeit abgerufen werden kann.

Zum Beispiel

std::string s( "aaaaa" );

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

oder

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

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

Sie können Ihre eigenen new- und delete-Funktionen sowie eine zusätzliche get-size-Funktion implementieren:

#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];
}

Alternativ können Sie die Operatoren new und delete auf ähnliche Weise überschreiben.

2
barak manos

Der Operator sizeof gibt also die Menge an Speicher zurück, die in Byte zum Speichern des Operanden benötigt wird. 

Der zum Speichern eines Zeichens erforderliche Speicherplatz beträgt immer 1 Byte. Die sizeof(char) gibt also immer 1 zurück.

char a[] = "aaaaa";

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

Dies ist für len1 und len2 gleich, da diese Division von 1 die Gleichung nicht beeinflusst. 

Der Grund, warum sowohl len1 als auch len2 den Wert 6 tragen, hängt mit der Zeichenfolge terminierung char '\0' zusammen. Welches ist auch ein Zeichen, das ein anderes Zeichen zur Länge hinzufügt. Daher wird Ihre Länge 6 anstelle der 5 sein, die Sie erwartet haben. 

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

Sie haben bereits erwähnt, dass die Länge hier 4 beträgt, was richtig ist. Der Operator sizeof gibt den Speicherbetrag für den Operanden zurück und in Ihrem Fall handelt es sich um einen Zeiger a. Ein Zeiger benötigt 4 Bytes Speicher, daher beträgt die Länge in diesem Fall 4. Da kompiliert man es wahrscheinlich zu einer 32-Bit-Binärdatei. Wenn Sie eine 64-Bit-Binärdatei erstellt hätten, wäre das Ergebnis 8.

Diese Erklärung könnte hier schon sein. Ich möchte nur meine zwei Cent teilen.

1
Montaldo

wenn new ein Array zuordnet, hängt vom Word vor dem Array abhängig vom Compiler (ich verwende gnu c ++) Informationen über die Anzahl der zugewiesenen Bytes.

Der Testcode:

#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);

    }
}

auf meiner Maschine kippt aus:

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

Ich würde diese Lösung nicht empfehlen (Vektor ist besser), aber wenn Sie wirklich verzweifelt sind, könnten Sie eine Beziehung finden und in der Lage sein, die Anzahl der Bytes aus dem Heap zu ermitteln.

0
sak

In C++17 (oder neuer) können Sie den std::string_view als Null-Overhead-Wrapper für String-Literale verwenden.

0
bobah

Sie können die Länge eines Zeichens wie folgt ermitteln:

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

sprintf () druckt den Mystring auf sich selbst und gibt die Anzahl der gedruckten Zeichen zurück.

0
DevonJohn

Sie können ein Back-Tracker-Zeichen erstellen, z. B. können Sie an das Ende Ihrer Zeichenfolge ein beliebiges Sonderzeichen anhängen, z.
Aber dies ist ein sehr riskanter Weg, da dieser Charakter an anderen Stellen auch im Char * sein kann.

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* 

Andernfalls definieren Sie eine benutzerdefinierte Struktur, um die Länge zu verfolgen und Speicher zuzuweisen. 

0
Pratik Singhal