C 指向数组的指针

您可以先跳过本章,等了解了 C 指针的概念之后,再来学习本章的内容。

如果您对 C 语言中指针的概念有所了解,那么就可以开始本章的学习。数组名是一个指向数组中第一个元素的常量指针。因此,在下面的声明中:

  1. double balance[50];

balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。因此,下面的程序片段把 p 赋值为 balance 的第一个元素的地址:

  1. double *p;
  2. double balance[10];
  3.  
  4. p = balance;

使用数组名作为常量指针是合法的,反之亦然。因此,*(balance + 4) 是一种访问 balance[4] 数据的合法方式。

一旦您把第一个元素的地址存储在 p 中,您就可以使用 p、(p+1)、*(p+2) 等来访问数组元素。下面的实例演示了上面讨论到的这些概念:

实例

  1. #include <stdio.h>
  2. int main ()
  3. {
  4. /* 带有 5 个元素的整型数组 */
  5. double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0};
  6. double *p;
  7. int i;
  8. p = balance;
  9. /* 输出数组中每个元素的值 */
  10. printf( "使用指针的数组值\n");
  11. for ( i = 0; i < 5; i++ )
  12. {
  13. printf("*(p + %d) : %f\n", i, *(p + i) );
  14. }
  15. printf( "使用 balance 作为地址的数组值\n");
  16. for ( i = 0; i < 5; i++ )
  17. {
  18. printf("*(balance + %d) : %f\n", i, *(balance + i) );
  19. }
  20. return 0;
  21. }

当上面的代码被编译和执行时,它会产生下列结果:

  1. 使用指针的数组值
  2. *(p + 0) : 1000.000000
  3. *(p + 1) : 2.000000
  4. *(p + 2) : 3.400000
  5. *(p + 3) : 17.000000
  6. *(p + 4) : 50.000000
  7. 使用 balance 作为地址的数组值
  8. *(balance + 0) : 1000.000000
  9. *(balance + 1) : 2.000000
  10. *(balance + 2) : 3.400000
  11. *(balance + 3) : 17.000000
  12. *(balance + 4) : 50.000000

在上面的实例中,p 是一个指向 double 型的指针,这意味着它可以存储一个 double 类型的变量。一旦我们有了 p 中的地址,*p 将给出存储在 p 中相应地址的值,正如上面实例中所演示的。

关于 double 类型与 float 类型

printf() 只会看到双精度数,printf 的 %f 格式总是得到 double,所以在 printf() 中使用 %f%lf 的输出显示效果是一样的。但是对于变量来说,double 类型比 float 类型的精度要高。double 精度更高,是指它存储的小数位数更多,但是输出默认都是 6 位小数,如果你想输出更多小数,可以自己控制,比如 %.10lf 就输出 10 位小数。

所以一般情况下 double 类型的占位符可以用 %lf。

float 与 double 类型区别

float 单精度浮点数在机内占 4 个字节,用 32 位二进制描述。

double 双精度浮点数在机内占 8 个字节,用 64 位二进制描述。

C 指向数组的指针 - 图1

浮点数在机内用指数型式表示,分解为:数符,尾数,指数符,指数四部分。

数符占 1 位二进制,表示数的正负。

指数符占 1 位二进制,表示指数的正负。

尾数表示浮点数有效数字,0.xxxxxxx, 但不存开头的 0 和点。

指数存指数的有效数字。

指数占多少位,尾数占多少位,由计算机系统决定。

可能是数符加尾数占 24 位,指数符加指数占 8 位 — float

数符加尾数占 48 位,指数符加指数占 16 位 — double

知道了这四部分的占位,按二进制估计大小范围,再换算为十进制,就是你想知道的数值范围。

对编程人员来说,double 和 float 的区别是 double 精度高,有效数字 16 位,float 精度 7 位。但 double 消耗内存是 float 的两倍,double 的运算速度比 float 慢得多,C 语言中数学函数名称 double 和 float 不同,不要写错,能用单精度时不要用双精度(以省内存,加快运算速度)。
类型比特数有效数字数值范围
float32 6-7 -3.410(-38)~3.410(38)
double 64 15-16 -1.710(-308)~1.710(308)
long double 128 18-19 -1.210(-4932)~1.210(4932)

简单来说,Float 为单精度,内存中占 4 个字节,有效数位是 7 位(因为有正负,所以不是8位),在我的电脑且 VC++6.0 平台中默认显示是6位有效数字;double为 双精度,占 8 个字节,有效数位是 16 位,但在我的电脑且 VC++6.0 平台中默认显示同样是 6 位有效数字

例子:在 C 和 C++ 中,如下赋值语句:

  1. float a=0.1;

编译器报错:warning C4305: 'initializing' : truncation from 'const double ' to 'float '

原因: 在 C/C++ 中(也不知道是不是就在 VC++ 中这样),上述语句等号右边 0.1,我们以为它是个 float,但是编译器却把它认为是个 double(因为小数默认是 double),所以要报这个 warning,一般改成 0.1f 就没事了。

本人通常的做法,经常使用 double,而不喜欢使用 float。

C 语言和 C# 语言中,对于浮点类型的数据采用单精度类型 float 和双精度类型 double 来存储,float 数据占用 32bit, double 数据占用 64bit,我们在声明一个变量 float f= 2.25f 的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是 float 还是 double 在存储方式上都是遵从 IEEE 的规范 的,float 遵从的是 IEEE R32.24 ,而 double 遵从的是 R64.53。

无论是单精度还是双精度在存储中都分为三个部分:

  • 符号位(Sign):0 代表正,1 代表为负。
  • 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储。
  • 尾数部分(Mantissa):尾数部分。

C++ 中 float 与 double 类型的精度区别

double 精度高,有效数字 15-16 位,float 精度低,有效数字 6-7位,但是 double 消耗的内存是 float 的两倍,运算速度比 float 慢得多,建议能用 float 保证精度的就用 float,少用 double。

  1. #include <iostream>
  2. #include <iomanip>
  3. using namespace std;
  4. int main()
  5. {
  6. float a=12.257902012398877;
  7. double b=12.257902012398877;
  8. const float PI=3.1415926; // 常量定义
  9. cout<<setprecision(15)<<a<<endl; // 只有6-7位有效数字,后面的就不精确
  10. cout<<setprecision(15)<<b<<endl; // 有15-16位有效数字,所以完全正确
  11. cout<<setprecision(15)<<PI<<endl;
  12. return 0;
  13. }