删除数组的第一个字符

8

我有一个字符串:

str1 = "abcabcabc";

我该如何删除第一个字符?我希望最终的结果是:
str1 = "bcabcabc";

2
如果您展示一下您已经写的内容,并询问如何从卡住的地方继续,那将是很好的。 - vpit3833
1
你遇到了什么问题! - painotpi
1
啊,我明白出了什么问题:修改。stringtypedef char *string;吗?还是你在使用C++?我很确定string不是C语言的一部分。 - Mateen Ulhaq
1
@hkvega 那绝对不必要。 - Mateen Ulhaq
@hkvega 在你知道为什么要写递归函数而不是简单的“for”循环之前,请不要编写递归函数。 - Etienne de Martel
显示剩余2条评论
7个回答

23
如果您有一个指向字符串的字符指针,例如:
char *s = "This is my string";

那么你只需要执行 s++ 就可以了。

如果你有一个字符数组,最好的方法可能就是也拥有该数组的一个指针:

char s[] = "This is my string";
char *ps = s;

那么你可以执行ps++操作,并确保使用ps而不是s

如果你不想有一个单独的数组指针,那么你可以使用memmove函数复制数据:

memmove (s, s+1, strlen (s+1) + 1); // or just strlen (s)

虽然这些方法都不适用于初始为空的字符串,因此您必须首先检查它。另外请注意,尝试以这种方式(或任何方式)修改字符串字面量是不可取的,因为未定义是否允许。

另一种解决方法是编写一个循环:

for (char *ps = s; *ps != '\0'; ps++)
    *ps = *(ps+1);
*ps = '\0';

这将适用于所有字符串,无论是否为空。


1
多亏了你,我的一半神经都被拔掉了。 :) - Mateen Ulhaq
2
只有一个注释。如果字符串的空间是通过malloc分配的,请保留原始指针,否则将无法释放该空间。如果空间是硬编码的,那当然没有问题。对于递归函数的实现给予赞赏。 :-) - Stephen Chung
目前还不确定...但是 memmove (s, s+1, strlen (s+1)); 应该改为 memmove (s, s+1, strlen (s-1)); 吧? - mozzbozz
1
@mozzbozz,不是的,你可能在想 strlen(s) - 1 这个表达式,这是正确的,相当于 strlen(s + 1),但对于 s[0] == 0 的退化情况则不同。而表达式 strlen(s - 1) 不是同一回事。 - paxdiablo
1
@mozzbozz:然而,你的评论让我发现了一个漏洞,我没有复制最后的NUL,所以谢谢你,我已经修复了它。 - paxdiablo
啊,是的。这正是我所读到的:stlen(s) + 1 - 通常情况下,我不在函数名和参数列表之间使用空格 - 这可能会让我感到烦恼。你完全正确 :) C指针等在处理字符串时可能很方便,但也容易出错 :[] - mozzbozz

6

指针技巧(零成本):

char* s = "abcd";
char* substr = s + 1;
// substr == "bcd"

或者:

char s[] = "abcd";
char* substr = s + 1;
// substr == "bcd"

通过使用 memmove 实现原地操作:

char s[] = "abcd";
char* substr = s + 1;
memmove(s, substr, strlen(substr) + 1);
// s == "bcd"

请注意,我们必须使用char[]而不是char*,因为后者会引用只读内存,详见此处的描述。此外,应避免在原地使用strcpy,因为源和目标不能重叠。
通过 strcpy 将其复制到一个新字符串中:
char* src = "abcd";
char* substr = src + 1;
char dest[strlen(substr) + 1];
strcpy(dest, substr);
// dest == "bcd"

通过C++的 std::string::substr 将字符串转换为新字符串:

std::string src = "abcd";
std::string dest = src.substr(1);
// dest == "bcd"

通过 C++ 的 std::copy 将其复制到一个新字符串中:

std::string src = "abcd";
std::string dest;
std::copy(src.begin() + 1, src.end(), std::back_inserter(dest));
// dest == "bcd"

还有其他几十种方法(特别是包括C++),但我就到这里了。:)


这个有三个缺陷(每个部分一个)。 (1) 你不能增加字符数组,只能增加指针。 (2) 这个会创建一个新的字符串,但是通过之后将其复制回原始字符串可能可以解决。 (3) 你的 C++ 解决方案可能是正确的(我无法评论),但对于 C 问题来说几乎没有用处 :-) - paxdiablo
(1) 我修好了,然后又取消了修复,因为我忘记了为什么要修复它。 :) (2) 现在看起来还好吗? (3) 是的,我喜欢 C++。 - Mateen Ulhaq
整个三行的 if 代码块可以被替换为 strcpy(str2, &(str1[1]));,不需要 pstr1,而且 if 中的 sizeof 应该改为 strlen - paxdiablo
我非常喜欢strcpy方法,有没有办法使用它不仅删除第一个字母,还要删除最后一个字母? - Cheetaiean
1
@Cheetaiean 你可以使用 strncpy - Mateen Ulhaq

1

如果你真的是想要说

char str1 [] = "abcabcabc";

那么最简单的事情就是

str1 = &str1[1];

如果您想修改实际数据,则只需将所有内容上移一个位置。您可以使用循环或memmove()。递归函数过于复杂。

如果您真的是指C++并且正在使用字符串对象,那么您可以使用

str1 = str1.substr(1);

问题不在于衰变(确实会发生),而在于试图分配给非指针。 - paxdiablo
如果 str1 声明为 char* str1,它就能正常工作。你是对的,如果你声明 char str1[],那么它就不能工作。但不清楚 OP 正在使用什么。 - Adam
@Adam 如果将&str1传递给期望char **的函数并在那里对其进行增量操作,会发生什么?当str1超出范围时,是否会导致崩溃? - dashesy
@dashesy 如果 str1 声明为 char* str1,那么你无论如何都无法将其传递给接受 char** 的函数。当你访问已被释放的内存时会导致崩溃(希望如此)。这与上述内容无关。如果 str1 超出作用域,那么涉及到 str1 的任何语句都是编译器错误。 - Adam
@Adam- 我的意思是在 char str1[]="abcabcabc"; 时传递 &str1。 - dashesy
@dashesy 这是一个指向指针的指针,所以如果你增加它,你将会增加 sizeof(char*)(即4或8),而不是 sizeof(char)(即1)。换句话说,你在向编译器暗示你有一个指向字符串的指针数组,而不是一个字符数组。请注意,&str1 不同于 str1,因此你无论如何都会得到不同的结果。但是,如果 arg 是那个 char** 参数,你仍然可以执行 (*arg)++ - Adam

1
#include <stdio.h>
#include <conio.h>
main(){
   char a[10];
   int i;
   gets(a);

   for (i = 0; a[i] != '\0'; i++) {
      a[i] = a[i + 1];
   }

   printf("\n");
   puts(a);
   getch();
}

1

以下是一种实现方式:

int index = 0; //index to cull
memmove( &word[ index ] , &word[ index +1], strlen( word ) - index) ;

1
据我所知,如果你担心内存分配问题,你需要将(str1+1)复制到一个新的字符串中,然后为其分配内存,最后释放第一个指针。 真正简单的方法是只需使用 str1++ 就可以了。这会使它指向比以前多一个字符的位置,并且只需要一行代码就可以得到所需的结果。

0
#include <stdio.h>
int main() {
  char a[15] = "!Hello world!";
  sprintf(a,"%s",a+1);
  printf("%s",a);
}

这段代码表现出未定义的行为。来自cppreferenceC标准和POSIX规定,当参数与目标缓冲区重叠时,sprintf及其变体的行为是未定义的。 - Adrian Mole

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接