例如,我有一个字符串。而且我只想更改字符串的开头几个字符,其余部分保持原样。用C做到这一点的最佳方法是什么?
#include <stdio.h>
#include <string.h>
int main() {
char src[40];
char src2[40];
char dest[12];
memset(dest, '\0', sizeof(dest));
strcpy(src, "This is a string");
strcpy(src2, "That");
strncpy(dest, src, sizeof(src));
strncpy(dest, src2, sizeof(src2));
printf("Final copied string : %s\n", dest);
}
我希望将字符串从"This is a string"
更改为"That is a string"
。
有没有简单的方法可以实现我所缺少的功能?
答案 0 :(得分:8)
这里有几个问题。
首先,dest
只有12个字节长,太短而无法容纳“这是一个字符串”。尝试将该字符串复制到dest
中将超出缓冲区。这将调用undefined behavior。至少保留20个字节。
第二sizeof(src)
给出整个数组的大小,即40,而不是字符串的长度。如果目标缓冲区不够大,这也会产生不确定的行为。请改用strlen
。 sizeof(src2)
也是如此。
进行这些更改后,您应该具有以下内容:
#include <stdio.h>
#include <string.h>
int main() {
char src[40];
char src2[40];
char dest[20];
memset(dest, '\0', sizeof(dest));
strcpy(src, "This is a string");
strcpy(src2, "That");
strncpy(dest, src, strlen(src));
strncpy(dest, src2, strlen(src2));
printf("Final copied string : %s\n", dest);
}
答案 1 :(得分:2)
<audio id="sound" src="sound.mp3" autoplay="0" autostart="0"></audio>
<button onclick="playAud()" type="button">Play</button>
<button onclick="pauseAud()" type="button">Pause</button>
<script>
let aud = document.getElementById("sound");
function playAud() {
aud.play();
}
function pauseAud() {
aud.pause();
}
是sizeof(src2)
(它是整个数组的大小)-您可能是说40
(这只是用于字符串的字符数):
strlen(src2)
请注意,您的代码存在缓冲区溢出问题:strncpy(dest, src2, strlen(src2));
数组不足以容纳结果字符串。它必须至少包含17个字符,才能容纳dest
字符串。您还需要使用:
"This is a string"
答案 2 :(得分:1)
我很困惑,对memset()
的电话有点担心。 memset
调用允许使用strncpy()而不用担心终止'\ 0'。这样的调用假定此逻辑将在通用函数中使用,该通用函数将从更高级别的函数中调用。
如果不是这种情况,则应将第一个strncpy()替换为strcpy,并丢弃内存集:
#include <stdio.h>
#include <string.h>
int main() {
char src[40];
char src2[40];
char dest[20];
/* memset(dest, '\0', sizeof(dest)); -- No need for this */
strcpy(src, "This is a string");
strcpy(src2, "That");
strcpy(dest, src, strlen(src)); /* strcpy will put a '\0' at the end */
strncpy(dest, src2, strlen(src2));
printf("Final copied string : %s\n", dest);
}
但是,如果将从更高级别的函数中调用它,那么我们确实需要检查传入字符串的长度和/或malloc目标缓冲区并释放它。 memset()是房间里的大象,它暗示了应该考虑的更多逻辑。否则,只需将第一个strncpy()替换为strcpy()。
答案 3 :(得分:1)
通过@dbush扩展答案:
我认为代码仍然很脆弱。
#include <stdio.h>
#include <string.h>
int main() {
char src[40];
char src2[40];
char dest[20];
在这里创建了第一个陷阱:dest小于两个来源。虽然可以解决,但在后续代码中需要更多的努力。最好使目的地始终足够大以容纳来源。
memset(dest, '\0', sizeof(dest)); //Good
但是用0初始化其他数组也是一个好主意。
memset(src, '\0', sizeof(src));
memset(src2, '\0', sizeof(src2));
旁注:当然,您可以通过使用数组初始化语法来节省memset的麻烦:
char src[40] = {0};
char src2[40] = {0};
char dest[20] = {0};
如果IRL这两个字符串可能不是字符串常量,那么接下来的两行可能会很危险。 (即使这样!)没有提供长度检查...更好:
// strcpy(src, "This is a string");
// strcpy(src2, "That");
strncpy(src, "This is a string", sizeof(src)- 1);
strncpy(src2, "That", sizeof(src2) -1);
这样,我们确保不会引起任何溢出。 我们还要确保src和src2中的字符串正确以空值结尾。
现在,将src / src2复制到dest也很危险。我们必须确保不会溢出目标。
//strncpy(dest, src, strlen(src));
//strncpy(dest, src2, strlen(src2));
更好:
strncpy(dest, src, sizeof(dest) - 1);
strncpy(dest, "That is a long rubbish string that easily could overflow dest", sizeof(dest) -1);
我们仅复制dest可以容纳并保留的空终止符。
现在要替换前X个字符。同样,我们必须确保不会发生溢出。我们使用strlen来确定src2中以空值结尾的字符串的实际大小,但是需要使用/评估dest的最大大小。因此混合了strlen和sizeof。
memcpy只是为了好玩。您也可以使用strncpy。
memcpy(dest, src2, strlen(src2) < sizeof(dest) ? strlen(src2) : sizeof(dest));
printf("Final copied string : %s\n", dest);
}
因此,整个安全的实现如下所示:
#include <stdio.h>
#include <string.h>
int main() {
char src[40] = {0};
char src2[40] = {0};
char dest[20] = {0};
strncpy(src, "This is a string", sizeof(src)- 1);
strncpy(src2, "That is a long rubbish string and sooooooooooooooooooooooooo much more", sizeof(src2) -1);
strncpy(dest, src, sizeof(dest) - 1);
memcpy(dest, src2, strlen(src2) < sizeof(dest) ? strlen(src2) : sizeof(dest));
printf("Final copied string : %s\n", dest);
}
请注意,sizeof的使用似乎仅等同于strlen;在有效的字符类型上,在其他类型上,您需要执行更多操作。