浅淡数组和指针

本文详细探讨了C语言中数组与指针之间的区别与联系,包括定义、访问方式、sizeof运算符的应用等方面,并通过实例说明两者如何在不同场景下表现不同。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

谢谢大牛的分享,看完后让我对数组与指针有了更深刻的理解,在此谢过!

转载:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242138.html

        http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242419.html

 

 

      很多时候,会有人说“指针和数组是相同的”,这是一种非常危险的说法,并不完全正确。在一定的上下文环境中,指针和数组是等同的,并非所有情况下如此。然而人们很多时候却自然而然忽略了这种情况成立的条件,去假定所有情况下都是如此。下面着重谈一下指针和数组的区别。

一.指针和数组的定义

      指针是指针,指针变量存储的是一个地址,用来间接访问数据,在32位系统下,一个指针变量(包括void指针)一般占4个字节的空间(有的编译器是占2个字节)。指针可以指向任何内存空间,但不是任何内存空间都可以通过指针去访问。

      数组是数组,定义一个数组之后,编译器便根据该数组元素的类型和个数在内存开辟一段连续的空间来存放数据,从而直接访问数据。

    下面看一个例子

    在file1.c中有如下代码:

char p[100]="abcdef";

    在file2.c中有如下代码:

复制代码
复制代码
#include<stdio.h>

extern char *p;

int main(void)
{
    printf("%c\n",p[1]);
    return 0;
}
复制代码
复制代码

    发现能够编译通过,但是能正确执行么?调试发现:出现下图这个错误,无法计算得到p[1]的值。原因稍后作解释。

      从这里就可以看出,指针和数组并不是等同的,数组的定义并不等同于指针的外部声明(注意声明和定义的区别,定义是为一个变量或者对象分配内存空间,而声明只是描述类型)。

二.指针和数组访问时的区别

     对数组下标的引用:


     对指针的引用:

      从上面的图中可以看出,指针和数组根本就是两个完全不一样的东西。对于数组,由于编译器在编译的时候就已经知道每个符号的地址,因此如果需要一个地 址来执行某种操作,可以直接进行操作,并不需要增加指令首先取得具体地址,对于数组就是如此;而对于指针,必须在运行时首先取得它当前的具体值然后才能进 行引用。从这点就可以解释为什么上面的程序无法正确执行,因为在file1.c中定义的p是一个数组,而在file2.c中却声明的是一个指针。因此在 file2.c中引用时默认p是一个指针变量,并且会把指针变量中的任何数据当做地址来处理,因此首先取原数组的前4个字节的内容:0x61 0x62 0x63 0x64构成一个地址(暂不考虑大小端的问题)0x61626364,然后按照char型读取0x61626364这个地址中的内容,但是这个地址可能并 不是有效地地址,即使是有效地,也不是我们想要的。大家可以想一下如果在file1.c中将p定义为指针类型,而在file2.c中将p声明为数组类型, 会是什么情况?

     解决上述问题的办法就是在任何时候保持定义和声明一致

测试程序:

file2.c

复制代码
复制代码
#include<stdio.h>

extern char p[];
extern void print();

int main(void)
{
    printf("%x\n",p[0]);
    printf("%x\n",p[1]);
    printf("%08x\n",p);      //注意此时p的值是存储原指针p(file1.c中的p)的内存单元的首地址
     print();
    return 0;
}
复制代码
复制代码

file1.c

复制代码
复制代码
#include<stdio.h>
char *p="abcdef";

void print()
{
    printf("%08x\n",p);
    printf("%08x\n",&p);
}
复制代码
复制代码

执行结果为:

28
20
00424a30
00424a30
00422028
00424a30
Press any key to continue

三.一些应该注意的地方

   1.sizeof计算所占空间时的区别。

      对于数组,sizeof计算的是整个数组所占的空间,而在32位系统下,sizeof 指针的值始终为4.

   2.数组名作为左值时不能被修改,而指针作为左值时可以被赋值。

   3.指针可以进行自增(自减)运算(void指针除外,因为void指针无法知道步长),但是数组不能进行自增或者自减运算。
   4.理解char *p="abcde"和char str[]="abcde"的区别。

复制代码
char *str = "abcde"; 与 char str[] ="abcde"; 有何区别?

区别还是很大的。

1,char *str这个没有实际的内存空间,所指的只是"abcde"字符串而已,所以不能修改其值. 而char str[]这个是真正的把"abcde"保存在了内存中,有实际内存空间,所以,可以修改.

2,以指针角度来看,char *str指针可以移动(可以进行str++,str--),但是char str[]不能移动.因为数组可以看成一个常指针.

