我正在努力学习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”?
提前致谢!
答案 0 :(得分:4)
一旦你将char指针传递给你正在编写的函数,你将会忘记分配给s的内存量。您需要将此大小作为参数传递给函数。
答案 1 :(得分: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 );
答案 2 :(得分:2)
所以这一切总结为以下问题:如何找到编译器为我的目的地保留多少内存"字符串" ???
没有可移植的方法来查明分配了多少内存。你必须自己跟踪它。
实现必须跟踪指针的malloc
内存量,并且可能会让您找到可用的内容。例如,glibc的malloc.h
暴露
size_t malloc_usable_size (void *__ptr)
可让您大致访问该信息,但是,它并不能告诉您您的请求数量,但可用的数量。当然,这仅适用于您从malloc
(和朋友)获得的指针。对于数组,只能使用数组本身在范围内的sizeof
。
答案 3 :(得分:1)
char s[80] = "";
int i = someFunction(s); // should return 80
在表达式s
中是指向数组s
的第一个元素的指针。您不能使用指向其第一个元素的指针值的唯一信息来推断数组对象的大小。您可以做的唯一事情是在声明数组(此处为sizeof s
)之后存储数组大小的信息,然后将此信息传递给需要它的函数。
答案 4 :(得分:1)
没有便携式方式。但是,实现当然需要在内部了解这些信息。基于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);
答案 5 :(得分:0)
标记malloc返回的成员的方法是始终malloc一个额外的sizeof(size_t)字节。将其添加到malloc返回的地址,并且您有一个存储空间用于存储实际长度。存储malloced大小 - sizeof(size_t),你有新的功能集。
当你将这两种指针传递给你的新特殊strcpy时,你可以从指针中减去sizeof(size_t),并直接访问这些尺寸。这可以让您决定是否可以安全地复制内存。
如果你正在做strcat,那么两个大小,以及计算strlens意味着你可以做同样的检查,看看strcat的结果是否会溢出内存。
这是可行的。 这可能比它的价值更麻烦。
考虑如果传入未被mallocated的字符指针会发生什么。 假设大小在指针之前。这种假设是错误的。 在这种情况下尝试访问大小是未定义的行为。如果幸运的话,您可能会收到信号。
这种实现的另一个含义是,当你去释放内存时,你必须传入完全指向那个malloc返回的指针。如果你没有做到这一点,可能会发生堆损坏。
长话短说...... 不要这样做。
答案 6 :(得分:0)
对于在程序中使用字符缓冲区的情况,可以执行一些冒烟和镜像以获得所需的效果。这样的事情。
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不是为了保护程序员不正确地做事而设计的。 有点像你试图划船上游:) 这是一个很好的练习。
答案 7 :(得分:0)
尽管数组和指针看起来可以互换,但它们在一个重要方面有所不同;数组有大小。但是,因为传递给函数的数组“降级”为指针,所以大小信息会丢失。
关键是,在某些时候你知道对象的大小 - 因为你已经分配它或者声明它是一定的大小。 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 ) ;
}
因为与数组不同的结构可以通过这种方式传递。您也可以通过副本同样返回。然而,它可能有些低效。然而,有时方便性和安全性超过效率低下。