it-swarm.com.ru

Как получить реальную и общую длину 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 здесь - указатель, такой, что length здесь всегда будет 4 (или что-то другое в других системах).

Мой вопрос заключается в том, как я могу получить длину char * впоследствии? Я знаю, что кто-то может бросить мне вызов, что вы уже знаете его 10, потому что вы только что создали его. Я хочу знать это, потому что этот шаг получения его длины может пройти долгий путь от его создания, и я не хочу возвращаться далеко назад, чтобы проверить это число. Кроме того, я также хочу знать его реальную длину.

Чтобы быть более конкретным

  • как я могу получить его настоящий length=5?
  • как я могу получить его общий length=10?

для следующего примера:

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

Ты не можешь Во всяком случае, не со 100% точностью. Указатель не имеет длины/размера, но имеет свой собственный. Все, что он делает, это указывает на определенное место в памяти, которое содержит символ. Если этот символ является частью строки, то вы можете использовать strlen, чтобы определить, какие символы следуют за тем, на который указывает текущий, но это не означает, что array в вашем случае настолько велика.
В принципе:

Apointerне являетсяarray, поэтому не нужно знать, каков размер массива. Указатель может указывать на одно значение, поэтому указатель может существовать, даже не будучи массивом. Его даже не волнует, где находится память, на которую он указывает (только чтение, куча или стек ... не имеет значения). Указатель не имеет длины, отличной от себя. Указатель просто ...
Учти это:

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_t (тип возврата sizeof) int, лучше всего написать:

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

Поскольку size_t является типом без знака, если sizeof возвращает большие значения, значение length может быть тем, чего вы не ожидали ...

39
Elias Van Ootegem

Если char * завершен 0, вы можете использовать strlen

В противном случае невозможно определить эту информацию.

13
Olotiar

Есть только два способа:

  • Если указатель памяти на ваш char * представляет строку C (то есть содержит символы, у которых есть 0-байт, чтобы отметить его конец), вы можете использовать strlen(a).

  • В противном случае вам нужно где-то хранить длину. На самом деле указатель указывает только на one char. Но мы можем обращаться с ним так, как будто он указывает на первый элемент массива. Поскольку «длина» этого массива неизвестна, вам нужно где-то хранить эту информацию.

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

Это может звучать как Зло ™, и я не проверял его, но как насчет инициализации всех значений в массиве при выделении '\0' и последующем использовании strlen()? Это даст вам так называемое реальное значение, поскольку оно прекратит считать при первом обнаружении '\0'.

Что ж, теперь, когда я думаю об этом, пожалуйста, никогда не делайте этого. Если только вы не захотите приземлиться в кучу грязной памяти.

Кроме того, для выделенной памяти или total memory вы можете использовать следующие функции, если ваша среда предоставляет их:

2
Siddharth

char * a = новый символ [10];

Мой вопрос заключается в том, как я могу получить длину символа *

Это очень просто. :) Достаточно добавить только одно утверждение

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) вернет 5 вместо 6, как в вашем коде.

Таким образом, вывод прост: если вам нужно динамически выделить массив символов, рассмотрите использование класса std::string. Он имеет размер metaof и длину синонима, что позволяет в любой момент получить размер массива.

Например

std::string s( "aaaaa" );

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

или же

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

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

Вы можете реализовать свои собственные функции 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;

Это одинаково как для len1, так и len2, потому что это деление на 1 не влияет на уравнение. 

Причина того, что и len1, и len2 содержат значение 6, связана с символом завершения строки '\0'. Который также является символом, который добавляет другой символ к длине. Поэтому ваша длина будет 6 вместо 5, которые вы ожидали. 

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

Вы уже упоминали, что длина здесь равна 4, что правильно. Опять же, оператор sizeof возвращает объем памяти для операнда, а в вашем случае это указатель a. Указатель требует 4 байта памяти и, следовательно, длина составляет 4 в этом случае. Поскольку вы, вероятно, скомпилируете его в 32-разрядный двоичный файл. Если бы вы создали 64-битный двоичный файл, результат был бы 8.

Это объяснение может быть уже здесь. Просто хочу поделиться своими двумя центами.

1
Montaldo

когда new выделяет массив, в зависимости от компилятора (я использую gnu c ++), слово перед массивом содержит информацию о количестве выделенных байтов.

Тестовый код:

#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* 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