【问题标题】:How to get the real and total length of char * (char array)?如何获得char *(char数组)的真实长度和总长度?
【发布时间】:2014-01-28 03:31:32
【问题描述】:

对于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");

【问题讨论】:

  • strlen? (....)
  • @KirilKirov 不会给出数组的长度。嗯,它会,但只在特殊情况下。
  • 这个问题之前已经回答过了,指针不保存它所指向的数据块的大小信息(如果它指向一个数组),只保存它在内存中的起始位置。跨度>
  • 你可以使用std::vector<char>,这样你就有了大小,并且为你完成了内存管理
  • 能否请您确定您要询问的是哪种语言?如果是C,则不能使用new;如果是 C++,那么你应该使用更高级别的抽象,比如 vector 来解决这个问题。

标签: c++ c arrays pointers char


【解决方案1】:

你不能。无论如何,不​​是100%准确。指针没有长度/大小,只有它自己的。它所做的只是指向内存中保存一个字符的特定位置。如果该字符是字符串的一部分,那么您可以使用strlen 来确定当前指向的字符后面的字符,但这并不意味着您的情况中的 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 字符的数量。当然,一旦您将内容分配/重新分配给该数组的键,这将不起作用,并且如果数组外部的内存也恰好包含\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的值可能是你没想到的......

【讨论】:

  • @herohuyongtao:使用指针,你无法获得最大长度。在您的 sn-p new char[10] 中分配 10 个字符,并将指针分配给 a。您可以使用strlen(a) after strcpy,它会返回 5,但不可能获得 10,除非您执行类似 char *a = calloc(10, sizeof *a); 之类的操作,然后再使用 for(i=0;a[i] == '\0';++i);i-1 可以给你分配的内存的总长度,如果分配块旁边的内存也没有意外持有\0,所以这个是危险和坏的。但是您使用的是 C++:使用 std::stringstd::vector
  • 只是出于好奇,不是a[0]==0[a]==*a吗?为什么使用 sizeof (*a) 比使用 sizeof (a[0]) 更好?除非你的意思是它比单独使用 sizeof(a) 更好......
  • @Siidheesh:严格来说a[0] == 0[a] == *(a+0),但使用sizeof *a 的主要原因是在使用除char 以外的其他类型时,或者在自定义分配器中使用指向指针的指针。考虑void my_alloc(void **ptr, size_t size) { (*ptr) = malloc(size*sizeof(*(*ptr)));} 这将在分配结构、整数、字符......任何类型时起作用,而sizeof(type) 需要您知道类型。
  • @APerson:我的意思是说使用sizeof *a 比使用sizeof <type> 更好,并且比sizeof a 好很多(这并不总是你想要的)。就个人而言,我也碰巧更喜欢*a 而不是a[0],仅仅是因为它非常清楚(恕我直言)指针正在被取消引用。在审查代码或寻找段错误的原因时,这些行是我的第一个停靠点。当我看到a[0] 时,我可能会(错误地)假设a 是一个局部数组变量,而不是一个NULL 指针
【解决方案2】:

如果char *是以0结尾的,你可以使用strlen

否则,无法确定该信息

【讨论】:

  • strlen 无法可靠地给出数组的长度。
  • @Olotiar 除非您正在使用特定的编译器并找到有关已分配内存的数据的存储位置(毕竟,为了使内存分配正常工作,分配给该特定位置需要存储在某个位置,这样它就不会被另一个分配重叠,这样free 才能正常工作)。
  • strlen 算上'\0'
  • @JAB 这是一个很好的评论,虽然我不知道任何标准的方式来访问该信息。
  • @JAB 这只有在动态分配的情况下才有效。
【解决方案3】:

只有两种方式:

  • 如果您的char * 指向的内存指针表示一个C 字符串(即,它包含以0 字节标记其结束的字符),您可以使用strlen(a)

  • 否则,您需要将长度存储在某处。实际上,指针只指向 one char。但是我们可以把它当作它指向数组的第一个元素。由于该数组的“长度”未知,因此您需要将该信息存储在某处。

