指针的一些问题

简介:

1、c++/c语言中不少地方,数组和指针可以相互替换使用,容易让人产生一种错觉,指针和数组是等价的。

数组要么在静态存储区域创建,如全局数组;要么在栈上创建如函数内的数组。数组的名称对应着(而不是指向)一块内存,它的地址和容量在其生命周期内保持不变,数组的内容可变。

指针可以指向任意类型的内存块,它的特征是可变的,所以常常用指针来操作动态内存,指针比数组灵活,当时容易出错。

char a[] = "hello";
a[0] = 'x';
cout<<a<<endl;
char *p = "world";  //这里的p指向的是常量字符串
p[0] = 'x';  //编译器不能发现该错误
cout<<p;

如上边一段代码,a是容量为6的字符数组,a中的内容是可以改变的,如a[0]='x'。指针p指向的是一个常量字符串“world”(位于静态存储区),常量字符串的内容是不能够被修改的。但是从语法的角度看,编译器并不知道p[0]='x'有什么问题,但是该语句在企图执行时,就会出错。

复制代码
char a[] = "hello";
char *p = "world"; 
cout<<sizeof(a)<<endl;             //6
cout<<sizeof(p)<<endl;             //4
cout<<sizeof(char *)<<endl;     // 4
cout<<sizeof(void *)<<endl;      //4
cout<<sizeof(int *)<<endl;        //4  
cout<<sizeof(short *)<<endl;    //4
复制代码

 

另外指针和数组的容量计算也是有区别的。以上一段代码为例,sizeof(a)的值为6,但是sizeof(p)的值为4,这是因为sizeof(a)可以计算出数组的字节数,但是sizeof(p)得到的是一个指针变量的字节数,相当于sizeof(char *),而不是p所指向的内存容量。c++、c语言是没有办法知道指针所指向的内存容量,除非在申请内存时记住。

 

void test(char p[100])
{
    cout<<sizeof(p)<<endl;//4
}

 

注意:当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。如上边的代码,sizeof(p)的大小为4。

2、指针参数传递内存

复制代码
void GetMemory(char *p)
{
    p = (char *)malloc(100);
}

int main()
{
      char *str = NULL;
      GetMemory(str);
      strcpy(str,"hello");
      printf("%s",str);   //运行出错
      free(str);
}
复制代码

这段代码运行出错,原因出自函数Getmemory中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是_p,编译器使_p=p。如果函数体内的程序修改了_p的内容,就导致了参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。但是在本例中,_p申请了新的内存,只是把_p所指向的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西,每次执行一次GetMemory就会泄露一块内存。因为没有执行free释放内存。

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

如果一定要使用指针参数去申请内存,那么可以使用指向指针的指针,如上边的代码。当然也可以使用函数返回值来传递动态内存,如:

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

但是值得注意的是,我们这里使用返回值返回的是动态分配的堆内存,不是栈内存,如果不小心返回的是栈内存,就会出错,因为在函数结束时,栈内存自动消亡了。

char *GetMemory4()
{
    char p[] ="hello world!"
    return p;   //编译器会发出警告  
}

对上边的程序稍作修改

char *GetMemory5()
{
    char *p ="hello world!"
    return p;   
}

这时候p指向的是字符串常量,位于静态存储区,生命周期恒定不变,那么此时返回的是一个只读的内存块。

 

3、结构体的存储分配

复制代码
struct Align1
{
       int a;
       char b;
       char c;
};
struct Align2
{
       char b;
       int a;
       char c;
};
复制代码

如上边所示两个结构体的数据元素一样,但是位置顺序不同,那么他们占用的内存大小不同。在32位机器中整型4个字节,并且他的起始存储位置必须能够被4整除。所以以上两个结构体在内存中分配如图所示

编译器按照成员列表的顺序一个接着一个的给每个成员分配内存。只有当成员之间满足正确的对齐要求时,成员之间才会出现用于填充的额外内存空间。有些时候,我们有充分的理由决定不对数据结构成员进行重排,减少因边界对齐带来的空间损失。例如,我们可能想把相关的结构成员存储到一起,提高程序的可维护性和可读性。但是,如果不存在这样的理由,结构成员应该根据他们的边界进行重排,减少因为边界对齐而造成的内存损失。当程序创建几百个甚至上千个结构时,减少内存浪费的要求就比程序的可读性更为紧迫了。在这种情况下,在声明中增加注释可以弥补可读性方面的损失。

运行结果:

 

 










本文转自NewPanderKing51CTO博客,原文链接: http://www.cnblogs.com/newpanderking/p/3825417.html,如需转载请自行联系原作者

相关文章
|
6天前
|
云安全 人工智能 安全
AI被攻击怎么办?
阿里云提供 AI 全栈安全能力,其中对网络攻击的主动识别、智能阻断与快速响应构成其核心防线,依托原生安全防护为客户筑牢免疫屏障。
|
16天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
10天前
|
安全 Java Android开发
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
崩溃堆栈全是 a.b.c?Native 错误查不到行号?本文详解 Android 崩溃采集全链路原理,教你如何把“天书”变“说明书”。RUM SDK 已支持一键接入。
621 216
|
存储 人工智能 监控
从代码生成到自主决策:打造一个Coding驱动的“自我编程”Agent
本文介绍了一种基于LLM的“自我编程”Agent系统,通过代码驱动实现复杂逻辑。该Agent以Python为执行引擎,结合Py4j实现Java与Python交互,支持多工具调用、记忆分层与上下文工程,具备感知、认知、表达、自我评估等能力模块,目标是打造可进化的“1.5线”智能助手。
860 61
|
8天前
|
人工智能 移动开发 自然语言处理
2025最新HTML静态网页制作工具推荐:10款免费在线生成器小白也能5分钟上手
晓猛团队精选2025年10款真正免费、无需编程的在线HTML建站工具,涵盖AI生成、拖拽编辑、设计稿转代码等多种类型,均支持浏览器直接使用、快速出图与文件导出,特别适合零基础用户快速搭建个人网站、落地页或企业官网。
1337 157
|
5天前
|
编解码 Linux 数据安全/隐私保护
教程分享免费视频压缩软件,免费视频压缩,视频压缩免费,附压缩方法及学习教程
教程分享免费视频压缩软件,免费视频压缩,视频压缩免费,附压缩方法及学习教程
249 138
|
7天前
|
存储 安全 固态存储
四款WIN PE工具,都可以实现U盘安装教程
Windows PE是基于NT内核的轻量系统,用于系统安装、分区管理及故障修复。本文推荐多款PE制作工具,支持U盘启动,兼容UEFI/Legacy模式,具备备份还原、驱动识别等功能,操作简便,适合新旧电脑维护使用。
551 109