指针资料整理
这些天经常碰到指针的问题,故花了些时间整理下。如发现有错误,欢迎各位拍砖。指针在应用中的各种类型:
(1) int *ip; //(char* cp,float* fp,double* dp) 一级指针变量
(2) int **ip; //(char** cp,float** fp,double** dp) 二级指针变量
(3) const int* ip; //常量指针
(4) int* const ip; //指针常量
(5) const int* const icp; //常量指针常量
(6) int *ip[10]; //指针数组
(7) int (*ip)[10]; //数组指针
(8) int *f(int a); //char* copy(char* s1, char* s2) 指针函数
(9) int (*gp)(int); //函数指针
(10) int (*gp[10])(int); //函数指针的数组
下面针对这11中情况作一一解说
(1) int *ip; //(char* cp,float* fp,double* dp) 一级指针变量
一级指针是我们接触最多,也是最简单的指针。
#include <iostream>
using namespace std;
int main(void)
{
char *p = "hello world";
int i = 0;
while(p[i] != '\0')
cout << p[i++];
cout << '\n';
int *ip, iq;
iq = 5;
ip = &iq;
}
上面程序体现了二个问题:1)指针的增减运算 2)定义指针一个*只能修饰一个指针
指针的增减是以该类型的实体大小为单位的(但对指针作乘除是没有意义的)
对char指针加 1 实际增加了1个字节
对float指针加 1 实际增加了4个字节
对int指针加 1 实际增加了4个字节
定义指针一个*只能修饰一个指针
程序中的 ip是一个指针,而iq是一个整数变量
在很多c/c++程序,指针跟数组进行替换使用,这样以使得我们觉得指针跟数值是等价,但是数组跟指针还是有很大区别的,请看下面的例子。
#include <iostream>
using namespace std;
void fun(char a[100])
{
cout << sizeof(a) << '\n'; //4
}
int main(void)
{
char a[] = "hello world";
a[0] = 'Y';
char *p = "welcome to c++";
// p[0] = 'X'; //编译器不能发现该错误,但是运行时出错
cout << sizeof(a) << '\n'; //12
cout << sizeof(p) << '\n'; //4
fun(a);
}
看了上面的例子之后,我们来说说数组跟指针的区别:
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。
指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。
字符数组 a 的容量是 12 个字符,其内容为 hello world\0。a 的内容可以改变,如 a[0]= ‘Y’。指针 p 指向常量字符串“welcome to c++”,(位于静态存储区,内容为 welcome to c++\0) ,
常量字符串的内容是不可以被修改的。从语法上看,编译器并不觉得语句 p[0]= ‘X’有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误,
所以我们经常把char *p = "welcome to c++" 写成 const char *p = "welcome to c++",这样就能够使得 p[0] = 'X'语句在编译的时候就报错。
sizeof(a)的大小是12(注意别忘了\0) sizeof(p)是一个指针的大小,当把数组作为函数参数进行传递时,数组自动退化为指针。
指针参数如何传递内存的?
#include <iostream>
#include <string.h>
using namespace std;
char* getStr1()
{
char a[] = "hello world";
return a;
}
char* getStr2()
{
char *a = new char[12];
strcpy(a, "hello world");
return a;
}
char* getStr3()
{
char *a = "hello world";
return a;
}
void getStr4(char *p, int num)
{
p = new char[num];
strcpy(p, "hello world");
}
void getStr5(char **p,int num)
{
*p = new char[num];
strcpy(*p, "hello world");
}
int main(void)
{
cout << "getStr1 " << getStr1() << '\n';
char *str2 = NULL;
str2 = getStr2();
cout << "getStr2 " << str2 << '\n';
str2[0] = 'X';
cout << "getStr2 " << str2 << '\n';
delete [] str2;
char *str3 = NULL;
str3 = getStr3();
cout << "getStr3 " << str3 << '\n';
// str3[0] = 'X'; //编译通过,但运行出错
char *str4 = NULL;
int num = 50;
getStr4(str4, num);
cout << "getStr4 " << (str4 == NULL?"NULL": str4) << '\n';
char *str5 = NULL;
getStr5(&str5, num);
cout << "getStr5 " << str5 << '\n';
}
getStr1()输出的是乱码,因为在getStr1()里return的是栈内存,所以当退出函数之后,char a[]也随着被清除了。
getStr2()能够正确运行。
getStr3()虽然能够正确运行,但是不管在什么地方返回都是常量数据
getStr4()运行结束后,str4仍然还是NULL,因为char *p参数在getStr4()函数运行结束后随着也被清除了
getStr5()能够正确运行。
(2) int **ip; //(char** cp,float** fp,double** dp) 二级指针变量
二级指针变量与一级指针变量的关系,跟二维数组与一维数组的关系有些类似,在这就不多说了
(3) const int* ip; //常量指针
(4) int* const ip; //指针常量
(5) const int* const icp; //常量指针常量
这3个有一些相似性,看如下例子。
#include <iostream>
using namespace std;
int main(void)
{
const int a = 10;
int b = 5;
int c = 13;
const int *ip = &a;
int* const cp = &b;
const int* const icp = &c;
*ip = 30; //错误,常量指针不能修改指向的常量,*ip只能做右值
ip = &c; //正确,指针本身能够改变
*cp = 50; //正确,指针常量,指针所指向的值能够修改
cp = &c; //错误,指针常量本身不能够改变
*icp = 50;//错误,常量指针常量不能修改指向的常量
icp = &c; //错误,常量指针常量不能够改变指针本身
}
(6) int *ip[10]; //指针数组
(7) int (*ip)[10]; //数组指针
指针数组:顾名思义,就是说的首先是一个数组吧,然后数组的元素是指针而已
数组指针:指向一个数组的指针
#include <iostream>
using namespace std;
int main()
{
int a = 5, b = 1, c = 10;
int *p1 = &a, *p2 = &b, *p3 = &c;
int *p[3] = {p1, p2, p3}; //指针数组,数组中的每个元素都是一个指针
for(int i = 0; i < 3; i++)
cout << *p[i] << '\n';
int (*ap)[3]; //数组指针
int arr[4][3] = {{1,2},{2,3},{3}};
ap = arr;
for(int i = 0; i < 4; i++)
{
for(int j = 0; j < sizeof(ap[i])/sizeof(int); j++)
cout << ap[i][j] << " ";
cout << '\n';
}
}
(8) int *f(int a); //char* copy(char* s1, char* s2) 指针函数
指针函数是指函数返回值是一个指针类型
如我们在c语言中经常使用strcpy就是这样一个函数
char* strcpy(char* Dest, const char* Src)
{
assert(Dest != NULL &&Src !=NULL);
char* tmp = Dest;
while((*Dest++ = *Src++) != '\0');
return tmp;
}
看到这函数,我们可能会觉得为什么要有一个返回值呢?不是把Src的内容都已经复制到Dest中了么,这个主要是为了完成了链式运算,为了是strcpy函数能够作右值。
(9) int (*gp)(int); //函数指针
(10) int (*gp[10])(int); //函数指针的数组
#include <iostream>
using namespace std;
void fun1(){ cout << "good" << '\n';}
void fun2(){ cout << "better" << '\n';}
void fun3(){ cout << "best" << '\n';}
int main()
{
void (*gp)() = fun1; //函数指针
gp();
void (*gpa[3])();//函数指针数组
gpa[0] = fun1;
gpa[1] = fun2;
gpa[2] = fun3;
for(int i = 0; i < 3; i++)
gpa[i]();
}