[c++基础]指针与引用

本文详细介绍了C++中的引用和指针概念,包括声明、初始化、使用场景及区别。并通过具体示例讲解了引用在函数参数传递、动态内存管理等方面的应用,以及指针在函数、数组中的用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.引用

(1) 声明引用时,必须同时对其进行初始化。且不能给引用本身重新赋值,使他指向另一个变量,因此引用总是const的。即没有:const double const& a=1;只有const double& a=1;

(2) 在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率

(3) 引用作为函数返回值:
1. 不能返回局部变量的引用。
2. 不能返回函数内部new分配的内存的引用
3. 引用与一些操作符的重载:对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。

这里写图片描述

(4) 引用和指针的区别:

●指针是一个实体,而引用仅是个别名;
●引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可以“见异思迁”;
●引用没有const,指针有const,const的指针不可变;
●引用不能为空,指针可以为空;
●“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
●指针和引用的自增(++)运算意义不一样。比如:对引用变量int &ref=i重新赋值”ref=j”,并不会改变ref的指向,它仍然指向的是i,而不是j。理所当然,这时对ref进行++操作不会影响到j。而这些换做是指针的话,情况大不相同
●引用是类型安全的,而指针不是 (引用比指针多了类型检查)

2.函数实现两个数的交换

方法一:

void swap1(int *p, int *q) {
    int temp;
    temp= *p;
    *p= *q;
    *q= temp;
}
// 调用:swap1(&a, &b);

方法二:

void swap2(int &p, int &q) {
    int temp;
    temp= p;
    p= q;
    q= temp;
}
// 调用:swap2(a, b);

3.传递动态内存

1.错误示例:

#include<iostream>
void GetMemory(char* p, int num) {
    p = (char*)malloc(sizeof(char)*num);  
    //此时的p只是函数栈中的临时副本
}
int main() {
    char* str = NULL;
    GetMemory(str, 100);
    strcpy(str, "hello");//此时str仍然为NULL
    return 0;
}

正确示例1:使用返回值

#include<iostream>
char* GetMemory(char*p, int num) {
    p = (char*)malloc(sizeof(char)*num);
    return p;
}
int main() {
    char *str = NULL;
    str = GetMemory(str, 100);
    strcpy(str, "hello");
    cout << *str << endl;   //输出首字符, ‘h’
    cout << str << endl;    //输出字符串,“hello”
    cout << &str << endl;   //输出字符串地址
}

正确示例2:使用指针的指针

#include<iostream>
void GetMemory(char**p, int num) {
    *p = (char*)malloc(sizeof(char)*num);
}
int main() {
    char *str=NULL;
    GetMemory(&str, 100);
    strcpy(str, "hello");
    cout << *str << endl;
    cout << str << endl;
    cout << &str << endl;
    return 0;
}

正确示例3:使用指针的引用

#include<iostream>
void GetMemory(char* &p, int num) {
    p = (char*)malloc(sizeof(char)*num);  
}
int main() {
    char* str = NULL;
    GetMemory(str, 100);
    strcpy(str, "hello");
    return 0;
}

2.下面函数有什么问题?

char* strA() {
    char str[] = "hello world";
    return str;
}

解析:
因为这个函数返回的是局部变量的地址,当调用这个函数后,这个局部变量str就释放了,所以返回的结果是不确定的且不安全,随时都有被收回的可能。“hello world”数组自然也不能访问了。
可以考虑改成以下:

char* strA() {
    char* str = "hello world";
    return str;
}

char* str与char str[]的区别

局部数组是局部变量,对应的是内存的栈;指针变量是全局变量,对应的是内存中的全局区域。
故有:
char *c= "hello";
*c= "hello"; //错误

char c[]= "hello";
c[0]= 'e'; //正确

故也可以使用static开辟一段静态存储空间:

const char* strA() {
    static char str[] = "hello world";
    return str;
}

3.下面程序的输出结果?

#include<cstdio>

class A {
    public:
        A() {m_a = 1; m_b = 2;}
        ~A(){}
        void fun() {printf("%d%d\n", m_a, m_b);}
    private:
        int m_a;
        int m_b;
};

class B
{
    public:
        B(){m_c = 3;}
        ~B();
        void fun() {printf("%d\n", m_c);}
    private:
        int m_c;
};

int main() {
    A a;
    B *pb = (B*)(&a);
    pb->fun();
}

解释:
输出结果为1.
强行把A类的内存当做B来处理。pb->fun()调用的是B::fun()来打印m_c。而m_c是B类对象的唯一元素,对应的也就是A类内存中存放的第一个元素,也就是m_a。所以打印了1.

4.下面程序的输出?

#include<iostream>
#include<cstdio>
using namespace std;

