初识c++[含namespace,全缺省,重载,引用]

初识C++

引言

C++ 最初被设计为 C 语言的扩展,旨在提供更多的功能和更好的抽象能力。因此,C++ 继承了 C 语言的高效性和底层控制能力,并添加了面向对象编程(OOP)等特性。面向对象编程是一种程序设计范式,它将数据和与之相关的操作封装在一起,形成对象。这种编程方式有助于提高代码的可维护性、可复用性和扩展性。C++ 是第一种广泛应用 OOP 概念的语言之一,它引入了类、继承、多态等概念,使得程序员能够更轻松地实现复杂的软件系统。++ 继承了 C 语言的系统级编程能力,使其成为编写操作系统、设备驱动程序、嵌入式系统等底层软件的理想选择。其高效性和对硬件的直接控制能力使其在需要性能和控制的应用中得到广泛应用。 C++ 的语言特性和编译器的普及使其能够轻松地移植到不同的操作系统和硬件平台上。这种跨平台性使得 C++ 成为开发跨平台软件的首选语言之一。 所以c++兼容c,可以用c的语法。

namespace的出现:

原因:

在大型软件项目中,往往会有大量的函数、类和变量。为了避免命名冲突,C++ 引入了命名空间的概念。命名空间允许将一组相关的函数、类和变量封装在一个命名空间中,避免了与其他代码中的同名实体发生冲突。
在库和框架开发中,命名空间是一种常见的组织代码的方式。库作者可以将其库中的所有内容放置在一个命名空间中,以避免与用户代码中的命名冲突,并提供清晰的命名空间结构,使用户更容易理解和使用库。
在使用第三方库时,命名空间的使用可以防止库中的函数、类和变量与用户代码中的其他部分发生冲突。这使得在大型项目中整合和使用第三方库变得更加容易。

案例:
在这里插入图片描述
在这里插入图片描述

由于与库里面的函数名冲突,c++引入namespace,实现多个相同的名字的使用;这个是不好避免的,如果项目够大,函数名的命名是非常可能一致,如begin end 来表示开始结束。

namspace的使用规则:

定义命名空间: 使用关键字 namespace 加上命名空间的名称来定义一个命名空间。

namespace my_namespace {
    // 命名空间中的代码
}

作用域:

补充:域访问符

补充 :
1.命名空间+::表示指定访问命名空间里面的内容
2.::+全局的变量表示指定访问全局的那个变量

证明:优先级域
#include <stdio.h>
#include <stdlib.h>

namespace tempvalue {
    int temp = 2;
}

int temp = 1;

int main() {
    int temp = 0;
    printf("%d\n", temp); // 打印 main 函数内部的局部变量 temp
    printf("%d\n", tempvalue::temp); // 打印命名空间 tempvalue 中的变量 temp
    printf("%d\n", ::temp); // 打印全局变量 temp
    return 0;
}

在这里插入图片描述

我们把int temp=0;屏蔽掉

在这里插入图片描述

我们把全局变量屏蔽掉

在这里插入图片描述
在这里插入图片描述

我们把全局变量加回来,并把tempvalue展开,展开的操作using namespace 命名空间的名称

在这里插入图片描述

总结:域优先级

局部域->全局域 ->展开了命名空间域(相当是全局域,如果全局域有同一函数名变量会报错) or 指定访问命名空间域

命名空间的嵌套:

命名空间可以嵌套定义在另一个命名空间中,形成命名空间的层次结构。

namespace outer_namespace {
    namespace inner_namespace {
        // 命名空间中的代码
    }
}

大型项目用,如果想在命名空间使用同一变量名

在这里插入图片描述

本质:模块化编程

命名空间有助于模块化和组织代码。通过将相关的代码组织在命名空间中,可以更清晰地表达代码结构和功能分组,提高了代码的可读性和可维护性。

补充cout endl:

