1.一般变量引用
#include<iostream>
#include<string>
using namespace std;
int main()
{
int a = 10;
int b = 20;
int &rn = a; //声明rn为变量a的一个引用
int equal;
rn = b; //将b的值赋值给rn。此时rn其实就是a的一个别名,对rn赋值
//其实就是对a的赋值。因此执行完赋值后,a的值就是b的值,即都是20;
cout << "a = " << a << endl; //a=20
cout << "b = " << b << endl; //b=20
rn = 100; //将100赋值给rn,于是,a的值就变成了100
cout << "a = " << a << endl; //a=100
cout << "b = " << b << endl; //b=20
equal = (&a == &rn) ? 1 : 0; //将a的地址与rn的地址进行比较,如果相等,变量equal的值为1,否则为0
//将rn声明为a的引用,不需要为rn另外开辟内存单元。rn和a占内存中的同一个
//存储单元,它们具有同一地址,所以equal为1.
cout << "equal = " << equal << endl; //equal=1
return 0;
}
a = 20
b = 20a = 100
b = 20
equal = 1
2.指针变量引用
//2.指针变量引用
#include<iostream>
using namespace std;
int main()
{
int a = 1;
int b = 10;
int* p = &a; //声明整型的指针变量p并初始指向a
int* &pa = p; //声明p是一个指针引用pa
(*pa)++; //将pa指向的内容加1。由于pa是p的引用,所以此时实际上是对p指向的内容加1,
// 也就是a加1,结果为a变成了2.
cout << "a = " << a << endl; //a=2
cout << "b = " << b << endl; //b=10
cout << "*p = " << *p << endl; //*p=2
pa = &b; //将pa指向b的地址,由于pa是p的引用,所以此时p也指向b的地址。
(*pa)++; //将pa指向的内容加1,也就是b加1,b就变成了11
cout << "a = " << a << endl; //a=2
cout << "b = " << b << endl; //b=11
cout << "*p = " << *p << endl; //*p=11
return 0;
}
a = 2
b = 10
*p = 2
a = 2
b = 11
*p = 11
3.用参数引用交换两个字符串
//3.用参数引用交换两个字符串
#include<iostream>
#include<string.h>
using namespace std;
void swap(char *&x, char *&y) //swap函数是利用传指针引用实现
{ //字符串交换的。由于swap函数是指针引用类型,
char *temp; //因此传入函数的是实参,而不是形参
temp = x; //如果不用指针引用,那么指针交换只能在swap函数中有效,
x = y; //因为在函数体中,函数会分配两个临时的指针变量分别指向
y = temp; //两个指针参数,对实际的ap和bp没有影响
}
int main()
{
char *ap = "hello";
char *bp = "how are you";
cout << "ap: " << ap << endl;
cout << "bp: " << bp << endl;
swap(ap, bp);
cout << "swap ap,bp" << endl;
cout << "ap: " << ap << endl;
cout << "bp: " << bp << endl;
return 0;
}
ap: hello
bp: how are you
swap ap,bp
ap: how are you
bp: hello
4.指针和引用的区别
(1)初始化要求不同。引用在创建的同时必须初始化,即引用到一个有效的对象;而指针在定义的时候不比初始化,可以在定义后面的任何地方重新赋值。
(2)可修改性不同。引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用;而指针在任何时候都可以改变为指向另一个对象。给引用赋值并不是改变它和原始对象的绑定关系。
(3)不存在NULL引用,引用不能使用指向空值的引用,它必须总是指向某个对象;而指针则可以是NULL,不需要总是指向某些对象,可以把指针指向任意的对象,所以指针更加灵活,也容易出错。
(4)测试需要的区别。由于引用不会指向空值,这意味着引用之前不需要测试它的合法性;而指针则需要经常进行测试。因此使用引用的代码效率比使用指针的要高。
(5)应用的区别。如果是指一旦指向一个对象后就不会改变的指向,那么应该使用引用。如果有存在指向NULL(不指向任何对象)或在不同的时刻指向不同的对象这些可能性,应该使用指针。
5.为什么传引用比传指针安全
由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全。
对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。const指针仍然存在空指针,并且有可能产生野指针。
6.复杂指针的声明
用变量a给出下面的定义
(1)一个整型数:int a
(2) 一个指向整型数的指针:int *a
(3)一个指向指针的指针,它指向的指针是指向一个整型数的:int **a;
(4)一个有10个整型数的数组:int a[10]
(5)一个有10个指针的数组,该指针是指向一个整型数的:int* a[10]
(6)一个指向有10个整型数数组的指针:int(*a)[10]
(7)一个指向函数的指针,该函数有一个整型参数,该参数有一个整型参数并返回一个整型数:int(*a)(int)
(8)一个有10指针的数组,该指针指向一个函数,该指针指向一个函数,该函数有一个整型参数并返回一个整型数:int(*a[10])(int)
7.用指针赋值
//7.用指针赋值
#include<stdio.h>
int main()
{
char a[] = "hello,world"; //声明了一个数组a,并初始化,包括以'\0'结束字符。
char *ptr = a; //声明一个字符指针ptr,并初始化指向数组a首(a的第一个元素地址)
printf("%c\n", *(ptr + 4)); //将ptr加4,在输出地址的内容,也就是指向了输出a[4]的内容
printf("%c\n", ptr[4]); //ptr[4]和*ptr[4]一样,也是a[4]的内容
printf("%c\n", a[4]); //输出a[4]的内容
printf("%c\n", *(a + 4)); //*(a+4)与a[4]一样,也是a[4]的内容
*(ptr + 4) += 1; //使数组a[4]的内容加1,'o'加1后就是'p'
printf("%c\n", *(ptr + 4));
return 0;
}
o
o
o
o
p
8.指针加减操作
//8.指针加减操作
#include<stdio.h>
int main()
{
int a[5] = {1,2,3,4,5 };
int *ptr1 = (int *)(&a + 1); //a与&a的地址是一样的,但是意思不一样,a是数组首地址,也就是a[0]的地址;
printf("%d\n", ptr1); //&a是对象(数组)首地址,a+1是数组下一个元素的地址,即a[1];
//而&a+1是下一个对象的地址,即&a+5*sizeof(int)即a[5]
printf("%d\n", *(ptr1 - 1)); //输出a[4],即5
int *ptr = (int *)(a + 1); //指向数组a中的第二个元素
printf("%d\n", *ptr); //
printf("%d\n", *(a + 1)); //也是指向a中的第二个元素
return 0;
}
//对指针进行加1操作,得到的是下一个元素的地址,而不是原有地址直接加1。所以,一个类型为t的指针的移动,以sizeof(t)为移动单位。
1900248
5
2
2
9.指针比较
//9.指针比较
#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 = "abc";
char* str8 = "abc";
//数组str1,str2,str3,str4都在在栈中分配的,内存中的内容都为“abc”加上一个'\0',但是它们的位置是不同的
//因此前两个输出都是0;
//指针str5,str6,str7,str8也都是在栈中分配的,它们都指向"abc"字符串,注意“abc”存放在数据区,
//所以str5,str6,str7,str8其实指向同一块数据区的内存,所以后三个都输出是1
cout << (str1 == str2) << endl; //0
cout << (str3 == str4) << endl; //0
cout << (str5 == str6) << endl;
cout << (str6 == str7) << endl;
cout << (str7 == str8) << endl;
return 0;
}
0
0
1
1
1
10.指针常量与常量指针的区别
常量指针就是指向常量的指针,它所指向的地址的内容是不可修改的。
指针常量就是指针的常量,它是不可改变地址的指针,但是可对所指向的内容进行修改。
11.const关键字在指针声明时的作用
char * const p1;
char const * p2;
const char *p3;
const char * const p4;
如果const位于 * 号的左侧,则const 就是用来修饰指针所指的向量,即指针指向为常量;如果const 位于 * 号的右侧,const 就是修饰指针本身,即指针本身是常量。
p1 是指针常量,它本身不能被修改,指向的内容可以被修改。
p2和p3是常量指针,它本身可以被修改,指向的内容不可以被修改。
p4本身是常量,并且它指向的内容也不可以被修改。
12.指针数组与数组指针的区别
//12.指针数组与数组指针的区别
//指针数组指一个数组里存放的都是同一个类型的指针,例如
// int * a[10]
//数组a里面存放了10个int* 型变量
//数组指针指一个指向一维或多维数组的指针,例如
//int * b=new int[10]
//指针b指向含有10个整型数据的一维数组。注意,这个时候释放空间一定要delete[],否则会造成内存泄漏。
#include<iostream>
using namespace std;
int main()
{
int x1[4] = { 1,2,3,4 };
int x2[2] = { 5,6 };
int x3[3] = { 7,8,9 };
int *a[2]; //定义指针数组a
int *b = x1; //数组指针b指向了数组x1
int i = 0;
a[0] = x2; //a里的量指针元素分别指向了数组x2和x3
a[1] = x3;
cout << "输出a[0]: ";
for(i=0;i<sizeof(x2)/sizeof(int);i++)
{
cout << a[0][i] << " ";
}
cout << endl;
cout << "输出a[1]: ";
for (i = 0; i < sizeof(x3) / sizeof(int); i++)
{
cout << a[1][i] << " ";
}
cout << endl;
cout << "输出b: ";
for (i = 0; i < sizeof(x1) / sizeof(int); i++)
{
cout << b[i] << " ";
}
cout << endl;
return 0;
}
输出a[0]: 5 6
输出a[1]: 7 8 9
输出b: 1 2 3 4
13.函数指针和指针函数
指针函数是返回指针类型的函数
定义如下:
返回类型标识符 *返回名称 (形式参数表) { 函数体 }
函数指针是指向函数地址的指针。
//13.函数指针和指针函数
#include<iostream>
using namespace std;
int max(int x, int y)
{
return (x > y ? x : y);
}
float *find(float *p, int x) //函数find()被定义为指针函数
{
return p + 1;
}
int main()
{
float score[] = { 10,20,30,40 };
int(*p)(int, int); //指针p被定义为函数指针类型
float *q = find(score + 1, 1); //
int a;
p = max; //指针p被赋为max()函数的地址
a = (*p)(1, 2); //使用指针p就能完成调用max()函数的目的
cout << "a = " << a << endl;
cout << "*q = " << *q << endl;
return 0;
}
a = 2
*q = 30
14.函数指针的使用
//14.函数指针的使用
#include<stdio.h>
int add1(int a1, int b1);
int add2(int a2, int b2);
int main()
{
int numa1 = 1, numb1 = 2;
int numa2 = 2, numb2 = 3;
int(*op[2])(int a, int b); //定义了一个函数指针数组op,它包含两个指针元素
op[0] = add1; //第一个指针元素指向了add1的地址
op[1] = add2; //第二个指针元素指向了add2的地址
printf("%d %d\n", op[0](numa1, numb1), op[1](numa2, numb2)); //使用函数指针调用add1和add2这两个函数
return 0;
}
int add1(int a1, int b1)
{
return a1 + b1;
}
int add2(int a2, int b2)
{
return a2 + b2;
}
3 5
15.什么是野指针
“野指针”不是NULL指针,而是指向 “垃圾” 内存的指针。其成因主要为:指针变量没有被初始化,或指针p被free或者delete之后,没有置为NULL。
16. malloc/free 和 new/delete的区别
malloc与free是C++/C的标准库函数,new/delete是C++的运算符。他们都可用于申请内存和释放内存。
对于非内部数据类型而言,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是元算符,不在编译器控制权限之内,不能把执行构造函数和析构函数的任务强加于malloc/free,(即用malloc/free不会执行构造函数和析构函数)。因此只有使用new/delete函数。
//16.malloc/free和new/delete的区别
#include<iostream>
using namespace std;
class Obj
{
public:
Obj(void)
{
cout << "Initialization" << endl;
}
~Obj()
{
cout << "Destroy" << endl;
}
};
void UseMallocFree(void) //用malloc/free申请和释放堆内存,类的构造函数和析构函数函数不会被调用
{
cout << "in UseMallocFree()....." << endl;
Obj *a = (Obj*)malloc(sizeof(Obj));
free(a);
}
void UseNewDelete(void) //用new/delete申请和释放内存,类的构造函数和析构函数函数都会被调用
{
cout << "in UseNewDelete()....." << endl;
Obj *a = new Obj;
delete a;
}
int main()
{
UseMallocFree(); //类的构造函数和析构函数函数不会被调用
UseNewDelete(); //类的构造函数和析构函数函数都会被调用
return 0;
}
in UseMallocFree().....
in UseNewDelete().....
Initialization
Destroy
17.内存的分配方式有几种
静态存储区、栈、堆的内存分配。
(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,例如全局变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。
(3)从堆上分配,亦称动态内存分配。程序运行的时候用malloc或new申请多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非 常灵活,但问题也最多。