我试图将文件中的一些float
读入我已定义的数组:
float *taps[3];
我正在从文件中读取浮点数:
for (i = 0; i < 3; i++) {
fscanf(tapsInput, "%f", taps[i]);
}
我知道当指针指向错误时会出现分段错误,但我不确定我的代码在此设置中出了什么问题。
答案 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的地址是指向float
或float*
的指针。
现在,这里有一些奇怪的地方。在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 ;
}