【讨论】:

    【解决方案4】:
    • 在 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);
    }
    

    【讨论】:

      【解决方案5】:

      只考虑指针,你不能。您必须保持传递给new[] 的长度,或者更好的是使用std::vector 来跟踪长度,并在完成后释放内存。

      注意:此答案仅针对 C++,而非 C。

      【讨论】:

      • 假设他只使用 C++。如果代码也需要作为 C 程序运行,那么std::vector 将不会有太大帮助。
      • @JAB:哦,是的,我刚刚注意到问题是同时询问两种语言。我希望人们停止这样做。
      • 如果他使用new,他就不能使用C。
      • @JohnDibling 是的,但该问题同时被标记为 C++C(尽管现在不再存在)。
      【解决方案6】:

      char *a = new char[10];

      我的问题是如何获得 char * 的长度

      很简单。:) 只添加一个语句就足够了

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

      现在你可以得到分配数组的大小

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

      这里很多提到C标准函数std::strlen。但它不返回字符数组的实际大小。它只返回存储的字符串字面量的大小。

      区别如下。如果以你的代码sn-p为例

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

      然后 std::strlen( a ) 将返回 5 而不是您的代码中的 6。

      所以结论很简单:如果您需要动态分配字符数组,请考虑使用类std::string。它具有方法大小及其同义词长度,可以随时获取数组的大小。

      例如

      std::string s( "aaaaa" );
      
      std::cout << s.length() << std::endl;
      

      std::string s;
      s.resize( 10 );
      
      std::cout << s.length() << std::endl;
      

      【讨论】:

        【解决方案7】:

        所以 sizeof 运算符的作用是它返回存储操作数所需的存储量(以字节为单位)。

        存储 char 所需的存储量始终为 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' 有关。这也是一个字符,它在长度上增加了另一个字符。因此,您的长度将是 6 而不是您期望的 5。

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

        您已经提到这里的长度是 4,这是正确的。同样,sizeof 运算符返回操作数的存储量,在您的情况下,它是一个指针 a。一个指针需要 4 个字节的存储空间,因此在这种情况下长度为 4。因为您可能会将其编译为 32 位二进制文​​件。如果您创建了 64 位二进制文​​件,则结果将是 8。

        这个解释可能已经在这里了。只想分享我的两分钱。

        【讨论】:

          【解决方案8】:

          您可以实现自己的newdelete 函数,以及额外的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];
          }
          

          或者,您可以以类似的方式覆盖 newdelete 运算符。

          【讨论】:

          • 创造力+1,虽然我不建议在实际应用程序中这样做。您还必须实现复制和调整大小。使用 C++ 时,有更好的方法来解决问题。
          【解决方案9】:

          这听起来可能是 Evil™,我还没有测试过,但是如何初始化分配给 '\0' 的数组中的所有值,然后使用 strlen() 呢?这将为您提供所谓的真正价值,因为它会在遇到第一个'\0' 时停止计数。

          好吧,现在我想了想,请永远不要这样做。除非,你想落入一堆肮脏的记忆中。

          此外,对于分配的内存或总内存,如果您的环境提供以下功能,您可以使用它们:

          【讨论】:

          • 如果将一个char *数组的所有值都设置为'\0',那么strlen()将返回0。
          • @sharth,这确实是他想要的真正的价值,因为你提到的只是内存还没有被使用的情况。假设他通过将元素均衡为真实值来更改元素,他将不再从调用 strlen() 中得到 0。
          • 当 char 数组包含二进制信息(例如图像指针)时,这可能会导致一些问题:数组可以在数据中包含 '\0' 字节,在这种情况下,数据长度更大比 strlen(data).
          【解决方案10】:

          您可以创建一个回溯字符,例如,您可以将任何特殊字符“%”附加到字符串的末尾,然后检查该字符的出现。
          但这是一种非常危险的方式,因为该字符可以在其他地方也可以在 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* 
          

          否则定义一个自定义结构来跟踪长度和分配内存。

          【讨论】:

          • 呃,太难闻了!改为空终止字符串。使用可能出现在实际文本中的字符是个坏主意(例如,“你欠我 1 美元,因为这个糟糕的想法”;-)
          • @ps06756:仍然崩溃stringVar = new char[10],并且不回答 OP 问题。
          【解决方案11】:

          当 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
          

          我不会推荐这种方案(vector更好),但如果你真的很绝望,你可以找到一个关系,并能够推断出从堆中分配的字节数。

          【讨论】:

            【解决方案12】:

            合法的问题。我个人认为人们将指针与数组混淆是因为字符指针(char*),其用途与字符数组(char __[X])几乎相同。这意味着指针和数组是不一样的,所以指针当然不包含特定的大小,如果我可以这么说的话,只有一个地址。不过你可以试试类似 strlen 的东西。

            int ssize(const char* s)
            {
                for (int i = 0; ; i++)
                    if (s[i] == 0)
                        return i;
            
                return 0;
            }
            

            【讨论】:

              【解决方案13】:

              你可以像这样找到一个 char* 字符串的长度:

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

              sprintf() 将 mystring 打印到自身上,并返回打印的字符数。

              【讨论】:

              • 所以它完全等价于strlen,并且仅在数组中的最后一个字符是NUL并且之前没有NUL时才有效。
              【解决方案14】:

              你可以试试这个:

              int lengthChar(const char* chararray) {
                 int n = 0;
                 while(chararray[n] != '\0')
                   n ++;
                 return n;  
              }
              

              【讨论】:

              • 这只是strlen() 的(一个糟糕的实现),有它的所有限制()。
              【解决方案15】:

              Strlen 命令对我有用。你可以试试下面的代码。

              // 字符 *s

              unsigned int  strLength=strlen(s);
              

              【讨论】:

              • 取决于 s.. 是 C 字符串吗?
              • 只有当你在数组中放入一个终止的字符串时;然后你会得到字符串的长度,而不是数组。
              • 我猜你不想粘贴前三行。
              • 把你的数组像这样,然后试试... char a[]="aaaaa";写字符串 (100, 225, &a);
              • 你为什么坚持将strlen包装在一个名为WriteString的函数中?您的函数不写入字符串,它返回字符串的长度。
              猜你喜欢
              • 2021-09-04
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-06-08
              • 1970-01-01
              • 1970-01-01
              • 2010-10-24
              • 1970-01-01
              相关资源
              最近更新 更多