C

时间:2016-06-21 20:41:53

标签: c arrays pointers scanf

我试图将文件中的一些float读入我已定义的数组:

float *taps[3];

我正在从文件中读取浮点数:

for (i = 0; i < 3; i++) {
fscanf(tapsInput, "%f", taps[i]);
}

我知道当指针指向错误时会出现分段错误,但我不确定我的代码在此设置中出了什么问题。

2 个答案:

答案 0 :(得分:4)

  

我知道当指针指向错误时会出现分段错误

你的代码也是如此。在您的情况下,taps[i]已整理并指向无效的内存。所以,在你可以在taps[i]中存储任何内容之前,你需要为它分配内存。

在另一种方法中,我没有看到taps成为指针数组float的原因。你可以简单地做

float taps[3];

然后,将各个元素的地址传递给fscanf(),如

fscanf(tapsInput, "%f", &taps[i]);

答案 1 :(得分:1)

AlexD拥有它:float *taps[3];是一个指向float的指针数组,但您没有任何数据可以实际

这是工作代码:

#include "stdio.h"

float taps[3];
int
main(){
  for (int i = 0; i < 3; i++) {
      fscanf(stdin, "%f", &taps[i]);
  }
  printf("Values: %f %f %f", taps[0], taps[1], taps[2]);
  return 0;
}

结果:

$ gcc taps.c
$ ./a.out
42
33.5
0.01
Values: 42.000000 33.500000 0.010000
$

这就是你的代码无效的原因。当您编写float taps[3];时,您将3个指针的空间声明为float,即3个地址。但是,您不会对它们进行初始化,因此它们中的数据将是上次设置时在这些位置发生的随机位。当您尝试使用fprintf设置它们时,系统会查看内容,找到发生在那里的任何地址,说0xBEEF,并尝试在那里发送数据。幸运的是,这不是一个可以放置数据的地方,因此会引发分段错误。如果你不幸运的话,它可能恰好是你可以编写你的数据的地方,这意味着在未来的某个时候你可能会有一些真的奇怪的事情,因为你糊涂了与记忆。

现在,当您使用float taps[3];时,您现在正在内存中定义一个位置,该位置从放置tabs[0]的位置开始,并且有三个浮点数。然后你的陈述

fscanf(stdin, "%f", &taps[i]);

获取该数组,找到元素的地址 - 这就是&运算符的作用,并将值放在那里。

换句话说,{em>地址数组中的float *taps[3];; float taps[3];实际上是为3 floats分配空间。

<强>更新

在答案中编写代码比在评论中编写代码更容易。

亚历克斯,你需要考虑你的代码在说什么。正如我们所说,float *taps[3];是指向float的指针数组,因此每个float*元素都是一个地址。 float taps[3];float s的实际数组,因此每个元素都是float。因此,taps[2]本身就是float

(顺便说一句,请注意,您可以编写 taps[3]并且编译器不关心,因为C不检查边界。这将意味着计算机将查看taps[2]旁边的地址,并将其视为float,无论实际存在的是什么。)

&运算符获取其右侧的地址,因此当我们fscanf给出&taps[i]的参数时,我们将使用地址浮动的,并将其传递给fscanf。 float的地址是指向floatfloat*的指针。

现在,这里有一些奇怪的地方。在C中,数组的名称基本上代表一个地址。所以,如果我有一个原型foo(float[])的函数,它说该函数需要一个float数组。数组的名称本质上是对第一个值的地址的引用,因此该代码可以工作:

void foo(float[] bar){
  // print three floats in an array, and God;s help you if there 
  // aren't three there.
  for(int ix=0; ix<4; ix++){
    printf("%f\n",bar[ix]);
  }
}
float taps[3] = {1.0, 2.0, 3.0 };
foo(taps);

...但是如果我尝试foo(*taps)它就不会编译。为什么呢?

想象一下这个比喻。我家的地址是1662.所以,当我想给人们提一下我的房子时,我告诉他们这是1662号。但是'1662'就是我家的号码:地址1662处的东西是我的房子

获取“1662”的地址意味着什么?没什么 - 这只是一个数字 - 地址不占用任何空间。类似地,在C中,名称taps表示内存中地点的地址 - 但这只是一个数字,它不占用任何空间。所以,当你说*taps时,你正在取一个数字的地址;这是没有意义的,所以它不会编译。

现在,因为数组的名称只是一个地址,我们可以像这样声明我们的函数foo

void foo(float*) {

同样有效。

事实上,在C中,对于任何数组T和整数i,这都是一个标识:

*(T+i) == T[i]

,具体而言T代表与&T[0]相同的地址。

所以,现在,想一想。您在某个函数quux中有一个调用foo(float*)的数组:

void quux() {
  float taps[3] = { 1.0, 2.0, 3.0 };
  foo(taps);  // This compiles: you're giving the function 
              // an array name, and that represents the address
              // of an array
  // but foo(*taps) WON'T compile: that's the address of the address
  // of an array.
  return ;
}