在C中通过引用传递-缺点?
大多数高级语言(Python、Ruby甚至Java)都使用按引用传递。显然,我们在C中没有引用,但是我们可以使用指针来模拟它们。这样做有一些好处。例如:在C中通过引用传递-缺点?,c,C,大多数高级语言(Python、Ruby甚至Java)都使用按引用传递。显然,我们在C中没有引用,但是我们可以使用指针来模拟它们。这样做有一些好处。例如: int findChar(char ch, char* in) { int i = 0; for(i = 0; in[i] != '\0'; i++) if(in[i] == ch) return i; return -1; } 这是一种常见的C范例:通过返回一些错误值(在本例中
int findChar(char ch, char* in)
{
int i = 0;
for(i = 0; in[i] != '\0'; i++)
if(in[i] == ch)
return i;
return -1;
}
这是一种常见的C范例:通过返回一些错误值(在本例中,如果字符不在字符串中,则返回-1
)来捕获异常或错误情况
问题是:如果要支持长度超过2^31-1
个字符的字符串,该怎么办?显而易见的解决方案是返回一个无符号int
,但这对这个错误值不起作用
解决方案如下:
unsigned int* findChar(char ch, char* in)
{
unsigned int i = 0;
for(i = 0; in[i] != '\0'; i++)
if(in[i] == ch)
{
unsigned int index = (unsigned int*) malloc(sizeof(unsigned int));
*index = i;
return index;
}
return NULL;
}
有一些明显的优化,我不是为了简单而做的,但是你明白了;返回NULL作为错误值
如果对所有函数都这样做,还应将参数作为指针传入,以便将一个函数的结果传递给另一个函数的参数
这种方法(除了内存使用之外)是否有我没有的缺点
<>编辑:我想补充(如果我的问题不完全明显)我有C++的一些经验,但是我在C.</P> < P>这是一个完全的初学者。这是个坏主意,因为调用方负责<代码>免费< /COD>索引,否则你正在泄露内存。或者,您可以使用
static int
并每次返回它的地址-不会有泄漏,但函数会变得不可重入,这是有风险的(但如果您记住这一点,则可以接受)
更好的方法是返回指向char函数finds的指针,如果不存在则返回NULL。顺便说一句,strchr()
就是这样工作的
编辑以反映原始帖子中的更改
在特定示例中,您应该使用
size\t
作为返回类型:这是一种数据类型,它充分表示任何系统上的字符串大小。也就是说,您不可能拥有一个长度超过其所能代表的大小的字符串。然后,您可以相当安全地使用(size_t)-1
作为错误指示器:实际上,您也不能将具有该大小的字符串放入内存,因为您还需要一些地址空间来执行代码;API的一个限制是,如果存在这样长的字符串,就不支持它们
您的方法不仅有使用更多内存的缺点,而且还有速度较慢的缺点:被调用方需要malloc,调用方需要释放。这些都是相当昂贵的操作
这里还有另一个相关的标准方法:errno。如果出现错误指示器,您不知道错误是什么。因此,在C中,我们通常不使用out参数,而是将错误详细信息放入全局或线程局部变量。最大的缺点是它需要findChar()的调用者释放()返回的内存,或创建内存泄漏。你对strchr()轮子的改造很糟糕 我也不明白为什么你会认为返回一个指向unsigned int的指针是向前迈出的一大步。首先,您可以只返回一个无符号int,如果您所追求的是在32位机器上返回最多2^32的值,而不是2^31-1。其次,您声明的目标是避免大字符串的问题。如果你在64位机器上,int和unsigned int仍然是32位呢?这里真正需要的是一个很长的过程,但是返回指针实际上在这里没有帮助
省略虚假批评如果没有malloc,位置仍然可以是堆栈变量,您可以在if语句中使用它:
int findChar(char ch, char* in, int* pos)
{
int i = 0;
for(i = 0; in[i] != '\0'; i++)
{
if(in[i] == ch)
{
*pos = i;
return 1;
}
}
return 0;
}
我不是专家,但我认为大量的小型
malloc
s可能会导致问题。首先,您必须注意在使用值之后释放内存。然后,您还必须处理空闲内存的碎片。作为指针传递更适合于复杂的结构。我想说,代码最严重的缺点是使用一个返回值来表示一般故障和成功后的结果
虽然这是一种常见的做法,但当需求发生变化时,它可能会导致wierd场景,就像您描述的那样。另一种做法是将返回值分开,例如
int findChar(char ch, char const * const in, unsigned int * const index)
{
if ( in != NULL && index != NULL)
{
unsigned int i;
for(i = 0; in[i]; i++)
{
if(in[i] == ch)
{
*index = i;
return EXIT_SUCCESS;
}
}
}
return EXIT_FAILURE;
}
…其中函数返回值告诉您函数是否成功,与“index”的值分开
然后,正如fortran所指出的,无法强制执行指针是输入值还是输出值,或者两者都是(即在函数内部修改) Java不使用按引用传递。它使用传递值,但对于引用类型,传递的值是引用。这和“真实的”通过引用传递之间有很大的区别。Python和Ruby可能是一样的——我不确定。在我看来,这里的术语非常精确是值得的。@Jon Skeet:是的,Python和Ruby中的值与Java中的引用的工作方式相同,但术语在这些方面有所不同communities@Jon双向飞碟:这是一种思维方式;我倾向于认为Java是通过引用传递的,除了原语,原语用不同的词表示相同的东西。各自为政您应该使用size\u t而不是int或unsigned int,使用0xFFFFFF作为sentinel错误值是完全可以接受的。说真的,如果字符串的大小是4G(甚至2G),那么如何表示它们的长度是最简单的问题。@Imagist:如果Java是按引用传递的,那么下面的代码将更改本地字符串的值