C - 为什么这个表达式* q = *(q + 1)在处理字符串时产生错误?

时间:2016-04-25 09:48:45

标签: c

void delchar(char *s, char c){
  char *p, *q = NULL;
  for(p = s;*p != '\0'; p++){
    if(*p == c){
        q = p;
        do{
            *q = *(q + 1);
            q++;
        }while(*q != '\0');
    }
 }
}

我想使用此代码删除字符串中的特定字母,因此我创建了一个指针p来扫描字符串,创建另一个指针q以移动该特定字母后面的元素并覆盖它。但事实证明表达式* q = *(q + 1)用于移动和覆盖错误“程序收到信号SIGSEGV,分段错误”。我只是不知道它的原因。

3 个答案:

答案 0 :(得分:2)

即使您不会因此而出现分段错误,您的算法也存在缺陷。即使删除了字符,也可以提前p光标。

使用while代替for

#include <stdio.h>
#include <string.h>

void delchar(char *s, char c)
{
    char *p, *q = NULL;

    p = s;
    while (*p != '\0')
    {
        if (*p == c)
        {
            q = p;
            do {
                *q = *(q + 1);
                q++;
            } while(*q != '\0');
        }
        else
            p++; // Advance cursor only if no character was deleted
    }
}

int main()
{
    char str[1024];
    strcpy(str, "Yes or  no?");
    delchar(str, ' ');
    printf("%s\n", str);

    return 0;
}

输出:

  

Yesorno?

您可能会遇到分段错误的原因是尝试更新字符串文字。字符串文字不会被修改。它们与char数组完全不同。主要区别在于您无法覆盖其内容。

int main()
{
    char *str = "to to"; // string literal
    delchar(str, ' '); // segmentation fault

    char array[] = "to to"; // array of char
    delchar(array, ' '); // no segmentation fault
    printf("%s\n", str);

    return 0;
}

您可以使用strcpy()复制字符串,就像我上面一样,或者您可以使用字符数组而不是字符串文字。

答案 1 :(得分:0)

有一个原因可能发生这种情况:当你尝试写入程序代码部分中的字符串(而不是堆栈或堆)时。

调用这样的代码(数据在(只读)代码部分):

char *s = "1234567890";
delchar(s, '5');

将是segfault,但是这(数据在堆上):

char *s;
s = malloc(1024);
strcpy(s, "1234567890");
delchar(s, '5');

不会。

要修复,我不会触及现有的char *,而是分配一个新的字符,复制除了要从中删除的字符之外的每个字符(注意:代码未经过测试,但是你得到这个想法。

char *delchar(const char *s, char c) {
    char *n, *p = calloc(strlen(s)+1, 1);
    n = p;
    while (*s != '\0') {
       if (*s != c) {
          *n = *s;
          n++;
       }
       s++;
    }
    return p;
}

代码更清晰,更易于阅读,而且您无法触及现有字符串。别忘了free()当然的结果。

答案 2 :(得分:-1)

从字符串中删除所有字符的典型算法是:

void remove_ch (char* restrict dest, const char* restrict src, char ch)
{
  while(*src != '\0')
  {
    if(*src != ch)
    {
      *dest = *src;
      src++;
    }
    dest++;
  }

  *dest = '\0';
}

要进行就地删除,您必须重写此功能。删除restrict并考虑使用临时char内存位置,因为重叠内存区域之间的复制可能会调用未定义的行为。

完成修改后,您应该能够传递与destsrc相同的参数。