用的iostream 库,练习用时一般是展开using namspace std;C++ 标准库(Standard Library)通常指的是标准模板库(Standard Template Library,STL)和 C 标准库的合集。在 C++ 中,std 是标准库的命名空间,包含了大量的类、函数和对象,用于实现各种基本功能和数据结构。以下是 std 标准库通常包含的内容:容器(Containers)迭代器(Iterators)算法(Algorithms)数值处理(Numerics)输入输出流(Input/Output Streams)字符串处理(String Manipulation)时间和日期(Time and Date)异常处理(Exception Handling)多线程和并发(Concurrency)其他工具和辅助功能(Miscellaneous Utilities)
cout 和 endl,它们都是 std 命名空间中的成员:展开了可以直接用; 一般是练习展开,大型项目不建议

cout 是标准输出流对象,用于将内容输出到标准输出设备(通常是控制台)。
endl 是 C++ 中的换行符,它会在输出流中插入一个换行符并刷新输出缓冲区。

#include <iostream>

using namespace std;

int main() {
    cout << "Hello, world!" << endl; // 直接使用 cout 和 endl
    return 0;
}

不用using namespace std;

如果不使用 using namespace std;,则需要在使用 cout 和 endl 时使用 std:: 前缀来指定命名空间,示例如下:

#include <iostream>

int main() {
    std::cout << "Hello, world!" << std::endl; // 使用 std::cout 和 std::endl
    return 0;
}

这样做的好处是可以避免引入整个 std 命名空间,从而避免潜在的命名冲突。特别是在大型项目中,可能会出现多个命名空间中具有相同名称的标识符,使用限定名称可以明确指定使用哪个命名空间中的标识符,提高了代码的可读性和可维护性。

也可以部分展开

#include <iostream>

int main() {
    using std::cout;
    using std::endl;

    cout << "Hello, world!" << endl; // 直接使用 cout 和 endl
    return 0;
}

在这个例子中,我们使用 using std::cout; 和 using std::endl; 分别引入了 cout 和 endl 到当前的命名空间中。这样,我们就可以直接使用 cout 和 endl 而不需要使用 std:: 前缀

缺省值的出现:

原因:

简化函数调用: 如果一个函数有多个参数,而在大多数情况下某些参数使用相同的值,可以将这些参数设定为有默认值,从而简化函数调用。调用者在不需要显式提供这些参数的值时,可以直接调用函数。

提供函数的默认行为: 对于某些函数,可能存在常见的使用情况,因此可以为参数提供默认值以提供默认行为。这样,如果调用者没有提供特定的参数值,函数将使用默认值执行。

降低函数重载的需求: 使用默认参数可以减少需要编写和维护的函数重载数量。通过为函数参数提供默认值,可以避免编写多个功能类似但参数不同的函数,提高代码的可维护性。

增加代码的可读性和清晰度: 在函数声明中明确指定默认值可以增加代码的可读性。调用者在阅读函数声明时就能知道参数的默认值,而不必查阅文档或函数定义。

向后兼容性: 在修改函数接口时,通过添加默认参数可以保持对现有代码的向后兼容性。这样,旧的函数调用仍然有效,而新的调用可以选择性地提供新参数的值。

用法:

在 C++ 中,函数参数的默认值(缺省值)可以在函数声明或函数定义时指定。指定了默认值的参数在函数调用时是可选的,如果调用者没有提供相应的参数值,则使用参数的默认值

#include <iostream>

// 在函数声明中指定默认值
void printNumber(int number = 10) {
    std::cout << "Number is: " << number << std::endl;
}

int main() {
    // 调用函数时不提供参数值,将使用默认值
    printNumber(); // 输出: Number is: 10

    // 调用函数时提供参数值,则不使用默认值
    printNumber(20); // 输出: Number is: 20

    return 0;
}

全缺省:

如果一个函数的所有参数都有默认值,那么这个函数被称为全缺省函数。调用这种函数时,可以不提供任何参数值,所有参数都会使用默认值。

void fullyDefaultedFunc(int a = 0, int b = 0) {
    // 函数体
}

在调用 fullyDefaultedFunc() 时,可以不提供任何参数值。

半缺省:

如果一个函数的部分参数有默认值,而另一部分没有,默认值的参数在参数列表中排在后面,那么这个函数被称为半缺省函数。调用这种函数时,可以只提供部分参数值,未提供参数值的部分将使用默认值。

