1. 一般变量引用
一般变量
1 、引用相当于是变量的别名
2 、对引用取地址 == 对变量取地址
引用相当于变量取别名;并没有为引用开辟内存单元,它们占用同一个存储单元
2. 指针变量引用
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#if 1
int main ( )
{
int a = 1 , b = 10 ;
int * p = & a;
int * & pa = p;
( * pa) ++ ;
cout << "a = " << a << endl;
pa = & b;
( * pa) ++ ;
cout << "b = " << b << endl;
system ( "pause" ) ;
return EXIT_SUCCESS;
}
#endif
3. 变量引用
#include <iostream>
using namespace std;
int main ( )
{
int a = 1 , b = 10 ;
int & d = a;
system ( "pause" ) ;
return 0 ;
}
引用类型的变量
声明的同时必须初始化
引用是从一而终的,不能改变的,以后该引用不能再引用其他变量
不允许对野指针的内容进行赋值,因为可能引发未知错误
4. 如何交换两个字符串
法1 :指针引用类型-- > 传指针引用
法2 :二维指针 -- > 传指针的地址
以上两种方法,其实最终操作:都是操作的字符串的地址,交换二者的地址
法1
void swap1 ( char * & x, char * & y)
{
char * temp;
temp = x;
x = y;
y = temp;
}
法2
void swap2 ( char * * x, char * * y)
{
char * temp;
temp = * x;
* x = * y;
* y = temp;
}
5. 参数引用
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
const float pi = 3.14f ;
float f;
float f1 ( float r)
{
f = r * r* pi;
return f;
}
float & f2 ( float r)
{
f = r * r* pi;
return f;
}
#if 1
int main ( )
{
float f1 ( float = 5 ) ;
float & f2 ( float = 5 ) ;
float a = f1 ( ) ;
float c = f2 ( ) ;
float & d = f2 ( ) ;
cout << f << endl;
d + = 1.0f ;
cout << f << endl;
system ( "pause" ) ;
return 0 ;
}
#endif
6. 参数引用的常见错误
常量类型变量,不能被非常量类型引用引用
-- > const 修饰的变量,不能被非const 的引用引用
常量引用,表明不能通过这个引用去更改被引用的对象
-- > 不能使用常量引用修改被引用变量的值
-- > const 引用了一个变量,那就不能通过这个引用赋值( 这样就会改变原变量,与const 本义冲突)
7. 指针和引用有什么区别
1 、初始化要求不同
引用在创建的同时必须初始化到一个有效的对象
指针在定义的时候可以不初始化,在定义之后可以重新赋值
2 、可修改性不同
引用一旦被初始化为指向一个对象,就不能被改变为另一个对象的引用( 给引用赋值并不改变它和原始对象的绑定关系,只是更改原始对象的值而已)
指针在任何时候都可以改变为指向另一个对象
3 、不存在NULL 引用
引用初始化时,必须指向某个确实存在的对象
指针可以是NULL ,不需要总是指向某些对象,可以把指针指向任意的对象
4 、测试的区别
引用不会指向空值,所以使用引用之前不需要测试它的合法性
指针则需要经常进行测试
5 、应用的区别
如果,一旦指向一个对象后就不改变指向,那么应该使用引用
如果,存在指向NULL ( 不指向任何对象) ,或者在不同时刻指向不同的对象,应该使用指针
8. 为什么传引用比传指针安全
引用:
不存在空引用,并且引用一旦被初始化为指向一个对象,就不能被改变为另一个对象的引用,因此引用很安全
指针:
对指针来说,它可以随时指向别的对象,并且可以不被初始化,或者为NULL ,所以不安全。
const 指针仍然存在空指针,并且有可能产生野指针
9. 指针的声明
一个整型数 An interger
int a;
一个指向整型数的指针 A pointer to an integer
int * a;
一个指向指针的指针,它指向的指针是指向一个整型数的 A pointer to a pointer to an integer
int * * a;
一个有10 个整型数的数组 An array of 10 integers
int a[ 10 ] ;
一个有10 个指针的数组,每个指针是指向一个整型数的 An array of 10 pointers to integers
int * a[ 10 ] ;
一个指向 有10 个整型数的一维数组 的指针 A pointer to an array of 10 integer
int ( * a) [ 10 ] ;
一个指向函数的指针,该函数有一个整型参数并返回一个整型数 A pointer to a function that takes an integer as an argument and returns an integer
int ( * a) ( int ) ;
一个有10 个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数 An array of 10 pointers to functions that take an integer argument and return an integer
int ( * a[ 10 ] ) ( int ) ;
解读复杂指针声明:右左法则
1 、首先找到未定义的标识符,从这个标识符开始读起( 一个声明里面未定义的标识符只会有一个)
2 、左右法则:
先从最里面的圆括号开始看起,然后往右看,再往左看。每当遇到圆括号时就调转阅读方向。
一旦解析完圆括号里面的所有的东西,就跳出圆括号,重复这个过程,直到整个声明解析完毕
如:int ( * temp[ 5 ] ) ( int * p) ;
1 、首先找到那个未定义的标识符,就是temp
2 、temp的外面有一对圆括号
3 、在这个圆括号里面
temp右边是一个[ ] 运算符,说明temp是一个含有5 个元素的数组
temp的左边有一个* ,说明temp数组里的每个元素都是指针类型
要注意这里的* 修饰的不是temp,而是修饰temp[ 5 ] ,
原因是[ ] 运算符优先级比* 高,temp先跟[ ] 结合,因此* 修饰的是temp[ 5 ]
跳出这个圆括号括号,看右边,也是一对圆括号,说明说明temp[ 5 ] 数组的每个元素是函数指针类型,
函数指针所指向的函数具有int * 类型的形参,返回值类型为int
如:int ( * ( * temp) [ 5 ] ) ( int * p) ;
1 、temp被一个圆括号包含,左边又有一个* ,那么temp是一个指针
2 、跳出括号,右边是一个[ ] 运算符号,说明temp是一个指向数组的指针
往左看,左边有一个* 号,说明这个数组的元素是指针
3 、跳出括号,右边又有一个括号,说明这个数组的元素是指向函数的指针
总结:temp是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int * 类型的形参,返回值为int 类型的函数
如:int ( * ( * temp) ( int * p) ) [ 5 ] ;
temp是个函数指针,
这类函数具有int * 类型的形参,返回值是指向数组的,
数组时具有5 个int 元素的数组
10. 用指针赋值
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#if 1
int main ( )
{
char a[ ] = "hello,world" ;
char * ptr = a;
cout << * ( ptr + 4 ) << endl;
cout << ptr[ 4 ] << endl;
cout << a[ 4 ] << endl;
cout << * ( a + 4 ) << endl;
* ( ptr + 4 ) + = 1 ;
cout << a << endl;
system ( "pause" ) ;
return 0 ;
}
#endif
11. 指针加减操作
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
int main ( )
{
int a[ 5 ] = { 1 , 2 , 3 , 4 , 5 } ;
int * ptr = ( int * ) ( & a + 1 ) ;
cout << * ( a + 1 ) << endl;
cout << * ( ptr - 1 ) << endl;
system ( "pause" ) ;
return 0 ;
}
对指针进行加1 , 操作,得到的是下一个元素的地址,而不是原地址值直接加1 。
所以一个类型为t的指针移动,以sizeof ( t) 为移动单位
a与& a地址是一样的,但是意思不一样。
a是数组首地址,也就是a[ 0 ] 的地址 ; a+ 1 是数组下一个元素的地址,即a[ 1 ]
& a是对象( 此处的对象指数组) 的首地址; & a+ 1 是下一个对象的地址,即a[ 5 ]
12. 指针比较
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
int main ( )
{
char str1[ ] = "abc" ;
char str2[ ] = "abc" ;
const char str3[ ] = "abc" ;
const char str4[ ] = "abc" ;
const char * str5 = "abc" ;
const char * str6 = "abc" ;
char * str7 = ( char * ) "abc" ;
char * str8 = ( char * ) "abc" ;
cout << ( str1 == str2) << endl;
cout << ( str3 == str4) << endl;
cout << ( str5 == str6) << endl;
cout << ( str6 == str7) << endl;
cout << ( str7 == str8) << endl;
system ( "pause" ) ;
return 0 ;
}
13.内存访问违规
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
int main ( )
{
char a;
char * str1 = & a;
char * str2 = ( char * ) "AAA" ;
system ( "pause" ) ;
return 0 ;
}
14. 指针的隐式转换
不能对NULL 进行解引用
编译时错误:不允许的隐式类型转换
运行时错误:访问野指针,或者对NULL 进行解引用等
15. 指针常量与常量指针的区别
常量指
const int * ptr;
指向常量的指针,指针可以修改指向,但指针所指向的地址的内容是不可修改的
指针常量
int * const ptr;
它首先是一个常量,然后它才是一个指针
指针是一个常量,不能修改这个指针所指向的地址,一开始初始化指向哪儿,它就只能指向哪儿
就像一个数组的数组名一样,是一个固定的指针
但是注意:这个指针指向的地址里的内容是可以修改的
指针是常量,不可以改变指针的指向( 即 指针本身不可以修改) ,但指针指向的内容可以修改
略
18. this指针的正确叙述
类的非静态成员函数才有this 指针
19. this指针
一个类的成员函数只有一份,类的所有对象共用这个成员函数的函数体
成员函数之所以能把属于此类的各个对象的数据区分开,就在于每次执行类成员函数时,会把当前对象的this 指针( 对象首地址) 传入成员函数,函数体内所有对类数据成员的访问,都会转化为this - > 数据成员的方式
20. 指针数组与数组指针的区别
指针数组:表示它是一个数组,数组中的每一个元素是指针( 每个指针,都在栈上分配了内存)
数组指针:表示它是一个指针,指针指向了一个数组
略
22. 函数指针与指针函数的区别
指针函数
带指针的函数,本质是一个函数,返回的是某一类型的指针
指针函数是返回指针类型的函数
每一个函数,本身都有一个入口地址,该地址相当于一个指针。
比如函数返回一个整型值,实际上也相当于返回一个指针变量的值,
只不过这时的变量是函数本身而已,而整个函数相当于一个“变量”
函数指针
本质是个指针,只不过该指针变量指向函数,可用该指针变量调用函数
函数指针是指向函数地址的指针
23. 数组指针域函数指针的定义
指针数组 int * a[ 10 ] ;
数组指针 int ( * a) [ 10 ] ; int * a= new int [ 10 ] ;
指向函数的指针数组 int ( * a[ 10 ] ) ( int , int ) ;
24. 各种指针的定义
函数指针 void ( * a) ( int , int ) ;
函数返回指针 int * a ( ) ;
const 指针 cont int * p;
指向const 的指针 int * const p;
指向const 的const 指针 const int * const p;
函数指针的使用
函数指针的使用
27. typedef用于函数指针的定义
如:
typedef int ( * PtrToFun) ( int , int ) ;
使用:
int fun ( int x, int y) ;
PtrToFun p= fun;
int ret = p ( 2 , 3 ) ;
28. 什么是“野指针”
“野指针”是指向“垃圾”内存的指针 ( 不是NULL 指针)
“野指针”是很危险的
“野指针”的成因
1 、指针变量没有被初始化。
指针变量在创建的同时应当被初始化,要么将指针设置为NULL ,要么让它指向合法的内存
让指针指向合法的内存后,再通过指针,对指向的内容进行修改,才是安全的
2 、指针被free或者delete 之后,没有置为NULL 。
让人误以为该指针是个合法的指针
29. 野指针的危害
注意:
对没有初始化( 没有指向合法内存) 的指针/ “野指针”指向的内容进行赋值操作是十分危险的
30. 有了malloc/free,为什么还要new/delete
1 、简介:
malloc/ free是C++ / C的标准库函数
new / delete 是C++ 的运算符
它们都可用于申请动态内存和释放内存
2 、对于 非内部数据类型 的对象
1 、对象在创建的同时自动执行构造函数,对象在消亡之前要自动执行析构函数
2 、由于,malloc/ free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/ free
3 、new / delete 能完成上述任务
4 、new 运算符能够完成动态内存分配和初始化工作,delete 运算符运算符能完成清理和释放内存工作
C++ 中,new / delete 元素安抚可以调用类的构造函数和析构函数
31. 指针的初始化
“野指针”必须初始化为NULL
32. 各种内存分配和释放的函数的联系和区别
C语言的标准内存分配函数:malloc、calloc、realloc、free等
malloc与calloc的区别为1 块与n块的区别
1 、malloc的调用形式为( Type* ) malloc ( size) :
在内存的动态存储区中分配一块长度为"size" 字节的连续区域,返回该区域的首地址,此时内存中的值没有初始化,是随机数
2 、calloc的调用形式为( Type* ) calloc ( n, size) :
在内存的动态存储区中分配n快长度为“size”字节的连续区域,返回首地址,此时内存中的值被初始化为0
3 、realloc的调用形式为( Type* ) ( realloc) ( * ptr, size) :
将ptr内存大小增大到size,新增加的内存块没有初始化
4 、free的调用形式为free ( void * ptr) :
释放ptr所指向的一块内存空间
C++ 中,new / delete 元素安抚可以调用类的构造函数和析构函数
33. 动态内存的传递
new / malloc等在堆区为字符串“abcde”申请内存时,
应该是 new ( strlen ( "abcde" ) + 1 ) ,
因为字符串是以'\0' 作为结束符的,应该多分配一个字节的内存方'\0'
函数中不能返回栈内存地址,应该返回堆内存地址
( 因为函数调用完,栈就会被销毁)
34. 动态内存的传递
想在函数内部,为函数体外申请堆区内存,有三种方法
注意:编译器总是为函数的每个参数制作临时的变量,不注意这个问题的话,就只是在操作函数内部的临时变量,而不是操作外部的那个指针
1 、传递二级指针,直接操作外部指针的地址
2 、传递指针的引用,操作这个指针就是操作外部的指针
3 、返回堆内存首地址
堆内存不再使用时,应该把堆内存释放,并把指针赋值为“NULL ”
避免内存泄漏、野指针的产生,是良好的编程习惯
动态内存的 才传递
36. “野指针”用于变量值的互换
“野指针”不能用于变量值的互换
必须对指针进行初始化,才能对指针指向的内容进行赋值,否则会导致程序运行时崩溃
37. 内存的分配方式有几种
静态存储区
在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,例如全局变量
栈区
执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放
堆区
动态内存,在程序运行的时候用malloc或new 申请,程序员自己负责在合适用free或delete 释放内存
38. 什么是句柄
Windows环境中,句柄是用来标识项目的
Windows程序中并不是用物理地址来表示一个内存块、文件、任务或动态装入模块的,
相反,WINDOWSAPI给这些项目分配确定的句柄,并将句柄返回给应用程序,然后通过句柄来进行操作
句柄地址( 稳定) ( 记载着对象在内存中的地址) -- > 对象在内存中的地址( 不稳定) ( 实际对象物理内存地址)
注意:程序每次重新启动,系统不能保证分配给这个程序的句柄还是原来的那个句柄,而且绝大多数情况的确是不一样的
39. 指针与句柄有什么区别
对于Windows句柄的理解及其与一般指针的区别
1 、指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数据,
2 、Windows并不希望一般程序修改其内部数据结构,因为只有不太安全。
所以Windows给每个GlobalAlloc等函数声明的内存区域指定一个句柄,句柄是一种指向指针的指针
句柄和指针都是地址,不同之处在于:
1 、句柄所指的可以是一个很复杂的结构,并且很有可能与系统有关的。
比如说线程的句柄,它所指的就是一个类或者结构,它和系统有很密切的关系。
当一个线程由于不可预料的原因而终止时,系统就可以返回它所占用的资料,比如CPU、内存等。
反过来想可以知道,这个句柄中的某一项是与系统进行交互的。
由于Windows系统是一个多任务的系统,它随时都可能要分配内存、回收内存、重组内存。
2 、指针也可以指向一个复杂的结构,但是通常是由用户定义的,所以必需的工作都要用户完成,特别是在删除的时候。