如何纠正 char * 到 int 转换的分段错误

How to correct segmentation fault with char * to int conversion

为了将以整数序列作为地址的指针转换为整数,我编写了以下程序:

#include <math.h>
#include <stdio.h>

char *recepcao() { char *receber;
    scanf("%s", receber);
    return receber;
}
int conversao(char *string) { int i, j;
    for(i=0; string[i]!='[=10=]'; ++i) {
        continue;
    }
    int *var;
    int contagem=0;
    for(j=0; j<i-1; ++j) {
        var[j]=(string[j]-'0');
        contagem+=var[j]*pow(10, (i-j-1));
    }

    return contagem;
}
int main() {
    printf("%i", conversao(recepcao())); return 0;
}

我已经尝试了大约一千次来纠正所有循环,但在收到 scanf 值后程序仍然会崩溃,如某些 IDE 所述,并会在其他 IDE 上显示 "Segmentation Fault: 11" 消息。我怎样才能解决这个问题?该消息的正确定义是什么?

您将指针与数组混淆了。指针只是指向;声明一个指针不保留内存,它只是创建一个指针变量指向一些未定义的地方。

更正的程序:

char *recepcao(char *receber) {
    scanf("%s", receber);
    return receber;
}
int conversao(char *string) { int i, j;
    for(i=0; string[i]!='[=10=]'; ++i) {
        continue;
    }
    int var[100];
    int contagem=0;
    for(j=0; j<i-1; ++j) {
        var[j]=(string[j]-'0');
        contagem+=var[j]*pow(10, (i-j-1));
    }

    return contagem;
}
int main() {
    char receber[100];
    printf("%i", conversao(recepcao(receber))); return 0;
}

更新:

数组和指针在 C 中的使用方式非常相似,但它们并不相同。声明数组时,编译器会保留所需的内存:

int a[10], b[10];
a[1] = 1;  // OK
a = b;     // Error!

数组元素是可变的,但数组名称是不是。数组名是一种标签,您不能更改它的值使其指向另一个数组。

指针变量。当您声明一个指针时,编译器会使用未定义的内容创建它(就像任何其他变量一样)。

int *p, *q;
p = q;      // OK but useless because both the pointers contain junk addresses
a[0] = *p;  // Depending on where p points you will get either junk in a[0] or memory access violation
. . . 
p = a;                 // Now you can use p as a synonym for array a declared above
if (*(p + 1) == a[1])  // Always true
if (p[2] == *(a + 2))  // Always true
. . .
b = p;   // This won't work; can't assign a new value to array name b
. . .
q = (int*)malloc(sizeof(int) * 10); // Another way of creating int array of 10 elements
q = p;  // Array created with malloc is lost because nothing points to it now

基本上,指针和数组之间的唯一区别是指针是变量而数组名不是。你不能写 "a = p" 就像你不能写 "2 = i".

数组作为正式参数还有一个相当令人困惑的怪癖:

   void xyz(int a[10]) {
       . . .
   }

   void main() {
       int b[20];
       . . .
       xyz(b);
   }

看起来我们将 b 分配给 a,并更改数组大小,对吧?不,"int a[10]" 被视为 "int *a",sizeof(a) 将 return 指针的大小,而不是数组的大小。

receber 是未初始化的指针。 scanf 期望 char* 读取的字符将被写下来。但在你的情况下,它指向一些你不应该访问的内存。试图访问它是未定义的行为。在您的情况下,它会导致分段错误。

在许多解决方案中,可以使用 char receber[MAXLEN+1]char * receber = malloc(MAXLEN+1)。现在这里有一个案例。第一个解决方案将产生一个具有自动存储持续时间的数组 - 长话短说,当函数结束时它将指向一些无效的内存 - 所以你不能 return 它(如果您使用第一个解决方案)。

第二种解决方案将动态分配内存,其存储持续时间将超出此函数的范围。 在这种情况下,这将是正确的使用方法。

对于 var 在其他函数中,您可以使用动态内存分配或 VLA 支持(如果有的话)。您应该分配大小等于字符串长度的内存。在这里它并没有停留在分配上——你需要用值初始化它,这样你就可以像以前一样在算术运算中使用它。 (再次使用未初始化是未定义的行为)

为了您的信息,有一个名为 strlen 的函数,它为您提供字符串的长度(以 nul 结尾的字符数组)- 在这里使用它。

此外,如果您查看参考手册或手册页,您会看到 pow returns double - 所以在这里您会遇到一些精度问题。要计算积分功率,请使用自定义函数 - 在精度和避免严重精度错误的情况下效果更好。

这里有几个问题

char *recepcao() { 
    char *receber;
    scanf("%s", receber);
    return receber;
}

有 2 个错误:receber 是一个未初始化的指针,它指向 特别是,因此传递给 scanf 是未定义的行为。

另外receberrecepcao的局部变量,当 recepcao returns,所以你返回了一个指向无效内存位置的指针。

最好在 main 中声明一个数组并将该数组(及其大小)传递给函数。我也会用fgets代替scanf,比较容易控制fgets.

您可以使用 strlen(string) 而不是

for(i=0; string[i]!='[=11=]'; ++i) {
        continue;
}

但如果不允许您在作业中使用 strlen 也没关系。

在这里使用 pow 也太过分了,不需要包含浮点数 算术 (pow returns a double) 当你可以使用变量 exp 初始化为 1 并乘法 它在每个循环中增加 10(请参阅下面的代码)。

还有

int *var;
...
for(j=0; j<i-1; ++j) {
    var[j]=(string[j]-'0');
    ...

不会工作,因为 var 没有初始化。没有必要使用 指针在这里,甚至不是一个数组,你没有对 保存的值。最好使用单个 char 变量:

char var;
...
for(j=0; j<i-1; ++j) {
    var = string[j] - '0';
    ...

所以你可以用这样的信息重写你的程序:

#include <ctype.h>

char *recepcao(char *receber, size_t len) {
    if(fgets(receber, len, stdin) == NULL)
    {
        fprintf(stderr, "Could not read from user\n");
        return NULL;
    }

    // removing newline
    receber[strcspn(receber, "\n")] = 0;

    return receber;
}

int conversao(const char *string) { 
    if(string == NULL)
        return 0;

    size_t len = strlen(string);

    int res = 0, pow = 1;

    for(size_t i = len - 1; i >= 0; --i)
    {
        if(isdigit(string[i]))
        {
            res += (string[i] - '0') * pow;
            pow *= 10;
        } else {
            fprintf(stderr, "'%s' contains non-digits\n", string);
            return 0;
        }
    }

    return res;
}

int main(void)
{
    char line[100];

    if(recepcao(line, sizeof line) == NULL)
        return 1;

    printf("%d\n", conversao(line));

    return 0;
}