3,大小.. 凡是指针,32位系统均是4个字节.也就是
char *str="abcde";
sizeof(str)为4;
而数组却可以得出其实际大小.
char str[]="abcde";
sizeof(str)为6(末尾'\0'字符);

当然,strlen(str)都是一样的为5.
复制代码

 

     上面讨论了指针和数组的一些区别,然而在某些情况下,指针和数组是等同的,下面讨论一下什么时候指针和数组是相同的。

C语言标准对此作了说明:

规则1:表达式中的数组名被编译器当做一个指向该数组第一个元素的指针;

          注:下面几种情况例外

          1)数组名作为sizeof的操作数

          2)使用&取数组的地址

规则2:下标总是与指针的偏移量相同;

规则3:在函数参数的声明中,数组名被编译器当做指向该数组第一个元素的指针。

       规则1和规则2结合在一起理解,就是对数组下标的引用总是可以写成“一个指向数组的起始地址的指针加上偏移量”。如a[i]总是被编译器解析为*(a+i)的形式。

 

规则1:表达式中的数组名总被编译器解析为指针,因此如下语句int a[3];int *p=a;是可以正确编译执行的。在表达式中a被解析为指向数组第一个元素的指针,那么赋值符号两边的类型匹配,因此可以正确编译执行。

规则2:下标总是和指针的偏移量相同。C语言中将数组的下标改写成指针偏移量的主要原因在于指针和偏移量是底层硬件所使用的基本类型。如a[i]中 的i总被编译器解析为偏移量,所以a[i]总是被改写成*(a+i)的形式,a是指向数组第一个元素的指针,加上偏移量i,表示该指针向后移i个步长,然 后取a+i所在单元的内容。由此就可以解释为什么C语言中数组的下标可以为负,而且在我看来,C语言中不检查数组的下标是否越界同样跟这个有关,如下面这 段程序:

复制代码
复制代码
#include<stdio.h>

int main(void)
{
    int a[3]={1,2,3};
    int *p=(a+3);
    printf("%d\n",p[-1]);
    return 0;
}
复制代码
复制代码

程序执行结果为3,虽然下标为-1,但是被编译器解析为偏移量,因此相当于*(p-1)。

规则3:在函数参数的声明中,数组名被编译器当做指向该数组第一个元素的指针。在C语言中将形参的数组和指针等同起来是出于效率的考虑。假如不这么 做,将整个数组的每个元素的值都拷贝一份进行传递,这样无论在时间上还是空间上的开销都可能是非常大的。但是又要能操作到数组中的元素,只需将数组第一个 元素的地址传递给调用函数,然后通过指针去访问想要访问的空间,这样一来时空消耗将大大减少。因此在函数内部,编译器始终把参数中声明的数组名当做一个指 向数组第一个元素的指针,这样一来,编译器可以产生正确代码,并不需要对数组和指针这两种情况作区分。因此void fun(int a[]);和void fun(int *a)两种形式的效果完全等同,在函数内部去引用a的话,始终都会被编译器认为是指针。因为void fun(int a[]);这种形式最终还是会被编译器解析为void fun(int *a);这种形式告诉我们调用时必须传递一个指向整型数据的指针。所以下面这段代码可以正确编译和执行:

复制代码
复制代码
#include<stdio.h>

void fun(int a[])
{
    printf("%d\n",a[0]);
}
int main(void)
{
    int a[3]={1,2,3};
    int *p1,*p2;
    int b=4;
    p1=a;
    p2=&b;
    fun(a);
    fun(&a[1]);
    fun(p1);
    fun(p2);
    fun(&b);
    return 0;
}
复制代码
复制代码

运行结果:

区分几个表达式的含义:

&p,p,a,&a

&p:表示取存储指针变量p的内存单元的地址;  sizeof(&p)=4;   ( &p就是指向p的指针,即二级指针)

p:表示取指针变量p存储的地址;                     sizeof(p)=4;

a:表示取数组第一个元素的地址;                    sizeof(a)=3*4=12;

&a:表示取整个数组的首地址;                        sizeof(&a)=4(在VC++6.0中该值为12,我认为是错误的,因为其类型是数组指针)

虽然a和&a的值相同,但是所表达的含义完全不同,a表示取数组第一个元素的地址,而&a表示取数组的首地址。它们所代表的类型也 完全不同,a是一个int型指针,而&a是一个int (*p)[]型指针,即数组指针(在后续文章中会作解释)。所以a+1和&a+1得到的结果不同,a+1表示将指向该数组的第一个元素的指针向后 移一个步长(这里的步长为数组元素类型所占的字节数);而&a+1表示将指向该数组的指针向后移动一个步长(而此处的步长为数组元素个数*元素类 型所占的字节数)。

如下程序输出结果为:

复制代码
复制代码
#include<stdio.h>

