指针是C和C++的精华所在,一个数据对象的内存地址称为该数据对象的指针。指针具有不同的类型,可以指向不同的数据存储体,eg:简单变量、数组、数组元素、结构体、甚至函数。
1、 指针与引用的区别
引用:实际就是指两个变量名用了一个相同的变量值,就是说这两个变量有一个相同的内存地址。(1)引用及其绑定的对象的关系:在数值上它们是联动的,改变你也就改变了我,改变我也就改变了你。事实上,访问对象和访问对象的引用,
就是访问同一块内存区域。
(2)从一而终,当你在引用的声明语句里把一个引用绑定到某个对象后,这个引用就永远只能和这个对象绑定在一起了,没法改了。
注:指针不一样。当在指针的声明语句里把指针初始化为指向某个对象后,这个指针在将来如有需要还可以改指别的对象。
(3)声明时必须初始化,既必须指明把引用绑定到什么对象上。
注:指针在声明时可以先不初始化
(4)如果一个函数返回的是一个引用类型,那么该函数可以被当作左值使用。
最后把引用给总结一下:
1、对象和对象的引用在某种意义上是一个东西,访问对象和访问对象的引用其实访问的是同一块内存区。
2、使用对象和使用对象的引用在语法格式上是一样的。
3、引用必须初始化。
4、引用在初始化中被绑定到某个对象上后,将只能永远绑定这个对象。
5、基类类型的引用可以被绑定到该基类的派生类对象,只要基类和派生类满足上文提到的两个条件。这时,该引用其实是派生类对象中的基类子对象的引用。(事实上一个基类类型的引用可以被初始化绑定到派生类对象,只要满足这两个条件:1,指定的基类是可访问的。2,转换是无二义性的。)
6、用传递引用的方式给函数传递一个对象的引用时,只传递了该对象的地址,系统消耗较小。在函数体内访问形参,实际是访问了这个作为实参的象。
7、一个函数如果返回引用,那么函数调用表达式可以作为左值。
二者的区别:(1) 引用的非空性,即在任何情况下,都不能使用指向空值的引用;
不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针要高。
(2)引用在声明时必须初始化;但指针在声明时可以先不初始化; (3) 在使用引用之前不需要测试它的合法性;相反,指针则应该总是被测试,以防止其为空;
(4) 引用从一而终,当你在引用的声明语句里把一个引用绑定到某个对象后,这个引用就永远只能和这个对象绑定在一起了,没法改了; 但指针不一样。当在指针的声明语句里把指针初始化为指向某个对象后,这个指针在将来如有需要还可以改指别的对象。 (5)如果一个函数返回的是一个引用类型,那么该函数可以被当作左值使用。
(6) 总的来说,在以下情况,应该使用指针:
一是你考虑到存在不指向任何对象的可能性(把指针设置为空,即NULL);
二是你需要能够在不同的时刻指向不同的对象;
如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,这是应该使用引用。
2、指针常量和常量指针
常量指针: 指向常量的指针,指针所指向的地址的内容是不可以修改的,但是可以改变指针所指向的地址。 eg:const int a =7; const int*p=&a; const int aa=77; p=&aa;
指针常量:指针的常量,指针所指向的地址不可以修改,但是 可以对它指向的地址的内容进行修改。
eg; int b=4; int * const q; q=&b; b=44;
指向常量的指针常量:指针指向的地址和指向的地址的内容都是常量
eg; const int c=8; const int* const k=&c;
3、函数指针和函数返回指针 【函数指针】
函数指针定义: 函数类型 (*指针变量名)(形参列表);
“函数类型”说明函数的返回类型,由于“()”的优先级高于“*”,所以指针变量名外的括号必不可少,后面的“形参列表”表示指针变量指向的函数所带的参数列表。
例如: int (*f)(int x);
double (*ptr)(double x);
在定义函数指针时请注意:
函数指针和它指向的函数的参数个数和类型都应该是—致的;
函数指针的类型和函数的返回值类型也必须是一致的。
函数指针的赋值 函数名和数组名一样代表了函数代码的首地址,因此在赋值时,直接将函数指针指向函数名就行了。 例如,
int func(int x); /* 声明一个函数 */
int (*f) (int x); /* 声明一个函数指针 */
f=func; /* 将func函数的首地址赋给指针f */
赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,因此经过赋值以后,指针f就指向函数 func(x)的代码的首地址。
通过函数指针调用函数 详见 【指针函数】
一个函数不仅可以带回一个整型数据的值,字符类型值和实型类型的值,还可以带回指针类型的数据,使其指向某个地址单元。
返回指针的函数,一般定义格式为:
类型标识符 *函数名(参数表)
int *f(x,y);
其中x,y是形式参数,f是函数名,调用后返回一个指向整型数据的地址指针。f(x,y)是函数,其值是指针。
如:char *ch();表示的就是一个返回字符型指针的函数。
4、数组指针和指针数组 【数组指针】:指向数组的指针
- int (* ptr)[100] = NULL; //声明指向数组的指针,数组内有100个int元素
- char (* ptr1)[10] = NULL;
访问数组指针指向数组的元素
(* ptr)[i];// 访问第一i个元素
或者 *(*ptr+i)
数组指针的使用
int a[100];
正确的赋值方式:
ptr = &a;//
或者 ptr = (int (*)[100])a;
错误的赋值方式:ptr=a;
因为:数组的名字会被隐式地转换为指向数组第一个元素的指针。而数组指针是指向整个数组的指针,所以ptr=a这种赋 值方式不正确。必须显示地强制类型转换ptr = (int (*)[100])a;或者p = &a,这里&a也是可以到指向数组的指针。
数组指针的举例
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- int main()
- {
- int a[10] = {1,2,3,4,5,6};
- int (*ptr)[10] = NULL;//数组指针
- ptr = (int (*)[10])a;//通过显示类型转换为ptr赋值
-
- printf("%p %p\n",ptr,a);
- printf("%d ",(*ptr)[2]);
- printf("%d\n",*(*ptr+4));
- return 0;
- }
函数的执行结果 0xbfa82dd4 0xbfa82dd4
3 5
可以看出ptr存放的内存地址和a所存放的内存地址是一样的都指向数组的首地址。但是ptr是指向数组的指针,而a被隐式地转换为指向数组第一个元素的指针。
对ptr+1加的长度是整个数组的长度,而a+1加的加的长度是一个数组元素的长度。
ptr是变量可以自加,自减操作。而a是变量,不能执行自加自减少。
【指针数组】:数组元素都是指针类型的数组
char *str[10];//声明一个有10个元素的数组,每个元素都是一个char *型指针。
int *array[10];//声明一个有10个元素的数组,每个元素都是一个指向int *型指针 数组指针的使用
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- int main()
- {
- char *str[5];//定义数组指针
- char s[10];
- int i = 0;
- for (;i < 5;++i)
- {
- printf("输入一个字符串:");
- scanf("%s",s);
- str[i] = (char *)malloc(sizeof(s));//给每个指针分配内存
- strcpy(str[i],s); //复制字符串
- }
- for (i = 0;i < 5;++i)
- {
- printf("str[%d] = %s\n",i,str[i]);
- }
- for (i = 0;i < 5;++i)
- {
- free(str[i]);//释放内存
- }
- return 0;
- }
5、造成野指针的原因
(1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的默认值是随机的,它会乱指 一气。
(2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
(3)指针操作超越了变量的作用范围。这种情况让人防不胜防。6、字符串指针的特别之处
char a='ASDF'; char *p=&a;
cout<< p<
cout<<*p<
原因:这里有一个特殊的处理,虽然这里的p的内容确确实实是一个地址,但是cout操作字符指针的话,它遇到地址,就会直接去寻找这个地址所指向的内容,并把它的空间里的机器数按照字符的规则转化成字符输出,直到遇到“\0”这个操作符才停止。
目的:可以很容易的处理字符串
eg: char a[]="mantou"; a[]在内存中在7个字节,而不是6个,因为在mantou字符串后面还隐藏有一个“\0”
char *p=a; (这里不用&a是因为,a[]是一个数组,数组名a本身就是一个指针常量)
cout<<*p<
注意:
cout<<&p<
*:指针运算符(或称“间接访问”运算符),取其指向的内容
&:取地址运算符
7、字符串指针变量与字符数组的区别 用字符数组和字符指针变量都可实现字符串的存储和运算。 但是两者是有区别的。在使用时应注意以下几个问题:
1. 字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以‘\0’作为串的结束。字符数组是由于若干个数组元素组成的,它可用来存放整个字符串。
2. 对字符数组作初始化赋值,必须采用外部类型或静态类型,如: static char st[]={“C Language”};而对字符串指针变量则无此限制,如: char *ps="C Language";
3. 对字符串指针方式 char *ps="C Language";可以写为: char *ps; ps="C Language";而对数组方式:
static char st[]={"C Language"};
不能写为:
char st[20];st={"C Language"};
而只能对字符数组的各元素逐个赋值。
从以上几点可以看出字符串指针变量与字符数组在使用时的区别,同时也可看出使用指针变量更加方 便。前面说过,当一个指针变量在未取得确定地址前使用是危险的,容易引起错误。但是对指针变量直 接赋值是可以的。因为C系统对指针变量赋值时要给以确定的地址。因此,
char *ps="C Langage";
或者 char *ps;
ps="C Language";都是合法的。
char c[] = "hello world" 是分配一个局部数组
char *c = "hello world" 是分配一个全局数组
局部数组是局部变量,它所对应的是内存中的栈;全剧数组是全局变量,它所对应的是内存中的全局区域,全局区域中的值不能进行修改。
C++提供两种字符串的表示方法:c风格的字符串和标准c++引入的string类类型。建议使用string类。
1. C风格字符串
字符串存储在一个字符数组中,一般通过一个char *类型指针操纵它。然而,系统内部实际上是存储在一个字符串数组中,然后,st指向数组的第一个元素:
Char *st=”hello world”;
常用指针的算术运算来遍历c风格的字符串,每次指针增加1,直到空字符为止:
While(*st++){…}
当包含:#include
//返回长度
Int strlen(const char *);
//比较两字符串是否相等
Int strcmp(const char*,const char *);
//以下两个的使用方式不清楚
//第二个字符拷贝至第一个
Char *strcpy(char *,const char *);
//第二个字符接至第一个后面
Char *strcat(char*,const char *);
C风格字符串的长度可以是0,有两种方式:
Char *pc1=0;
Char *pc=””;
2. 字符串类型
C++标准库对此进行支持。
使用string类,必须包含头文件:
#include
String st(“Hello world”);
//返回长度(不包含空字符)
St.size();
String st2;
//检查是否是空
St2.empty()=true or false;
//复制两个字符串
String st3(st);
St2=st3;//simple
//比较是否相等
If(st2==st3){….}
//两个字符串连接成第三个字符串
String s1(“hello,”);
String s2(“world!”);
String s3=s1+s2;
//或者,直接加到s1
S1+=s2;
//可以把一个c风格的字符串转换成string类对象
Const char *pc=”a try”;
String s1=pc;//ok
//但,反向的转换不能自动执行
Char *str=s1;//error
为实现这种转换必须显示调用名为c_str()
//几乎正确的
char *str=s1.c_str();
但为了防止字符数组被程序直接处理,c-str()返回的一个指向常量数组的指针:const char*,str被定义为非常量指针,所以违例。正确是:
Const char *str=s1.c_str();//ok
String 类型支持通过下标操作符号访问单个字符。
阅读(1445) | 评论(1) | 转发(0) |