【问题标题】:Find the size of reserved memory for a character array in C在 C 中查找字符数组的保留内存大小
【发布时间】:2013-01-27 14:05:42
【问题描述】:

我正在尝试学习 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”?

提前致谢!

【问题讨论】:

  • 一点建议:如果您想要这种类型的安全性,请使用 C++。
  • 此外,标准做法(标准库中的标准)是char *strncpy( char *dest, const char *src, std::size_t count );,即它将目标的大小作为参数传递。
  • sizeof 在这种情况下工作得非常好,给出 80。

标签: c string pointers


【解决方案1】:

一旦你将一个 char 指针传递给你正在编写的函数,你就会失去关于分配给 s 多少内存的知识。您需要将此大小作为参数传递给函数。

【讨论】:

    【解决方案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 );
    

    【讨论】:

      【解决方案3】:

      所以这一切都归结为以下问题:如何找到编译器为我的目标“字符串”保留了多少内存???

      没有可移植的方法来确定分配了多少内存。您必须自己跟踪它。

      实现必须跟踪malloced 到指针的内存量,并且它可能会为您提供一些可用的信息。比如glibc的malloc.h暴露

      size_t malloc_usable_size (void *__ptr)
      

      这使您可以大致访问该信息,但是,它不会告诉您您请求了多少,而是告诉您有多少是可用。当然,这只适用于您从malloc(和朋友)获得的指针。对于数组,您只能使用sizeof,其中数组本身在范围内。

      【讨论】:

        【解决方案4】:
        char s[80] = "";
        int i = someFunction(s); // should return 80
        

        在表达式中s 是指向数组s 的第一个元素的指针。您不能仅使用指向其第一个元素的指针值的信息来推断数组对象的大小。你唯一能做的就是在声明数组后存储数组大小的信息(这里sizeof s),然后将此信息传递给需要它的函数。

        【讨论】:

          【解决方案5】:

          没有便携方法可以做到这一点。但是,实现肯定需要在内部知道这些信息。基于 Unix 的操作系统,如 Linux 和 OS X,为这项任务提供了功能:

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

          【讨论】:

          • 这对他正在做的事情没有帮助。一个“安全的”strcpy 在没有由 malloc 分配的指针上崩溃根本不安全。
          • @Pubby 这些函数可能不应该在生产代码中使用。依赖这样的东西不是很好的做法,我只是说确实可以。
          【解决方案6】:

          标记 malloc 返回的成员的一种方法是始终 malloc 一个额外的 sizeof(size_t) 字节。将其添加到 malloc 返回的地址中,您就有了存储实际长度的存储空间。将 malloced 大小 - sizeof (size_t) 存储在那里,您就有了新函数集的基础。

          当您将其中两种类型的指针传递给您的新特殊 strcpy 时,您可以从指针中减去 sizeof(size_t),并直接访问大小。这让您决定是否可以安全地复制内存。

          如果你在做strcat,那么这两个尺寸,连同计算strlens意味着你可以做同样的检查,看看strcat的结果是否会溢出内存。

          这是可行的。 这可能比它的价值更麻烦。

          考虑一下如果你传入一个未分配的字符指针会发生什么。 假设大小在指针之前。这个假设是错误的。 在这种情况下尝试访问大小是未定义的行为。运气好的话,可能会收到信号。

          这种实现的另一个含义是,当您释放内存时,您必须传入完全返回的 malloc 指针。如果你没有得到正确的,堆损坏是可能的。

          长话短说... 不要那样做。

          【讨论】:

            【解决方案7】:

            对于您在程序中使用字符缓冲区的情况,您可以做一些烟雾和镜子来获得您想要的效果。像这样。

            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 并非旨在保护程序员不做错事。 这有点像你试图在上游划桨:) 这是一个很好的练习。

            【讨论】:

              【解决方案8】:

              虽然数组和指针看起来可以互换,但它们在一个重要方面有所不同;数组有大小。但是,由于数组在传递给函数时会“降级”为指针,因此大小信息会丢失。

              关键是在某个时候知道对象的大小 - 因为你分配它或声明它是一个特定的大小。 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 在这方面的水平相对较低,并且不添加代码或不明确的数据);如果需要该信息,则必须明确传递它。

              您可以传递字符串并保留大小信息,甚至通过复制而不是通过引用传递字符串的一种方法是将字符串包装在结构中:

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

              因为与数组不同的结构可以通过这种方式传递。您同样可以通过副本返回。然而,它可能有些低效。然而,有时便利性和安全性胜过低效。

              【讨论】:

                猜你喜欢
                • 2012-12-27
                • 2014-03-05
                • 2021-12-13
                • 2021-08-28
                • 1970-01-01
                • 2012-01-15
                • 2010-09-26
                • 2011-05-03
                • 1970-01-01
                相关资源
                最近更新 更多