我开始通过阅读K&R并完成一些练习来学习C语言.经过一番挣扎,我终于可以用以下代码完成练习1-19:
/* reverse: reverse the character string s */ void reverse(char s[], int slen) { char tmp[slen]; int i, j; i = 0; j = slen - 2; /* skip '\0' and \n */ tmp[i] = s[j]; while (i <= slen) { ++i; --j; tmp[i] = s[j]; } /* code from copy function p 29 */ i = 0; while ((s[i] = tmp[i]) != '\0') ++i; }
我的问题是关于tmp
char数组被复制到的最后一段代码s
.为什么不是简单的s = tmp;
工作呢?为什么必须按索引遍历数组复制索引?
也许我只是老了,脾气暴躁,但我见过的其他答案似乎完全忽略了这一点.
C不执行数组赋值,句点.您不能通过简单的赋值将一个数组分配给另一个数组,这与其他语言不同(例如PL/1; Pascal及其后代的许多--Ada,Modula,Oberon等).C也没有真正的字符串类型.它只有字符数组,你不能复制字符数组(除了你可以复制任何其他类型的数组)而不使用循环或函数调用.[字符串文字实际上不算作字符串类型.]
复制数组的唯一时间是将数组嵌入到结构中并执行结构分配.
在我的K&R第2版副本中,练习1-19要求提供功能reverse(s)
; 在我的K&R第1版副本中,它是练习1-17而不是1-19,但同样的问题被问到了.
由于在此阶段尚未涵盖指针,因此解决方案应使用索引而不是指针.我相信这会导致:
#includevoid reverse(char s[]) { int i = 0; int j = strlen(s) - 1; while (i < j) { char c = s[i]; s[i++] = s[j]; s[j--] = c; } } #ifdef TEST #include int main(void) { char buffer[256]; while (fgets(buffer, sizeof(buffer), stdin) != 0) { int len = strlen(buffer); if (len == 0) break; buffer[len-1] = '\0'; /* Zap newline */ printf("In: <<%s>>\n", buffer); reverse(buffer); printf("Out: <<%s>>\n", buffer); } return(0); } #endif /* TEST */
使用-DTEST编译它以包含测试程序,而不必仅reverse()
定义函数.
使用问题中给出的函数签名,您可以避免strlen()
每行输入调用两次.注意使用fgets()
- 即使在测试程序中,使用它也是一个坏主意gets()
.与之fgets()
相比的缺点gets()
是fgets()
不会删除尾随的换行符gets()
.好处fgets()
是,你没有得到数组溢出,你可以判断程序是否找到换行符或者在遇到换行符之前是否用尽了空格(或数据).
您的tmp
数组是在堆栈上声明的,因此当您的方法完成时,用于保存值的内存将因为作用域而被释放.
s = tmp
意味着s
应该指向相同的内存位置tmp
.这意味着当tmp
释放时,s
仍将指向现在可能无效的,释放的内存位置.
这种类型的错误称为悬空指针.
编辑:这不是这个答案的评论中指出的悬空修饰符.问题在于s = tmp
只说改变参数指向的内容,而不是实际传递的数组.
此外,您可以通过单次传递执行反向操作,而无需在内存中分配整个数组,只需将值逐个交换:
void reverse(char s[], int slen) { int i = 0; // First char int j = slen - 2; // Last char minus \n\0 char tmp = 0; // Temp for the value being swapped // Iterate over the array from the start until the two indexes collide. while(i < j) { tmp = s[i]; // Save the eariler char s[i] = s[j]; // Replace it with the later char s[j] = tmp; // Place the earlier char in the later char's spot i++; // Move forwards with the early char j--; // Move backwards with the later char } }