例如,在下面的函数中,参数 b 是指定了默认值的参数,而参数 a 是没有指定默认值的参数。这样的函数就是半缺省函数。## 重载的出现

void partiallyDefaultedFunc(int a, int b = 0) {
    // 函数体
}

这意味着在调用 partiallyDefaultedFunc() 时,可以只提供参数 a 的值,而参数 b 将会使用默认值 0。但如果你要提供参数 b 的值,那么也必须提供参数 a 的值。

实际用法:可以实现手动开辟空间

#include <stdlib.h> // 包含 malloc 函数的头文件

// 默认分配一个包含 5 个 int 的内存块
int* allocateIntArray(size_t n=5) {
    int* array = (int*)malloc(n * sizeof(int));
    return array;
}

// 释放分配的内存块
void deallocateIntArray(int* array) {
    free(array);
}

int main() {
    // 使用 allocateIntArray 默认分配一个包含 5 个 int 的内存块
    int* array = allocateIntArray();
    //int* array = allocateIntArray(100);//想开多少手动写
    if (array != NULL) {
        // 使用分配的内存块
        // 在这个示例中,内存块尚未初始化,包含未知的数据
        deallocateIntArray(array); // 释放内存块
    }

    return 0;
}

补充声明定义分离缺省参数哪里给:

在做大型项目时我们通常会把不同的功能实现在不同的文件里面:
自定义的头文件:xxx.h
1.声明外部接口:头文件中声明的函数和类提供了对外部代码的接口。其他文件可以包含这些头文件来使用这些函数和类,而不需要知道其内部实现细节。
2.避免重复定义:通过头文件中的预处理器指令(如#ifndef, #define, #endif),可以防止同一文件被包含多次,避免了重复定义变量、函数等的问题。
3.提供类型和函数的文档:头文件通常包含了对函数和类的注释文档,可以提供给其他开发者使用时的参考文档。
促进代码重用:通过头文件可以将一些通用的功能封装成函数或者类,并在多个文件中重复使用,提高代码的重用性。
4.编译优化:使用头文件可以让编译器在编译过程中优化代码,例如在函数调用时进行参数类型检查和类型转换,提高代码的健壮性和性能。
5.模块化代码:头文件可以包含函数声明、类声明、常量定义等内容,使得代码可以分成多个模块,提高代码的可维护性和可读性。

//stack.h声明表示有结构体stack和stackinit函数的声明
#pragma once
#include<stdio.h>
#include<stdlib.h>

struct Stack
{
	int* a;
	int top;
	int capacity;
};

void StackInit(struct Stack* pst, int defaultCapacity = 4);

//stack.cpp 对于StackInit的具体实现
#include"stack.h"

void StackInit(struct Stack* pst, int defaultCapacity)
{
	pst->a = (int*)malloc(sizeof(int) * defaultCapacity);
	if (pst->a == NULL)
	{
		perror("malloc fail");
		return;
	}

	pst->top = 0;
	pst->capacity = defaultCapacity;
}
//test.cpp 主要是main函数的使用
#include"stack.h"
int main() {
	struct Stack s1;
	struct Stack s2;
	StackInit(&s1);
	StackInit(&s2, 5);
}

现在的问题是在那里给缺省值:是stack.h声明给还是stack.cpp的实现给。

结论:声明给,实现不给;

原因:在源文件生成可执行程序这个过程,会经历编译链接的过程,编译阶段又分为预编译,编译,汇编;预编译是 C 和 C++ 编译过程的第一个阶段,主要负责处理源代码中的预处理指令(Preprocessor directives)。预处理指令以 # 开头,在编译之前对源代码进行一些文本替换和处理,其主要作用包括:
包含头文件:通过 #include 指令,将指定的头文件内容插入到当前文件中。这使得可以在多个源文件中共享函数、类和常量等定义。
条件编译:使用 #if、#ifdef、#ifndef 等条件指令来根据预定义的宏或者编译器选项选择性地编译特定的代码块,从而实现跨平台、调试时输出调试信息等功能。
宏替换:通过 #define 定义宏,可以将一些常量、函数、代码片段等用宏名称替换,从而提高代码的可读性和灵活性。
删除注释:预处理阶段会删除源代码中的注释,包括单行注释 // 和多行注释 /* */。
处理特殊符号:例如 #、## 等符号的特殊处理,它们通常用于宏定义中的特殊操作。
文件包含:使用 #include 指令可以包含其他源文件的内容,使得代码模块化,并且可以将项目分割成更小的部分,提高代码的可维护性。
字符串处理:通过字符串化操作符 #,可以将宏参数转换为字符串字面量。
行号标记:预处理器会在每一行插入一个行号标记,以便在编译过程中进行错误提示和调试。
所以stack.h在test.cpp和stack.cpp里面会展开

//test.cpp 主要是main函数的使用
//void StackInit(struct Stack* pst, int defaultCapacity = 4);test.cpp就有里面的声明
//如果声明不给void StackInit(struct Stack* pst, int defaultCapacity);
int main() {
	struct Stack s1;
	struct Stack s2;
	StackInit(&s1);
	StackInit(&s2, 5);
}

在这里插入图片描述

在这里插入图片描述

声明给了的

在这里插入图片描述

重载:

c不支持同名的函数名,前面的namespace 可以实现在不同的作用域有同名的函数名,如果想同一作用域c++引入重载的概念。

重载定义:

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数
的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

类型不同:

#include <iostream>

// 定义一个函数重载
void print(int num) {
    std::cout << "Integer: " << num << std::endl;
}

// 重载 print 函数,接受 double 类型参数
void print(double num) {
    std::cout << "Double: " << num << std::endl;
}

// 重载 print 函数,接受字符串参数
void print(const char* str) {
    std::cout << "String: " << str << std::endl;
}

int main() {
    print(5);         // 调用第一个 print 函数,输出 "Integer: 5"
    print(3.14);      // 调用第二个 print 函数,输出 "Double: 3.14"
    print("Hello");   // 调用第三个 print 函数,输出 "String: Hello"

    return 0;
}

在上面的示例中,我们定义了一个名为 print 的函数,它被重载了三次:分别接受 int、double 和 const char* 类型的参数。根据调用时传入的参数类型,编译器将选择调用对应版本的 print 函数。这样,我们可以使用相同的函数名来打印不同类型的数据。

个数不同:

#include <iostream>

// 重载的函数版本1,接受一个整数参数
void print(int num) {
    std::cout << "Integer: " << num << std::endl;
}

// 重载的函数版本2,接受两个整数参数
void print(int num1, int num2) {
    std::cout << "Sum of integers: " << num1 + num2 << std::endl;
}

int main() {
    print(5);          // 调用第一个 print 函数,输出 "Integer: 5"
    print(3, 4);       // 调用第二个 print 函数,输出 "Sum of integers: 7"

    return 0;
}

顺序不同

#include <iostream>

// 重载的函数版本1,接受两个整数参数并输出它们的和
void print(double num1, int num2) {
    std::cout << "Print 1: " << num1 << ", " << num2 << std::endl;
}

// 重载的函数版本2,接受两个整数参数并输出它们的差
void print(int num1, double num2) {
    std::cout << "Print 2: " << num1 << ", " << num2 << ", " << num3 << std::endl;
}

int main() {
    print(1.1, 2);       // 调用第一个 print 函数,输出 "Print 1: 1.1, 2"
    print(3, 4.4);    // 调用第二个 print 函数,输出 "Print 2: 3, 4.4"

    return 0;
}

注意:特殊情况

#include<iostream>
using namespace std;
void Func(int a = 10)
{
	cout << "void Func(int a = 10)" << endl;
}
void Func()
{
	cout << "void Func(int a)" << endl;
}
int main() {
	Func();
}

这两个构成重载,但是在无参调用是会有歧义

支持重载原因:补充源代码到可执行程序的过程;

c++,c的源代码到生成可执行程序这个过程的主要步骤:编译+链接编译分3小步
第一步是预处理,它会处理源代码中的预处理指令,例如#include和#define,并生成一个预处理文件。
第二步是编译,它会将预处理文件转换为汇编语言,这是一种更接近机器语言的语言。
第三步是汇编,它会将汇编语言转换为目标文件,这是一种包含机器指令的二进制文件。
第四步是链接,它会将多个目标文件和库文件合并为一个可执行文件,这是可以在操作系统上运行的程序。

在这里插入图片描述

由于vs在编译阶段实行了处理直接观察不好观察
在其他的平台下:

在这里插入图片描述

可以明显看出都是func函数,但是底层名字不同
c++支持重载的本质:C++编译器会在编译阶段对函数名进行修饰,将函数名与参数列表信息结合起来生成一个唯一的标识符。

为什么返回值不同不构成重载:

#include <iostream>

int printNumber() {
    std::cout << "Integer: " << "int" << std::endl;
}

double printNumber() {
    std::cout << "Double: " << "double" << std::endl;
}

int main() {
    printNumber();  
  
    return 0;
}

此时printNumber():调的哪个?编译器也不知道;

引用:

引用的定义:

在编程中,“引用”通常指的是一个别名,即某个变量或对象的另一个名称。引用提供了对变量或对象的间接访问,它们与原始变量或对象共享相同的存储空间。在C++中,引用通常是通过使用**&**符号来声明的。在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。

int main() {
    int x = 5;  // 定义一个整型变量x,并赋值为5
    int &ref = x;  // 定义一个引用ref,并与x绑定
    return 0;
}

在这里插入图片描述

&跟在类型后面才是引用算是在一定的场合下替代了*的用法

引用特性:

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体
    (这三个特性都好理解,不做过多的阐述)

引用场景:

函数参数传递:通过引用传递参数,可以避免函数调用时产生参数的拷贝,提高程序的运行效率。
当我们通过引用传递参数时,函数的形参将直接引用到实参所对应的内存地址,而不是复制实参的值到形参中,这样可以避免产生额外的拷贝,从而提高程序的运行效率。这在需要传递大型数据结构或对象时特别有用。下面是一个通过引用传递参数的简单示例:

#include <iostream>

// 函数通过引用传递参数,交换两个整数的值
void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5;
    int y = 10;
    
    std::cout << "Before swap: x = " << x << ", y = " << y << std::endl;
    
    // 通过引用传递参数,交换x和y的值
    swap(x, y);
    
    std::cout << "After swap: x = " << x << ", y = " << y << std::endl;
    
    return 0;
}

