混淆指针与数组导致的问题

概述

在 c 语言中,一个数组变量可以理解成定义了一个指向数组的指针。大多数情况下这样的理解是没有问题的,但是还是要弄清楚他们的区别,否则会导致错误。本文会从因为混淆他们所导致的错误,揭示一个 C 语言鲜为人知的特点。

问题实例

// 在 test.c 文件定义一个全局的数组变量
char g_name[] = {'Y','u','n','\0'};
#include<stdio.h>

// 用指针的引用 test.c 定义的全局变量
extern char* g_name;
int main()
{
    printf("%c\n",g_name[0]);
    return 0;
}

对于这样的用法,对程序进行编译的时候没有出现错误,但是程序运行的时候程序就会奔溃。

修正

#include<stdio.h>

// 用数组的方式,引用 test.c里面的数组
extern char g_name[];
int main()
{
    printf("%c\n",g_name[0]);
    return 0;
}

能够正常运行

问题分析

数组的内存模型

#include <stdio.h>

int main()
{
    char name[] = {'Y','u','n','\0'};

    printf("      Addr of name:%p\n",name);
    printf("   Addr of name[0]:%p\n",&name[0]);
    printf("Content of name[0]:0x%x (%c)\n",name[0],name[0]);

    printf("   Addr of name[1]:%p\n",&name[1]);
    printf("Content of name[1]:0x%x (%c)\n",name[1],name[1]);

    printf("   Addr of name[2]:%p\n",&name[2]);
    printf("Content of name[2]:0x%x (%c)\n",name[2],name[2]);

    printf("   Addr of name[3]:%p\n",&name[3]);
    printf("Content of name[3]:0x%x (%c)\n",name[3],name[3]);

    return 0;
}

      Addr of name:0022CCD0
   Addr of name[0]:0022CCD0
Content of name[0]:0x59 (Y)
   Addr of name[1]:0022CCD1
Content of name[1]:0x75 (u)
   Addr of name[2]:0022CCD2
Content of name[2]:0x6e (n)
   Addr of name[3]:0022CCD3
Content of name[3]:0x0 ( )

从上面的结果可以看出C 语言数组的变量名可认为代表数组的开始地址,从数组的开始地址处每个数组的地址都是如下的顺序出现的

 

指针的内存模型

#include <stdio.h>

int main()
{
    char name[] = {'Y','u','n','\0'};
    char *p_name = &name[0];

    printf("    Addr of name:%p\n",name);
    printf("   Addr of pname:%p\n",&p_name);
    printf("Content of pname:0x%x \n",p_name);

    printf("Content of pname[0]:0x%x (%c)\n",p_name[0],p_name[0]);
    printf("Content of pname[1]:0x%x (%c)\n",p_name[1],p_name[1]);
    printf("Content of pname[2]:0x%x (%c)\n",p_name[2],p_name[2]);
    printf("Content of pname[3]:0x%x (%c)\n",p_name[3],p_name[3]);

    return 0;
}

    Addr of name:0022cccc
   Addr of pname:0022ccd0
Content of pname:0x0022ccd0
Content of pname[0]:0x59 (Y)
Content of pname[1]:0x75 (u)
Content of pname[2]:0x6e (n)
Content of pname[3]:0x0 ( )

 

问题成因

当从test.c的角度看 g_name 是定义在这一文件中的字符数组。由于 g_name 是定义好的全局变量,所以它的内存分配是在 .data 段。但是在外部以指针的方式引用的时候,从它的角度而言,g_name是一个在其他文件定义的指针变量,并且它只是引用这一变量,当他们的.o文件连接的时候变量会被当成同一个。由于在 wrong.c 里面,它被定义成指针那么它回去访问 0x006e7559 这个地址,但是这个地址可能是不合法的所以可能会出现段错误的情况。最主要的一点的原因是:当一个文件被编译成目标文件的时候,目标文件中将不会包含C语言的任何信息。

链接的过程就是将所有的同名符号合成一个。链接器发现不了目标文件中符号不匹配的问题

预防措施

如果链接器不能发现符号不匹配问题,那就只能依靠编译器了。在编写程序的时候应当将一个外部需要引用的变量或者函数放在一个头文件中,然后在定义和引用的源文件中包含它,这种包含就能够建立起联系。有了这种联系编译器就能够发现问题。关于声明和引用不匹配的问题同样会发生在函数的使用上面,当我们进行 extern 使用的时候,要格外的注意。

总结

通过这个问题,回顾了指针和数组的内存模型,并揭示了链接器并不关系 C 语法的这一事实。

为了避免产生混淆指针与数组产生的问题,我们需要养成总是将头文件作为(变量和函数的)定义和引用的桥梁这一编程习惯。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1. 智慧社区背景与挑战 随着城市化的快速发展,社区面临健康、安全、邻里关系和服务质量等多方面的挑战。华为技术有限公司提出智慧社区解决方案,旨在通过先进的数字化技术应对这些问题,提升城市社区的生活质量。 2. 技术推动智慧社区发展 技术进步,特别是数字化、无线化、移动化和物联化,为城市社区的智慧化提供了可能。这些技术的应用不仅提高了社区的运行效率,也增强了居民的便利性和安全性。 3. 智慧社区的核心价值 智慧社区承载了智慧城市的核心价值,通过全面信息化处理,实现对城市各个方面的数字网络化管理、服务与决策功能,从而提升社会服务效率,整合社会服务资源。 4. 多层次、全方位的智慧社区服务 智慧社区通过构建和谐、温情、平安和健康四大社区模块,满足社区居民的多层次需求。这些服务模块包括社区医疗、安全监控、情感沟通和健康监测等。 5. 智慧社区技术框架 智慧社区技术框架强调统一平台的建设,设立数据中心,构建基础网络,并通过分层建设,实现平台能力及应用的可持续成长和扩展。 6. 感知统一平台与服务方案 感知统一平台是智慧社区的关键组成部分,通过统一的RFID身份识别和信息管理,实现社区服务的智能化和便捷化。同,提供社区内外监控、紧急救助服务和便民服务等。 7. 健康社区的构建 健康社区模块专注于为居民提供健康管理服务,通过整合医疗资源和居民接入,实现远程医疗、慢性病管理和紧急救助等功能,推动医疗模式从治疗向预防转变。 8. 平安社区的安全保障 平安社区通过闭路电视监控、防盗报警和紧急求助等技术,保障社区居民的人身和财产安全,实现社区环境的实监控和智能分析。 9. 温情社区的情感沟通 温情社区着重于建立社区居民间的情感联系,通过组织社区活动、一键呼叫服务和互帮互助平台,增强邻里间的交流和互助。 10. 和谐社区的资源整合 和谐社区作为社会资源的整合协调者,通过统一接入和身份识别,实现社区信息和服务的便捷获取,提升居民生活质量,促进社区和谐。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值