C++ 语法基础(三)(一)

简介: C++ 语法基础(三)(一)

指针

知识点


指针的概念


指针保存了一个内存地址,对指针的操作就是对地址的操作。


可以将内存理解为一个“大数组”,指针相当于存储了一个数组下标,它指向下标对应位置的变量。


image.png


指针的声明


用以下代码声明指针。其中,&运算符被称为取地址运算符,它返回变量在内存中的地址。


void指针可以指向任意类型的值。


nt a;
int *ptrToA = &a;
double b;
double *ptrToB = &b;
void *voidPtrToA = &a;
void *voidPtrToB = &b;

指针的声明


用以下代码声明指针。其中,&运算符被称为取地址运算符,它返回变量在内存中的地址。


void指针可以指向任意类型的值。

int a;
int *ptrToA = &a;
double b;
double *ptrToB = &b;
void *voidPtrToA = &a;
void *voidPtrToB = &b;

取地址运算符不能作用于常量或表达式,如 int *ptr = &2; 或者 int *ptr = &(a + b); 。因为他们在内存中并没有固定的地址。


空指针


空指针的值可以用 NULL (C和C++98的风格)或者 nullptr (C++11新标准)表示。他们的值都是0x0,表示指针不指向任何对象。


空指针不可以解引用,对其解引用会出现运行时错误(Runtime Error)。


NULL的定义:


//cite from <stddef.h>
#ifndef __cplusplus
#define NULL ((void *)0) // C语言
#else
#define NULL 0 // C++
#endif

可以发现,C语言的NULL保证为 void* 类型,但C++的NULL仅为常量 0。在C++中使用NULL宏可能会导致函数重载错误。相比之下, C++11引入的 nullptr 始终保证其为指针类型。


在C++代码中,建议使用 nullptr。


image.png


知识点


指针赋值


使用指针时,假设 ptr 为一个指针。


ptr 的值为指针本身的值,是一个十六进制地址;

同类型的指针之间可以赋值,如 ptr1 = ptr2,赋值相当于改变了指针指向的对象。

间接访问


为了访问指针指向的值,我们使用 * 符号,这被称为解引用运算符:


*ptr 的值为指针指向的变量的值;

对 *ptr 的修改会作用到原对象上。

注意声明int *ptr = &a; 中的 * 并不是解引用运算符,它是类型声明的一部分。(非常非常非常重要的一句话,让我搞错了好长时间)


void类型


void* 指针可以指向任何类型的值。


void* 类型不可以解引用。


事实上,使用 void* 代表着你放弃了所有类型检查和类型安全性。因此,除非必要,不建议在C++代码中使用 void* 类型。此后我们会学到处理任意类型更强大、安全的工具:模板。


我们看一下以下代码的运行结果,加深一下理解:


int a = 233;
int *ptrToA = &a;
void *voidPtrToA = &a;
cout << *ptrToA << endl; // 233
cout << ptrToA << endl; //一个十六进制数,表示内存中的位置。例如0x7ffd99314e64
*ptrToA = 466;
cout << a << endl; // 466
*voidPtrToA = 699; // Compile Error: ‘void*’ is not a pointer-to-object type
int c = 1, *ptrToC = &c;
ptrToA = ptrToC; // 现在ptrToA指向了变量c
reinterpret_cast


对于一个指向类型 A 的指针,我们可以将其转换成一个指向类型 B 的指针。此时,指针指向的位置没有变,只是对于内存中数据的解释方式变了。


转换的方式便是 B *ptr2 = reinterpret_cast<B *> ptr1;。


如以下代码,表示用一个 float 指针解释一个内存中的 int 变量:


int x = 1;

float *fp = reinterpret_cast<float *> &x;

要注意的是转换后的类型,它的有效长度不能比原来的类型更长。比如说 int 类型为 4 byte,double 类型为 8 byte。将一个指向 int 类型的指针转换成指向 double 类型的指针,这在语法上没有问题,但是如果解引用得到的指针,double 多出的 4 byte 的数据是无意义的。



image.png



image.png

image.png


相关文章
|
12天前
|
编译器 C++ 容器
C++语言的基本语法
想掌握一门编程语言,第一步就是需要熟悉基本的环境,然后就是最重要的语法知识。 C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象,方法、即时变量。 对象 - 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。 类 - 类可以定义为描述对象行为/状态的模板/蓝图。 方法 - 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。 即时变量 - 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。 完整关键字
29 2
|
1月前
|
Java 编译器 程序员
C++中的语法知识虚继承和虚基类
**C++中的多继承可能导致命名冲突和数据冗余,尤其在菱形继承中。为解决这一问题,C++引入了虚继承(virtual inheritance),确保派生类只保留虚基类的一份实例,消除二义性。虚继承通过`virtual`关键字指定,允许明确访问特定路径上的成员,如`B::m_a`或`C::m_a`。这样,即使基类在继承链中多次出现,也只有一份成员副本,简化了内存布局并避免冲突。虚继承应在需要时提前在继承声明中指定,影响到从虚基类派生的所有后代类。**
44 7
|
1月前
|
编译器 C++ 开发者
C++一分钟之-属性(attributes)与属性语法
【7月更文挑战第3天】C++的属性(attributes)自C++11起允许附加编译器指令,如`[[nodiscard]]`和`[[maybe_unused]]`,影响优化和警告。注意属性放置、兼容性和适度使用,以确保代码清晰和可移植。示例展示了如何使用属性来提示编译器处理返回值和未使用变量,以及利用编译器扩展进行自动清理。属性是提升代码质量的工具,但应谨慎使用。
49 13
|
2月前
|
编译器 程序员 C++
C++一分钟之-属性(attributed)与属性语法
【6月更文挑战第28天】C++的属性为代码添加元数据,帮助编译器理解意图。C++11引入属性语法`[[attribute]]`,但支持取决于编译器。常见属性如`nodiscard`提示检查返回值,`maybe_unused`防止未使用警告。问题包括兼容性、过度依赖和误用。使用属性时需谨慎,确保团队共识,适时更新以适应C++新特性。通过示例展示了`nodiscard`和`likely/unlikely`的用法,强调正确使用属性能提升代码质量和性能。
52 13
|
2月前
|
编译器 C语言 C++
|
2月前
|
存储 自然语言处理 编译器
【C++语言1】基本语法
【C++语言1】基本语法
|
3月前
|
编译器 C语言 C++
C++的基本特性和语法
C++的基本特性和语法
46 1
|
3月前
|
存储 C++ 容器
【C++从练气到飞升】09---string语法指南(二)
【C++从练气到飞升】09---string语法指南(二)
|
3月前
|
存储 Linux C语言
【C++从练气到飞升】09---string语法指南(一)
【C++从练气到飞升】09---string语法指南(一)