指针与引用、数组前缀区别
指针与引用、数组前缀区别
Edmend Zhang指针与引用、数组前缀区别
指针和引用是C++中的两个重要概念,它们都用于间接访问变量或对象的值,但它们在使用上有一些关键的区别:
1. 定义与语法
- 指针:指针是一个变量,用于存储另一个变量的内存地址。可以通过
*
操作符访问指针所指向的值。指针的定义方式如下:1
2int a = 10;
int* p = &a; // p 是一个指针,指向 a 的地址 - 引用:引用是一个变量的别名,一旦引用被创建,它就不能再指向其他变量。引用的定义方式如下:
1
2int a = 10;
int& r = a; // r 是 a 的引用,即 r 和 a 指向相同的变量
2. 是否可以重新赋值
- 指针:指针可以在程序的不同地方指向不同的变量或对象,即指针的值(所指向的地址)可以改变。
1
2int b = 20;
p = &b; // 现在 p 指向 b 的地址 - 引用:引用在初始化后无法改变,一旦引用一个变量,就不能再引用其他变量。
1
2int b = 20;
r = b; // 这里不是改变引用,而是将 b 的值赋给 a(因为 r 是 a 的别名)
3. 空引用和空指针
- 指针:指针可以是空指针(
nullptr
或NULL
),表示不指向任何有效地址。1
int* p = nullptr; // p 是空指针
- 引用:引用必须在定义时被初始化,不能是空引用,也不能被赋值为
nullptr
。
4. 内存占用
- 指针:指针本身占用内存,用于存储地址。
- 引用:引用不占用额外的内存空间,实际上是被引用变量的一个别名。
5. 运算符重载
- 指针:可以进行指针运算,例如增加或减少指针的值以访问数组中的不同元素。
1
p++; // 指针 p 移动到下一个元素
- 引用:引用不能进行算术运算,因为它们只是变量的别名,而不是实际的内存地址。
6. 常见用途
- 指针:常用于动态内存分配、数组操作、函数指针以及实现复杂的数据结构(如链表、树等)。
- 引用:常用于函数参数传递、返回值优化以及简化操作符重载。
总结来说,指针更灵活但也更容易引发错误(如空指针、野指针等),而引用更安全、易于使用,但缺乏指针的灵活性。
指针与数组前缀区别
1 | cCopy codeint arr[3] = {1, 2, 3}; |
在这个例子中,p++
是允许的,因为指针p
可以指向数组的下一个元素。但arr++
会产生编译错误,因为数组名(数组前缀)是常量,不能改变其指向的地址。
总结来说,指针是一个独立的变量,数组前缀是数组的首地址,虽然两者都可以用来访问数组元素,但它们的使用方式和限制不同。
指针常见问题
理解 p++
的行为
p++
是 后置自增 操作符,它的特点是:
- 先使用当前值:在表达式计算时,使用当前指针
p
的值。 - 再自增:在使用完当前值后,将
p
的值增加 1,使其指向下一个元素。
*p++
的解析与行为
当你编写 *p++
时,整个表达式实际上是按照以下步骤进行的:
- 保存当前指针
p
的值:这时p
还没有发生变化。 - 解引用当前指针
p
的值:即访问p
当前指向的内存地址的值。 - **自增
p
**:在解引用之后,p
自增 1,指向下一个内存地址。
由于 p++
是后置的,所以它会先返回原始地址的值,然后再自增。因此,在 *p++
表达式中,*
操作符使用的是 p
自增前的地址。
举例说明
假设我们有以下代码:
1 | int arr[] = {10, 20, 30}; |
这个代码片段会按以下顺序执行:
p
当前指向arr[0]
,值为10
。*p++
先使用当前p
指向的地址,即arr[0]
,解引用并得到值10
。- 然后
p++
执行,自增p
的值,使p
指向arr[1]
,即值为20
的位置。 - 最后,
value
被赋值为10
,这是p
自增前解引用获得的值。
为什么 value
是之前地址的值?
因为 p++
是后置自增,它保证了在自增之前,p
原本指向的地址会被使用。自增操作是在使用完 p
当前值之后才进行的。因此,*p++
在解引用时使用的是 p
自增之前的地址,这也是为什么 value
得到的是自增前的值。
二级指针
多级指针,指针的级数没有限制
指针作为函数返回值
C++ 指针指向字符串 打印指针输出整个字符串
这是因为C++中的std::cout
对指针有不同的输出行为:
char\*
类型的指针:当cout
遇到一个char*
类型的指针时,它会将这个指针解释为一个指向字符串的指针,并输出指针所指向的整个字符串。- 其他类型的指针:对于非
char*
类型的指针,cout
会输出指针的内存地址。
例如,如果你有一个整数指针
如何输出指针的地址
如果你想要输出char*
指针的地址,而不是它指向的字符串,可以使用类型转换或直接打印指针的地址:
1 | cpp |
或者使用%p
格式化输出:
1 | cpp |
这两种方法都会输出指针str
的内存地址,而不是它指向的字符串内容。
指针常量和指针
指针常量是指向某个地址的指针,并且这个指针不能再指向其他地址。换句话说,指针本身是一个常量,但它指向的值可以改变。
int* const ptr = &a;
位运算
左移 a<<1
右移 a>>1
按位与作用
1.清零
如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相
与,结果为零。
2.取一个数的指定位
比如取数X=10101110的低4位,只需要找一个数Y,令Y的低4位为1,其余位为0,
即Y=0000 1111,然后将X与Y进行按位与算(X&Y=0000 1110)即可得到x的指定位。
3.判断奇偶
只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if((a&1)
== 0)代替if(a%2 == 0)来判断a是不是偶数。
对于负数的右移:因为负数在内存中是以补码形式存在的,所有首先根据负数的原码求出负数的补码(符号位不变,其余位按照原码取反加1),然后保证符号位不变,其余位向右移动到X位,在移动的过程中,高位补1.等移位完成以后,然后保持符号位不变,其余按位取反加1,得到移位后所对应数的原码。即为所求。
取反~是单目运算符 无法进行类似&=的运算
C++ new和malloc区别
https://blog.csdn.net/happyjacob/article/details/104484218
C++ 静态创建和动态创建的区别
在C++中类的对象建立分为两种,一种是静态建立,如A a;另一种是动态建立,如A* p=new A(),Ap=(A)malloc();静态建立一个类对象,是由编译器为对象在栈空间中分配内存,通过直接移动栈顶指针挪出适当的空间,然后在这片内存空间上调用构造函数形成一个栈对象。动态建立类对象,是使用new运算符将对象建立在堆空间中,在栈中只保留了指向该对象的指针。栈是由编译器自动分配释放 ,存放函数的参数值,局部变量的值,对象的引用地址等。其操作方式类似于数据结构中的栈,通常都是被调用时处于存储空间中,调用完毕立即释放。堆中通常保存程序运行时动态创建的对象,C++堆中存放的对象需要由程序员分配释放,它存在程序运行的整个生命期,直到程序结束由OS释放。而在java中通常类的对象都分配在堆中,对象的回收由虚拟机的GC垃圾回收机制决定。
https://blog.csdn.net/error_again/article/details/112601202