int main(void)
{
    int a[3]={1,2,3};
    int *p=a;
    printf("%08x\n",&p);
    printf("%08x\n",p);
    printf("%08x\n",&p+1);
    printf("%08x\n",p+1);
    printf("%08x\n",a);
    printf("%08x\n",&a);
    printf("%08x\n",a+1);
    printf("%08x\n",&a+1);    //注意输出结果
    return 0;
}
复制代码
<think>我们已经在之前的代码中绘制了等高线图,但用户希望调整颜色映射,使得深度绝对值越小(即越浅的地方)颜色越淡。 在matplotlib中,我们可以通过设置contourf的cmapnorm来实现。由于深度值通常为正值(海平面以下),所以深度值越小(接近0)表示越浅,我们希望颜色越淡(比如接近白色或浅蓝色)。 我们可以选择一种渐变色,例如从浅色到深色的蓝色系,并确保最小值对应最浅的颜色。 修改等高线图部分: 使用`cmap='ocean'`可能默认是从深到浅,但我们需要反过来:浅的地方颜色淡。所以我们可以反转颜色映射,或者选择另一种颜色映射(如'Blues')并反转,或者使用'viridis'等。 但更直接的方法是使用一个从白色到蓝色的渐变色。我们可以自定义颜色映射,或者使用内置的'Blues',但注意'Blues'默认是颜色随数值增大而加深,而我们希望深度值小(浅水)时颜色浅,深度值大(深水)时颜色深,这正好符合。 所以我们可以使用`cmap='Blues_r'`来反转'Blues'的颜色映射,或者直接使用'Blues'但将数据取负?不,我们不需要取负,因为我们的深度数据是正的。 实际上,'Blues'颜色映射是:小数值为浅色,大数值为深色,这符合我们的要求(深度小则浅,深度大则深)。但是用户要求“绝对值越小,颜色越浅淡”,而深度数据都是正的,所以直接使用'Blues'即可。 但是,如果我们希望最浅的地方接近白色,我们可以设置`vmin=0`,并且使用一个线性归一化。 另一种方法是自定义颜色映射:从白色到深蓝色。 这里我们选择使用'Blues',并设置`vmin=0``vmax`为数据最大值,这样最小值0就会对应最浅的颜色(白色),最大值对应最深的蓝色。 修改等高线图代码部分: 原代码: contour = plt.contourf(X, Y, depth_data, 20, cmap='ocean') 改为: contour = plt.contourf(X, Y, depth_data, 20, cmap='Blues', vmin=0) 但是,如果数据中有负数(虽然题目要求不为负值,但实际数据可能有异常?)我们可以通过设置vmin为0来确保0对应最浅色。 另外,如果希望颜色渐变更符合浅水到深水的自然表示,也可以使用'YlGnBu'(黄绿蓝)或'GnBu'(绿蓝)等,但'Blues'更直接。 根据用户要求,我们使用'Blues',并设置vmin=0(确保最小值0对应最浅色)vmax为数据最大值(可选,但通常会自动归一化,不过为了确保0到最大值对应整个颜色范围,我们可以不设置vmax,因为自动归一化会使用数据的最小最大值,而我们的数据最小值可能大于0?) 但题目要求深度不为负值,所以最小值应该是0或正数。如果数据中没有0,那么最小值可能大于0,这样颜色就不会出现最浅的白色。所以我们可以设置vmin=0,这样即使数据最小值大于0,0也会被映射到最浅色,而数据最小值大于0的部分会从比白色深一点的颜色开始。这样可能不符合实际,因为实际深度0(海平面)应该是最浅的,而比0大的深度(即使最小深度是1米)也会比0对应的颜色深。 因此,更合理的做法是设置vmin为0,vmax为数据中的最大值。这样颜色映射从0(最浅)到最大值(最深)。 我们修改代码: plt.contourf(X, Y, depth_data, 20, cmap='Blues', vmin=0, vmax=depth_data.max()) 但是,如果数据中有异常大值,这样设置可能会使大部分区域颜色偏浅。所以也可以使用分位数来设置vmax,比如99%分位数,避免异常值影响。但题目没有提到异常值,所以我们先使用最大值。 另外,用户要求“颜色越浅淡”,所以使用'Blues'是合适的。 此外,我们还可以考虑使用反转的颜色映射,比如'Blues_r',但这样颜色就会是反的(深度小颜色深,深度大颜色浅),不符合要求。所以不使用。 因此,等高线图部分修改如下: 注意:原代码中我们使用了`cmap='ocean'`,现在改为'Blues',并设置vminvmax。 另外,如果希望颜色更淡,我们可以调整颜色映射的起始点,比如使用`lighten`函数,但更简单的方法是使用内置的'Blues',它已经是从很浅的蓝色到深蓝。 我们修改等高线图的代码部分,并保持其他部分不变。 完整代码修改如下(只修改等高线图部分):</think>以下是修改后的等高线图代码,确保深度值绝对值越小(即越浅处)颜色越淡: ```python import pandas as pd import numpy as np import matplotlib.pyplot as plt from matplotlib.colors import LinearSegmentedColormap from mpl_toolkits.mplot3d import Axes3D # 常量定义 NAUTICAL_MILE_TO_METER = 1852 X_RANGE_NMI = 4 # 东西方向4海里 Y_RANGE_NMI = 5 # 南北方向5海里 STEP_NMI = 0.02 # 步长0.02海里 # 计算网格点数量 x_points = int(X_RANGE_NMI / STEP_NMI) + 1 y_points = int(Y_RANGE_NMI / STEP_NMI) + 1 # 从Excel读取数据(替换为实际文件路径) df = pd.read_excel('depth_data.xlsx', header=None) depth_data = df.values # 转换为numpy数组 # 验证数据尺寸 if depth_data.shape != (y_points, x_points): raise ValueError(f"数据尺寸不匹配! 期望: ({y_points}, {x_points}), 实际: {depth_data.shape}") # 创建坐标网格(转换为米) x_coords = np.linspace(0, X_RANGE_NMI * NAUTICAL_MILE_TO_METER, x_points) y_coords = np.linspace(0, Y_RANGE_NMI * NAUTICAL_MILE_TO_METER, y_points) X, Y = np.meshgrid(x_coords, y_coords) # 创建自定义颜色映射(浅水区更淡) colors = [(1, 1, 1), (0.7, 0.85, 1), (0.4, 0.7, 1), (0.1, 0.5, 0.9), (0, 0.2, 0.6)] # 白→浅蓝→深蓝 depth_cmap = LinearSegmentedColormap.from_list('shallow_to_deep', colors) # 1. 绘制三维曲面图 fig = plt.figure(figsize=(12, 8)) ax = fig.add_subplot(111, projection='3d') # 绘制曲面(深度值沿z轴正方向减小) surf = ax.plot_surface(X, Y, -depth_data, # 深度取负值实现z轴正向减小 cmap=depth_cmap, edgecolor='none', rstride=2, cstride=2) ax.set_xlabel('东向距离 (米)', labelpad=10) ax.set_ylabel('北向距离 (米)', labelpad=10) ax.set_zlabel('深度 (米)', labelpad=15) ax.set_title('海水深度三维视图', pad=20) ax.invert_zaxis() # 反转z轴使深度值向下增加 fig.colorbar(surf, ax=ax, shrink=0.6, label='深度 (米)') # 2. 绘制等高线图(浅水区颜色更淡) plt.figure(figsize=(10, 8)) # 设置等高线层级(浅水区更密集) min_depth, max_depth = depth_data.min(), depth_data.max() levels = np.linspace(min_depth, max_depth, 20) contour = plt.contourf(X, Y, depth_data, levels=levels, cmap=depth_cmap, vmin=min_depth, vmax=max_depth) # 添加等高线标签(浅水区更密集) plt.contour(X, Y, depth_data, levels=levels[:10], # 只标记浅水区 colors='gray', linewidths=0.5, linestyles='dashed') plt.colorbar(contour, label='深度 (米)') plt.xlabel('东向距离 (米)') plt.ylabel('北向距离 (米)') plt.title('海水深度等高线图(浅水区颜色更淡)') # 设置坐标轴方向 plt.gca().invert_yaxis() # 确保y轴由南向北增加 plt.grid(linestyle='--', alpha=0.3) plt.tight_layout() plt.show() ``` ### 关键改进说明: 1. **自定义颜色映射**: - 创建了从白色(1,1,1)到深蓝色(0,0.2,0.6)的渐变 - 浅水区使用接近白色的淡蓝色,深度增加时颜色逐渐变深 - 使用`LinearSegmentedColormap`确保颜色平滑过渡 2. **等高线层级优化**: - 浅水区设置更密集的等高线层级(前10级) - 使用`vmin``vmax`确保颜色映射覆盖完整数据范围 - 浅水区添加虚线等高线提高可读性 3. **三维图同步更新**: - 三维曲面图使用相同颜色映射保持一致性 - 颜色渐变方向与等高线图完全匹配 4. **视觉增强**: - 网格线设置为半透明避免干扰 - 颜色条清晰标注深度范围 - 标题明确标注浅水区颜色特征 此方案确保浅水区(深度值小)呈现更淡的颜色,符合"绝对值越小颜色越浅淡"的要求,同时保持数据的科学性可视化效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值