class A
{
    public:
        int a;
        A(){a=1;}
        void print() {printf("%d", a);}
};

class B: public A
{
    public:
        int a;
        B() {a=2;}
};

int main() {
    B b;
    b.print();
    printf("%d", b.a);
}

解析:
B类中的a把A类中的a“隐藏”了。在构造B类时,先调用A类的构造函数。所以A类的a是1,B类的a是2.输出是12

4.函数指针与指针函数

指针函数 :指函数的返回值为指针的函数,一般是形如下的函数:
int* func(int x,int y);
函数指针:指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么指向这个函数的函数指针便是指向这个地址。
函数指针主要有两个作用:用作调用函数和做函数的参数
int (*func)(int x);

1.示例程序:

#include<cstdio>

int max(int x, int y) {
    return x>y?x:y;
}

int main() {
    int (*p)(int, int) = &max;  /* 函数指针 */
    int a, b, c, d;
    scanf("%d%d%d", &a, &b, &c);
    d = (*p)((*p)(a, b), c);   /* 调用方式 */
    printf("%d\n", d);
}

2.示例程序:
这里写图片描述

5.数组指针和指针数组

判断方法:看谁优先级高,就是什么

例如:int (*ptr)[] 优先级高的是*ptr 所以是一个指针,一个指向int类型数组指针;
int *ptr[]int *(ptr[]) 因为[]优先级高于*,所以是一个数组,一个放着指针的数组。

1.下面程序的输出?

#include<cstdio>
#include<iostream>
using namespace std;

int main() {
    int v[2][10] = {{1,2,3,4,5,6,7,8,9,10}, {11,12,13,14,15,16,17,18,19,20}};
    int (*a)[10] = v;
    cout << **a << endl;
    cout << **(a+1) << endl;
    cout << *(*a+1) << endl;
    cout << *(a[0]+1) << endl;
    cout << *(a[1]) << endl;
}

解析:
1 11 2 2 11
a是一个指向10个int数组的指针,因此a+1表明指针向后移动1*sizeof(数组大小)

2.下面程序的输出?

#include<cstdio>
#include<iostream>
using namespace std;

int main() {
    int a[]={1,2,3,4,5};
    int *ptr = (int*)(&a+1);
    cout << *(a+1) << " " << *(ptr-1) << endl;
}

解析:
2 5
a是一个5个元素的数组,(&a+1)是5元素数组的下一个数组的首地址,也就是’5’这个元素后面的一个位置,所以*(ptr-1)的值就是5了。

数组名本身就是指针,再加个&,就变成了双指针,这里的双指针就是二维数组,加1,就是数组整体加一行,ptr指向a的第6个元素

3.下面数据声明代表什么?

float(**def)[10]
def是一个二级指针,指向一个一级指针,这个一级指针指向一个float数组
double*(*gh)[10]
gh是一个一级指针,指向一个double*数组
double(*f[10])()
f是一个数组,数组的元素是函数指针,函数形参为空,返回值为double
int*((*b)[10])
b是一个指针,指向一个数组,数组元素是int*
long (*fun)(int)
fun是一个函数指针,函数返回值是long,参数是一个int
int (*(*F)(int, int))(int)
F是一个函数指针,该函数的参数是(int,int),返回值是一个函数指针,这个返回的函数指针参数是一个int,返回值是一个int

this指针

  • this指针本质是一个函数参数,只能在成员函数中使用,全局函数、静态函数都不能使用this。
  • this在成员函数的开始执行前构造,在成员的执行结束后清除。
  • this指针会因编译器不同而有不同的放置位置。可能是堆、栈,也可能是寄存器。
  • this是指向对象的“常指针”,因此无法改变。它是一个指向相应对象的指针。
  • this指针只有在成员函数中才有定义。因此,你获得一个对象后,也不能通过对象使用this指针。

迷途指针,野指针

野指针: 声明一个指针(例如 int *p;),编译器会随机分配一个内存地址给它,但是,此地址不可用(如 cout<<p; 此句会出现运行时错误,见图1,但是编译会通过)。

迷途指针: int *p = new int; delete p; 此时的p指针就是一个迷途指针。为了避免出现迷途指针,记得再加上p= NULL;

malloc,free与new, delete

  • malloc与free是C++/C 语言的标准库函数,new/delete 是C++的运算符。

对于非内部数据类的对象而言,光用maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加malloc/free。

外:
内部数据类型是编译器本来就认识的,不需要用户自己定义 。
非内部数据类型不是编译器本来就认识的,需要用户自己定义才能让编译器识别 。

例如:由enum,union,class、struct等关键字修饰的变量基本数据类型是非内部数据类型;由int,char,double等都是内部数据类型, 用它们定义的变量便是相应的对象.


— From 《程序员面试宝典》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值