C++之指针使用

一指针和数组对比C++/C程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为两者是等价的。数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

C++之指针使用此处内容已经被作者隐藏,请输入验证码查看内容
验证码:
请关注本站微信公众号,回复“”,获取验证码。在微信里搜索“”或者“”或者微信扫描右侧二维码都可以关注本站微信公众号。

C++指针使用的好坏直接反映了编程人员水平的高低,下面从指针和数组的区别、指针参数是如何传递内存、野指针、malloc/free、new/delete和内存耗尽怎么办方面进行总结。

一 指针和数组对比

  C++/C程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为两者是等价的。数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。

(1)修改内容

char a[] = “hello”;
a[0] = ‘X’;  // 数组可以修改字符串内容

char *p = “world”; // 注意p指向常量字符串
// 编译器不能发现该错误
// 但该语句企图修改常量字符串的内容而导致运行出错
p[0] = ‘X’; 

(2)内容复制和比较

// 数组…
char a[] = "hello";
char b[10];
strcpy(b, a); // 不能用 b = a;
if(strcmp(b, a) == 0) // 不能用 if (b == a)

// 指针…
int len = strlen(a);
char *p = (char *)malloc(sizeof(char)*(len+1));
strcpy(p,a); // 不要用 p = a;
if(strcmp(p, a) == 0) // 不要用 if (p == a)

(3)计算内存容量

char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12字节
cout<< sizeof(p) << endl; // 4字节

注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针

void Func(char a[100])
{
 cout<< sizeof(a) << endl; // 4字节而不是100字节
}

二 指针参数如何传递内存

(1)错误示例

void GetMemory(char *p, int num)
{
 p = (char *)malloc(sizeof(char) * num);
}

void Test(void)
{
 char *str = NULL;
 GetMemory(str, 100); // str 仍然为 NULL
 strcpy(str, "hello"); // 运行错误
}

  编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。

  在上面的例子中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,因为没有用free释放内存。

(2)解决方法1:使用指向指针的指针

void GetMemory2(char **p, int num)
{
 *p = (char *)malloc(sizeof(char) * num);
}

void Test2(void)
{
 char *str = NULL;
 GetMemory2(&str, 100); // 注意参数是 &str,而不是str
 strcpy(str, "hello");
 cout<< str << endl;
 free(str);
}

(3)解决方法2:指针作为函数返回值

char *GetMemory3(int num)
{
 char *p = (char *)malloc(sizeof(char) * num);
 return p;
}

void Test3(void)
{
 char *str = NULL;
 str = GetMemory3(100);
 strcpy(str, "hello");
 cout<< str << endl;
 free(str);
}

注:(1)在上面的例子中,要特别注意在函数调用完后用free释放malloc的内存;

  (2)不要在函数体内返回栈内存的指针

三 野指针

  “野指针”不是NULL指针,是指向“垃圾”内存的指针。

  人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。 

  “野指针”的成因主要有三种:

  (1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。

  (2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。

  (3)指针操作超越了变量的作用域范围。

class A
{
public:
  void Func(void){ cout << “Func of class A” << endl; }
};

void Test(void)
{
 A *p;
 {
  A a;
  p = &a; // 注意 a 的生命期
 }
 p->Func(); // p是“野指针”
}

四 malloc/free/new/delete

  malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。

  对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。

  因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

五 内存耗尽怎么办

  如果在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败。通常有三种方式处理“内存耗尽”问题。

  (1)判断指针是否为NULL,如果是则马上用return语句终止本函数。

  (2)判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。

  (3)用_set_new_hander函数为new设置用户自己定义的异常处理函数。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/120158.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • browserify使用「建议收藏」

    browserify使用「建议收藏」一、简介browserift是javascript编译工具,可以通过预编译方式,将后端的node模块加入javascript中,进而可在前端浏览器上执行。二、安装1、安装nodejs和npmnodejs是node的运行环境,npm是node的包管理工具。它们的安装在此略过(可参数本博客其它文章)。2、browserify安装命令:npminstall-gbrowserify三、使…

    2022年10月29日
  • Conda 替换镜像源方法尽头,再也不用到处搜镜像源地址[通俗易懂]

    Conda 替换镜像源方法尽头,再也不用到处搜镜像源地址[通俗易懂]文章目录conda替换镜像源教程1镜像源添加方法2如何找到你要用的源conda替换镜像源教程由于国内访问conda官网很慢,离线安装又费时费力,因此,替换国内源是一个极佳的办法。但是,目前国内源的替换教程过于老旧,都是2018-2021年的方法,尽管替换镜像源的方法不变,但是网上的资料中,很多镜像源都失效了,没有一个教程能够告诉大家如何去找自己的镜像源并添加进去。本教程出于此目的,保证大家以后添加的镜像源实效性强。(时间2022.3.10)1镜像源添加方法首先是一些常用命令,帮你诊断目前你的co

  • 回发或回调参数无效。在配置中使用 <pages enableEventValidation=”true”/> 或在页面中使用 <%@ Page EnableEventValidation=”true”

    回发或回调参数无效。在配置中使用 <pages enableEventValidation=”true”/> 或在页面中使用 <%@ Page EnableEventValidation=”true”回发或回调参数无效。在配置中使用或在页面中使用启用了事件验证。出于安全目的,此功能验证回发或回调事件的参数是否来源于最初呈现这些事件的服务器控件。如果数据有效并且是预期的,则使用ClientScriptManager.RegisterForEventValidation方法来注册回发或回调数据以进行验证。这两天写程序总是遇到相似的程序在不同页面,出现不一样的结果。以下是

  • idea 2022.01.21 激活码【2022免费激活】2022.02.26「建议收藏」

    (idea 2022.01.21 激活码)好多小伙伴总是说激活码老是失效,太麻烦,关注/收藏全栈君太难教程,2021永久激活的方法等着你。https://javaforall.cn/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~2KLKA7BQFO-eyJsaWNlbnNlSWQiOi…

  • 怎样学习工业PLC

    怎样学习工业PLC可编程控制器是集计算机技术、通讯技术、自动控制技术为一体的工业控制装置。对于初学者来说掌握了plc基本原理,熟悉常用的编程方法,在进行简单系统编程时尚可以运用自如,但对较为复杂的控制系统设计往往力不从心,要想在PLC应用方面得心应手,学习者除了要建立正确的学习方法,深入学习plc编程技巧,最重要的是相关知识的学习。下面从六个方面谈谈我的教学心得。一、多收集程序范例、增加编程经验在PLC的编程方…

    2022年10月18日
  • 北京DNS 列表

    北京DNS 列表转载自 winterhome最终编辑 winterhome北京地区免费DNS:北京ns.bta.net.cn202.96.0.133ns.spt.net.cn202.96.199.133ns.cn.net202.97.16.195202.106.0.20202.106.148.1202.106.196.115北京的DNS是:D

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号