函数返回值为引用与函数返回值不为引用的底层分析:

函数返回值:函数可以返回引用,使得返回值可以直接赋值给另一个变量或对象。

首先是不传引用返回:

#include <iostream>

int count() {
	int n = 0;
	n++;
	return n;
}
int main() {
	int ret = count();
	return 0;
}

我们来观察count()函数的反汇编:

在这里插入图片描述

首先,它执行了一些函数的入口操作,包括保存寄存器 rbp 和 rdi,分配栈空间,设置栈帧指针 rbp,并调用一个用于调试的函数 __CheckForDebuggerJustMyCode。
然后,它读取静态变量 n 的值到寄存器 eax,对 eax 加一,然后把 eax 的值写回 n。这就实现了 n++ 的操作。
接着,它再次读取静态变量 n 的值到寄存器 eax。这就实现了 return n 的操作,但是返回的是 n 的值,而不是 n 的引用。传值返回需要传递一个值。

在这里插入图片描述

小的对象还好占用空间不大,但是大了占用空间就多;为减少占用空间传引用:

对于静态对象一样会生成临时变量(传给寄存器)

在这里插入图片描述

对于引用做函数的返回:

#include <iostream>

int & count() {
	static int n = 0;
	n++;
	return n;
}
int main() {
	int ret = count();
	return 0;
}

在这里插入图片描述

首先,它执行了一些函数的入口操作,包括保存寄存器 rbp 和 rdi,分配栈空间,设置栈帧指针 rbp,并调用一个用于调试的函数 __CheckForDebuggerJustMyCode。
然后,它读取静态变量 n 的值到寄存器 eax,对 eax 加一,然后把 eax 的值写回 n。这就实现了 n++ 的操作。
接着,它用 lea 指令计算 n 的地址,并把它放到寄存器 rax 中。 这就实现了 return n 的操作,但是返回的是 n 的引用,而不是 n 的值。传引用返回传回来的是地址

