如何纠正 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
是未定义的行为。
另外receber
是recepcao
的局部变量,当
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;
}
为了将以整数序列作为地址的指针转换为整数,我编写了以下程序:
#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
是未定义的行为。
另外receber
是recepcao
的局部变量,当
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;
}