C++指针——数组指针/函数指针
指针对于新手来说是一件非常头疼的事情,特别是二重指针,第一此看的时候一知半解的,在拥有一定的代码量积累之后,终于鼓起勇气再看一遍,其他指针的知识点还好,到了二重指针、函数指针这里就有些难度了,多看了些资料,终于把它攻克了,下面把资料整理出来,以供大家参考:
首先鸣谢以下资料,如果作者有哪些讲得不清楚的可以参考一下下面资料,这篇文章就是根据下面资料整理出来的:
http://www.cnblogs.com/ggjucheng/archive/2011/12/13/2286391.html
http://bbs.51cto.com/thread-1017222-1-1.html
http://www.cnblogs.com/gmh915/archive/2010/06/11/1756067.html
在讲二重指针之前,我觉得有必要了解一下指针的运算:
指针的算术运算
指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的。例如:
例二:
char a[20];
int *ptr=a;
...
...
ptr++;
在上例中,指针ptr的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr的值加上了sizeof(int),在32位程序中,是被加上了4。由于地址是用字节做单位的,故ptr所指向的地址由原来的变量a的地址向高地址方向增加了4个字节。
由于char类型的长度是一个字节,所以,原来ptr是指向数组a的第0号单元开始的四个字节,此时指向了数组a中从第4号单元开始的四个字节。
下面我们来看看各种数据类型在32位和64位下的字节数:
常用数据类型对应字节数
可用如sizeof(char),sizeof(char*)等得出
32位编译器:
char :1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
64位编译器:
char :1个字节
char*(即指针变量): 8个字节
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节
long long: 8个字节
unsigned long: 8个字节
指针的指针和数组指针
此图清晰地解释了二重指针以及指针和数组的关系:
请看以下代码:
#include<iostream>
usingnamespace std;
int main(){
int tab[8] = { 3, 4, 2, 6, 7, 9 };
int tab2[3][4] = { 3, 4, 2, 6, 7, 9 };
cout << *(tab + 3) << endl;//结果为6,指针指向int型,加1移动4个字节,加3移动12个字节
cout << tab[5] << endl;//结果为9,不解释
cout << *(&tab[6]) << endl;//结果为0
cout << *(*(tab2 + 1)) << endl;//*(tab2+1)为指针指向tab2[1][0],因此其结果为7
cout << *(&tab2[0][0] + 1) << endl;//结果为4
cout << *(tab2 + 1) << endl;
cout << &tab2[1][0] << endl;
cout << *(*(tab2 + 1))+1 << endl;
system("pause");
}
结果及理由如上图所示。弄懂这结果是怎样来的你就基本掌握了。
例:
int array[10];
int (*ptr)[10];
ptr=&array;
上例中ptr是一个指针,它的类型是int (*)[10],他指向的类型是int [10],我们用整个数组的首地址来初始化它。在语句ptr=&array中,array代表数组本身。
这句话看起来比较难理解,所以我们可以结合另一个例子来理解:
int *abcde = NULL;
int abcd[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
int abc[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int **pointer2=&abcde;
int(*pointer)[10] = &abc;
cout << "porinter:" << endl;
cout << *(*pointer+1) << endl;
cout << *(*pointer) << endl;
cout << "*porinter,&pointer[0]指针的值" << endl;
cout << *pointer << endl;
cout << &pointer[0] << endl;
cout << "porinter,abcd,pointer指针的值" << endl;
cout << abcd << endl;
cout << pointer2 << endl;
cout << pointer << endl;
cout << "porinter,abcd,pointer长度" << endl;
cout << sizeof(abcd) << endl;
cout << sizeof(pointer2) << endl;
cout << sizeof(pointer) << endl;
system("pause");
输出结果为:
如果让 (*pointer)[10] = & abcd[9]则会报错:(*)[9]类型的值不能指向(*)类型的实体;
所以通过这样的对比,应该可以比较容易地理解什么叫做pointer的指针类型是(*)[10],指向array[10]了吧
指针函数和函数指针:
指针函数和函数指针听起来很费解,但理解起来却并不见得比前面那位难:
指针函数是函数:指针函数本质是函数,它的返回值是指针,所以被称作指针函数。
函数指针是指针:函数指针的本质是指针,它指向函数,通过函数指针可以调用函数。
指针函数 |
函数指针 |
简介 |
|
指针函数本质是函数,它的返回值是指针,所以被称作指针函数 |
函数指针的本质是指针,它指向函数,通过函数指针可以调用函数 |
代码 |
|
void main() { int wk, dy; do { printf("Enter week(1 - 5)day(1 - 7)\n"); scanf("%d%d", &wk, &dy); } while (wk<1 || wk>5 || dy<1 || dy>7); printf("%d\n", *GetDate(wk, dy)); system("pause"); }
int * GetDate(intwk, intdy) { staticint calendar[5][7] = { { 1, 2, 3, 4, 5, 6, 7 }, { 8, 9, 10, 11, 12, 13, 14 }, { 15, 16, 17, 18, 19, 20, 21 }, { 22, 23, 24, 25, 26, 27, 28 }, { 29, 30, 31, -1 } };
return &calendar[wk - 1][dy - 1]; } |
void(*funcp)(); void FileFunc(), EditFunc();
void main() { funcp = FileFunc; (*funcp)(); funcp = EditFunc; (*funcp)(); system("pause"); }
void FileFunc() { printf("FileFunc\n"); }
void EditFunc() { printf("EditFunc\n"); } |
结果 |
|
|
|
解释 |
|
当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用函数,以用于需要指针或地址的表达式中。 |
int (*f) (int x); /* 声明一个函数指针 */ f=func; /* 将func函数的首地址赋给指针f */ 指向函数的指针包含了函数的地址,可以通过它来调用函数。声明格式如下: |
|
|
指针函数问题解决了,但是还是有很多小的问题,我在网上看面试题的时候,遇到了这样一个问题:
参考:(http://www.cnblogs.com/iuices/archive/2011/11/03/2234877.html)
下面几种方式哪种可以实现交换?
void swap1(intp, intq)
{
int temp = NULL;
temp = p;
p = q;
q = temp;
}
void swap2(int *p, int *q)
{
int *temp=NULL;
*temp = *p;
*p = *q;
*q = *temp;
}
void swap3(int *p, int *q)
{
int *temp = NULL;
temp = p;
p = q;
q = temp;
}
void swap4(int *p, int *q)
{
int temp = NULL;
temp = *p;
*p = *q;
*q = temp;
}
void swap5(int &p, int &q)
{
int temp = NULL;
temp = p;
p = q;
q = temp;
}
答案是4和5,代码在附件里有哦。
还有一题是考char str[]和char *str的区别的:
这个函数有什么问题?该如何修改?
1 2 3 4 5 |
|
解析:这个str里存在的地址是函数strA栈里“hello world”的首地址。函数调用完成,栈帧恢复调用strA之前的状态,临时空间被重置,堆栈“回缩”,strA栈帧不再属于应该访问的范围。这段程序可以正确输出结果,但是这种访问方法违背了函数的栈帧机制。
但是只要另外一个函数调用的话,你就会发现,这种方式的不合理及危险性。
如果想获得正确的函数,改成下面这样就可以:
1 2 3 4 5 |
|
首先要搞清楚char *str 和 char str[] :
1 |
|
是分配一个局部数组。局部数组是局部变量,它所对应的是内存中的栈。局部变量的生命周期结束后该变量不存在了。
1 |
|
str[]和*str的区别:
其实区别主要在两个地方http://blog.csdn.net/szchtx/article/details/10396149:
弄清第一点区别首先要明白常量指针和指针常量的区别:
const char *p; 常量指针,指向一块区域,这块区域不可写,只能读。
char * const p; 指针常量,指向一块区域,这块区域可读可写,但是指针的值初始后就不能改,类似于一般常量。
1、
[cpp] view plaincopyprint?
char ss[]="C++";
ss[0]='c'; // 合法
char *p="C++";
p[0]='c'; // 合法但不正确
该段代码在VS2010下编译可以通过,但是运行时程序会停止工作,为什么呢?原因在于p[0]='c'这一语句。该语句试图修改p指向的字符串的首个字符,出现了错误。
原因在于两种方式对字符数组操作的机制不同。使用char *p="C++"语句后,编译器在内存的文字常量区分配一块内存,保存”C++“这一字符串字面值,然后在栈上分配内存保存p,p的内容为"C++"的地址。p[0]='c'试图修改常量”C++“,程序当然就会崩溃了。而char ss[]="C++"语句,定义了一个数组,编译器为其在栈上分配了内存空间,因而可以进行修改操作。
因此,可以总结如下:
(1)char ss[]定义了一个数组,ss可认为是一个常指针,ss不可改变,但ss指向的内容可以发生改变。
(2)char *p定义了一个可变指针,p可以指向其它对象。但对于char *p=”abc“这样的情况,p指向的是常量,故内容不能改变。
2、
如下代码进一步说明char ss[]和char *p的区别:
[cpp] view plaincopyprint?
char *strA()
{
char str[]="Hello";
return str;
}
调用该函数,不一定能够得到正确的结果。因为str定义了一个局部数据,是局部变量,存在于函数strA中的栈帧中。当函数调用完成后,栈帧恢复到函数strA调用前的状态,临时空间被重置,为函数分配的栈空间被收回,str所指向的地址也就不存在了。
将上述代码修改:
char *strA()
{
char *str="Hello";
return str;
}
该函数能够正常运行,因为str指向的字符串字面值被保存在只读的数据段,是全局的,当函数调用完成后,str指向的地址未发生变化。
综上,可以看出使用char []较容易出错,可能出现不确定的结果。C++提供的string类相比之下,要安全的多了。
其实以上解释并不是很易懂,我也不是很懂,其实对C++内存分区不懂得话学习C++是很吃力的,所以附上链接,希望对有需要的朋友有帮助:http://www.cnblogs.com/hanyonglu/archive/2011/04/12/2014212.html
指针的题目有难有易,不过指针这种不是靠做题就能会的,实际灵活运用,才是王道。C++的学习之路远没有结束,哪怕看再多的资料也要躬身学习。如果感觉这些资料对你未来的学习有帮助,就足够了。附上自己整理的代码,希望能对大家有所帮助,再次感谢博客园的大神们~~~
最后知识点梳理,这篇文章介绍了:
1、指针运算;2、指针的指针;3、数组指针;4、指针函数和函数指针的区别;5、指针常量和常量指针的区别;6、char str[] 和char *str的区别;
相关推荐
内容概要:本文详细介绍了基于MATLAB GUI界面和卷积神经网络(CNN)的模糊车牌识别系统。该系统旨在解决现实中车牌因模糊不清导致识别困难的问题。文中阐述了整个流程的关键步骤,包括图像的模糊还原、灰度化、阈值化、边缘检测、孔洞填充、形态学操作、滤波操作、车牌定位、字符分割以及最终的字符识别。通过使用维纳滤波或最小二乘法约束滤波进行模糊还原,再利用CNN的强大特征提取能力完成字符分类。此外,还特别强调了MATLAB GUI界面的设计,使得用户能直观便捷地操作整个系统。 适合人群:对图像处理和深度学习感兴趣的科研人员、高校学生及从事相关领域的工程师。 使用场景及目标:适用于交通管理、智能停车场等领域,用于提升车牌识别的准确性和效率,特别是在面对模糊车牌时的表现。 其他说明:文中提供了部分关键代码片段作为参考,并对实验结果进行了详细的分析,展示了系统在不同环境下的表现情况及其潜在的应用前景。
嵌入式八股文面试题库资料知识宝典-计算机专业试题.zip
嵌入式八股文面试题库资料知识宝典-C and C++ normal interview_3.zip
内容概要:本文深入探讨了一款额定功率为4kW的开关磁阻电机,详细介绍了其性能参数如额定功率、转速、效率、输出转矩和脉动率等。同时,文章还展示了利用RMxprt、Maxwell 2D和3D模型对该电机进行仿真的方法和技术,通过外电路分析进一步研究其电气性能和动态响应特性。最后,文章提供了基于RMxprt模型的MATLAB仿真代码示例,帮助读者理解电机的工作原理及其性能特点。 适合人群:从事电机设计、工业自动化领域的工程师和技术人员,尤其是对开关磁阻电机感兴趣的科研工作者。 使用场景及目标:适用于希望深入了解开关磁阻电机特性和建模技术的研究人员,在新产品开发或现有产品改进时作为参考资料。 其他说明:文中提供的代码示例仅用于演示目的,实际操作时需根据所用软件的具体情况进行适当修改。
少儿编程scratch项目源代码文件案例素材-剑客冲刺.zip
少儿编程scratch项目源代码文件案例素材-几何冲刺 转瞬即逝.zip
内容概要:本文详细介绍了基于PID控制器的四象限直流电机速度驱动控制系统仿真模型及其永磁直流电机(PMDC)转速控制模型。首先阐述了PID控制器的工作原理,即通过对系统误差的比例、积分和微分运算来调整电机的驱动信号,从而实现转速的精确控制。接着讨论了如何利用PID控制器使有刷PMDC电机在四个象限中精确跟踪参考速度,并展示了仿真模型在应对快速负载扰动时的有效性和稳定性。最后,提供了Simulink仿真模型和详细的Word模型说明文档,帮助读者理解和调整PID控制器参数,以达到最佳控制效果。 适合人群:从事电力电子与电机控制领域的研究人员和技术人员,尤其是对四象限直流电机速度驱动控制系统感兴趣的读者。 使用场景及目标:适用于需要深入了解和掌握四象限直流电机速度驱动控制系统设计与实现的研究人员和技术人员。目标是在实际项目中能够运用PID控制器实现电机转速的精确控制,并提高系统的稳定性和抗干扰能力。 其他说明:文中引用了多篇相关领域的权威文献,确保了理论依据的可靠性和实用性。此外,提供的Simulink模型和Word文档有助于读者更好地理解和实践所介绍的内容。
嵌入式八股文面试题库资料知识宝典-2013年海康威视校园招聘嵌入式开发笔试题.zip
少儿编程scratch项目源代码文件案例素材-驾驶通关.zip
小区开放对周边道路通行能力影响的研究.pdf
内容概要:本文探讨了冷链物流车辆路径优化问题,特别是如何通过NSGA-2遗传算法和软硬时间窗策略来实现高效、环保和高客户满意度的路径规划。文中介绍了冷链物流的特点及其重要性,提出了软时间窗概念,允许一定的配送时间弹性,同时考虑碳排放成本,以达到绿色物流的目的。此外,还讨论了如何将客户满意度作为路径优化的重要评价标准之一。最后,通过一段简化的Python代码展示了遗传算法的应用。 适合人群:从事物流管理、冷链物流运营的专业人士,以及对遗传算法和路径优化感兴趣的科研人员和技术开发者。 使用场景及目标:适用于冷链物流企业,旨在优化配送路线,降低运营成本,减少碳排放,提升客户满意度。目标是帮助企业实现绿色、高效的物流配送系统。 其他说明:文中提供的代码仅为示意,实际应用需根据具体情况调整参数设置和模型构建。
少儿编程scratch项目源代码文件案例素材-恐怖矿井.zip
内容概要:本文详细介绍了基于STM32F030的无刷电机控制方案,重点在于高压FOC(磁场定向控制)技术和滑膜无感FOC的应用。该方案实现了过载、过欠压、堵转等多种保护机制,并提供了完整的源码、原理图和PCB设计。文中展示了关键代码片段,如滑膜观测器和电流环处理,以及保护机制的具体实现方法。此外,还提到了方案的移植要点和实际测试效果,确保系统的稳定性和高效性。 适合人群:嵌入式系统开发者、电机控制系统工程师、硬件工程师。 使用场景及目标:适用于需要高性能无刷电机控制的应用场景,如工业自动化设备、无人机、电动工具等。目标是提供一种成熟的、经过验证的无刷电机控制方案,帮助开发者快速实现并优化电机控制性能。 其他说明:提供的资料包括详细的原理图、PCB设计文件、源码及测试视频,方便开发者进行学习和应用。
基于有限体积法Godunov格式的管道泄漏检测模型研究.pdf
嵌入式八股文面试题库资料知识宝典-CC++笔试题-深圳有为(2019.2.28)1.zip
少儿编程scratch项目源代码文件案例素材-几何冲刺 V1.5.zip
Android系统开发_Linux内核配置_USB-HID设备模拟_通过root权限将Android设备转换为全功能USB键盘的项目实现_该项目需要内核支持configFS文件系统
C# WPF - LiveCharts Project
少儿编程scratch项目源代码文件案例素材-恐怖叉子 动画.zip
嵌入式八股文面试题库资料知识宝典-嵌⼊式⼯程师⾯试⾼频问题.zip