函数返回值为引用比函数返回值不为引用的减少拷贝的原因:

传引用减少拷贝的原因是,传引用只需要传递一个地址,而传值需要传递一个值。地址的大小通常比值的大小要小,而且地址的传递也比值的传递要快。因此,传引用可以节省时间和空间,提高效率。

函数返回值为引用的危险:

前面说了是返回地址:了解函数栈帧的都知道,调用函数会压栈的
栈帧是一个内存区域,用于存储函数的局部变量,参数,返回地址,以及其他一些信息。每当一个函数被调用时,就会创建一个新的栈帧,并压入栈中。每当一个函数返回时,就会弹出栈顶的栈帧,并销毁它。
对于刚才的函数,如果我们不用static int n = 0;就会出问题

#include <iostream>

int & count() {
    int n = 0;
	n++;
	return n;
}
int main() {
	int ret = count();
	return 0;
}

在这里插入图片描述

我们了解到传引用返回传回来的是地址 即n的地址 从这个反汇编来看n 的地址是相对于栈帧指针 rbp 的一个偏移量,例如 [rbp-20h]。这就说明了 n 是在count栈帧里面的当 count 函数返回时,它会把 n 的地址放到寄存器 rax 中,作为返回值。这样,调用者就可以通过 rax 来访问或修改 n 的值。但是,这并不意味着 n 的位置发生了变化。它仍然在 count 函数的栈帧里面,只是它的地址被返回了而已。 每当一个函数返回时,就会弹出栈顶的栈帧,并销毁它。 那我的rax再去访问那不是变成危险行为

调用count前:

在这里插入图片描述

调用count:

在这里插入图片描述

调用count结束:

在这里插入图片描述

但是rax寄存器的地址还是指向那:我们不进行其他的压栈行为倒是看起来没问题

在这里插入图片描述

函数返回值为引用可以修改返回值和获取返回值:

出了函数作用域,对象还在:

#include <iostream>
using namespace std;
int & count() {
	static int n = 0;
	return n;
}

int main() {
	int & ret1 = count();
	cout << ret1 << endl;
	ret1++;
	cout << ret1 << endl;
	int ret2 = count();
	cout << ret2 << endl;
	return 0;
}

在这里插入图片描述

此时ret1就是n的引用,可以直接访问和修改它

引用场景总结:

1.基本任何场景都可以引用传参
2.谨慎用引用做返回值。出了函数作用域,对象不在了,就不能用引用返回,还在就可以用引用返回

引用的权限问题:

引用过程中,权限可以平移或者缩小

情况1:

在这里插入图片描述
在这里插入图片描述

情况2:隐式类型的转换会产生临时变量,临时变量具有常性

int main() {
	double y = 2.2;
	int x = y;
	const int& z = y;
}

在这里插入图片描述

其中的关键步骤:cvttsd2si eax, mmword ptr [y]:将 xmm0 寄存器中的双精度浮点数值转换为整数,并将结果存储到 eax 寄存器中。这是通过截断(truncation)的方式进行转换,即去除小数部分。mov dword ptr [x], eax:将 eax 寄存器中的整数值存储到变量 x 所在的内存地址中

在这里插入图片描述

情况3:类似情况2的函数调用,产生临时变量,临时变量具有常性

#include <iostream>
using namespace std;
int  count() {
	static int n = 0;
	n++;
	return n;
}

int main() {
	const int & ret1 = count();
	
	return 0;
}

在这里插入图片描述

跟前面一样:它读取静态变量 n 的值到寄存器 eax,对 eax 加一,然后把 eax 的值写回 n。这就实现了 n++ 的操作。接着,它再次读取静态变量 n 的值到寄存器 eax。这就实现了 return n 的操作,但是返回的是 n 的值,而不是 n 的引用。

在这里插入图片描述
在这里插入图片描述

指针和引用:

指针和引用的底层比较:

从汇编的角度,看起来完全一致;前面说了:在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。 在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

在这里插入图片描述

在这里插入图片描述

指针和引用的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用比指针使用起来相对更安全
  • 39
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值