我正在阅读K& C编程语言书籍,遇到了一个问题。在此代码中,我试图分别用C对应的'n'
和't'
替换字符串中的换行符和制表符。我的代码正确地执行了这个函数,但也以某种方式从输入字符串数组中删除了第一个字符,即使字符串仅用于获取字符值,并且从未赋值给。
谁能告诉我我做错了什么?下面是我的代码:
#include <stdio.h>
void escape(char s[], char t[]);
int main(void){
char t[] = "This is a string"; // tab in between "a" and "string"
char s[] = "";
escape(s, t);
printf("%sn", s); // prints "This is atstring" as expected
printf("%sn", t); // prints "his is atstring" with first character removed. Why??
}
/**
* Copy from t to s while replacing actual tabs and newlines with "n" and "t"
* char s[] - the array that is being copied to
* char t[] - the array that is being copied from
*/
void escape(char s[], char t[]){
int i = 0, j = 0;
while (t[i] != ' '){
switch (t[i]){
case 'n':
s[j++] = '\';
s[j++] = 'n';
break;
case 't':
s[j++] = '\';
s[j++] = 't';
break;
default:
s[j++] = t[i];
break;
}
i++;
}
}
输出:
This is atstring
his is atstring
一些小改动:
- 由于我们不打算写入
t
,所以将其声明为const char[]
(将其写为const char*
更习惯,因此也要更改)。更常规的名称,如src
和dest
,将有助于理解。 s
没有足够的输出空间。将其调整为足以容纳扩展字符串的大小。作为上界,使其长度为输入字符串的两倍;我们知道输出不能超过这个长度。
事实证明,这些中的第二个也解决了您的问题-似乎t
和s
在内存中彼此存储得很近,因此注销s
的末尾已经损坏了t
的值。
你实际上遇到了一个严重的问题,它影响了许多用C编写的函数:调用者需要知道为要写入的函数提供多少空间。当它出错时,你会得到未定义的行为,这是出了名的难以诊断的(在最坏的情况下,你得到了你想要和期望的行为,直到你将代码部署到生产系统……)
固定代码:
#include <stdio.h>
void escape(char *dest, const char *src);
int main(void)
{
char t[] = "This is atstring"; // tab in between "a" and "string"
char s[sizeof t * 2] = "";
escape(s, t);
printf("%sn", s); // prints "This is atstring" as expected
printf("%sn", t); // prints "his is atstring" with first character removed. Why??
}
/**
* Copy from src to dest while replacing actual tabs and newlines with "n" and "t"
* char dest[] - the array that is being copied to
* char src[] - the array that is being copied from
*/
void escape(char *dest, const char *src) {
int i = 0, j = 0;
while (src[i] != ' ') {
switch (src[i]) {
case 'n':
dest[j++] = '\';
dest[j++] = 'n';
break;
case 't':
dest[j++] = '\';
dest[j++] = 't';
break;
default:
dest[j++] = src[i];
break;
}
i++;
}
}
我注意到另一个问题-我们不复制终止null字符。我们暂时不处理这个问题,因为我们的s
充满了null,所以在这个简单的测试中并不明显。为了修复它,我们需要将测试移到循环的末尾,在我们从源代码复制之后。do
-while
是理想的。
下面是用指针算术代替索引重写的函数,并修复了终止错误:
void escape(char *dest, const char *src) {
do {
switch (*src) {
case 'n':
*dest++ = '\';
*dest++ = 'n';
break;
case 't':
*dest++ = '\';
*dest++ = 't';
break;
default:
*dest++ = *src;
break;
}
} while (*src++);
}