【问题标题】:Pointers and segmentation faults in CC中的指针和分段错误
【发布时间】:2016-06-21 20:41:53
【问题描述】:

我正在尝试将文件中的一些 floats 读取到我创建的数组中,如下所示:

float *taps[3];

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

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

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

【问题讨论】:

  • float taps[3]; fscanf(tapsInput, "%f", &amp;taps[i]);
  • @AlexD 那么你需要把地址传递给scanf。
  • @SouravGhosh 当然,我刚刚添加了它。
  • @AlexD 我已经尝试过了,但是当我尝试编译程序时,它会给我一个警告。具体来说:“警告:%f 需要 'float *' 类型的参数,但参数 3 的类型为 'float **' 我认为这不会使其工作
  • @Nick 您是否从声明中删除了*

标签: c arrays pointers scanf


【解决方案1】:

我知道当指针指向错误时会发生分段错误

您的代码也是如此。在您的情况下,taps[i] 是未初始化的并指向 invalid 内存。所以,在你可以在taps[i] 中存储任何东西之前,你需要为它分配内存。

在另一种方法中,我看不出taps 是指向float 的指针数组的原因。你可以简单地做

float taps[3];

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

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

【讨论】:

  • 感谢您的回答。实际上,我必须将指向浮点数组开头的指针传递给代码中的另一个函数。我认为 *taps[3] 与 *(taps+3) 相同。声明指向数组开头的指针,然后再放 4 个数组元素会更准确吗?
【解决方案2】:

AlexD 有它:float *taps[3]; 是一个指向 float 的指针数组,但您没有任何地方可以让数据真正go

这是工作代码:

#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]);

获取该数组,找到元素的地址——这就是&amp; 运算符所做的,并将你的值放在那里。

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

更新

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

Alex,您需要考虑一下您的代码在说什么。正如我们所说,float *taps[3]; 是一个指向float 的指针数组,因此每个float* 元素都是一个地址。 float taps[3];floats 的实际数组,因此每个元素都是float。所以,比如说,taps[2] 本身就是一个float

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

&amp; 运算符获取其右侧的地址,因此当我们给 fscanf 一个参数 &amp;taps[i] 时,我们将获取该浮点数的 地址,并且将其传递给fscanf。浮点地址是指向floatfloat* 的指针。

现在,这里出现了一些奇怪的地方。在 C 中,array 的名称基本上代表了一个地址。所以,如果我有一个原型为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 表示与&amp;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 ;
}

【讨论】:

  • 谢谢@Charlie Martin。这是一个非常详细的答案,并解决了我的一些问题。我需要将一个指向浮点数组开头的指针传递给我后来的一个函数,这就是我声明我的数组的原因。我尝试使用您的声明并将数组作为 *taps 传递给我的函数,但它返回错误。这样做的正确方法是什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-10
  • 2013-09-18
  • 2016-02-24
  • 2021-05-25
  • 2012-11-24
  • 1970-01-01
  • 2020-11-18
相关资源
最近更新 更多