做一个小结,终结值和引用的疑惑。
基础知识
1.bit & byte
所有的数据最终在内存或硬盘都是电信号,表示为0或者1.我们叫0,或者1为1 bit。
8个bit叫做一个 byte,也叫Byte。为了避免bit缩写混淆 。b转制bit .B专指Byte.
一个字节是0xaa, 最大表示255 。 0xf是半个字节,4个bit,最大为15.
2.指针寻址,容量,带宽 。
我们通常说的指针地址,如32位 0xaabbccdd,他可以寻找到内存中的某一个位置,而地址+1的意思。0xaabbccde,是加了一个字节,地址+1,内存是加了8bit=1byte.
硬盘也是如此。我们通常说的硬盘大小也是以字节为单位。40gB.意思就是可以存储40g个字节。如果是ACSII码。也就是40G 个英文字母。
但我们的宽带却一般用的是bit为单位。所以要除8. 10m的意思。就是一秒可以传递 10/8 m个英文字母。
3. c++ 的一种内存 模型。
4.数字在内存的表示。
如int a=10,在内存的表示为: 0x0a 0x00 0x00 0x00,
int 32位。所以4个字节。高位到低位是从右边向左边。记住一个关键点,必须以字节为单位,而不是 每个bit都反过来。是4个字节 ,按字节反过来就是 00 00 00 0a,
1,值
值很好理解,如下图数字 5.
2.指针,引用
指针,在32位系统中,就是32bit。4字节。存储了一个地址,指向了实际存储数据的地方。
主要是看传参的情况。
1.在c++中。
指针有很多情况
Book *mybook; 可修改对象和指针本身。
const Book *mybook. 不可以修改对象数据。
Book const *mybook. 不可以修改指针本身。
指针已经可以满足一切了,但是太灵活,所以c++引入了 引用。
2。 引用
引用就是某一变量(目标)的一个别名,目的就是保证一个非空且指向地址是不可变的。好处,想想看。看到引用符号,就可以保证,他一定不会是null,一定不会因为空指针导致程序奔溃,你如何都不能吧他指向null,最多是把的所指的数据改为null。这就是好处。
但是本质上就是指针,最最重要的特性就是无法取得指针的地址。&会指向指针所指内容。
因为引用存在的意义就是一个目标的别名,来方便一些情况。 所以编译器必须保证不能让你去修改指针本身,但目前的技术和现实下引用的实现是通过指针来实现的。
book &myb=*mybook1;
book &myb2=mybook2;
这里2个引用。当然是必须有一个4字节的空间来存储指针。但是无论你如何操作,都没有办法查看到myb本身的地址。
引用特性:
0.编译器不会让你获得存放指针的地址。导致网上很多文章说引用不占内存。
1.不需要->符号来指向变量。而是直接用.
2.必须初始化
3.不能修改引用本身(也就是不能修改指针本身)
这里例子,一个是指针,一个是引用,但是内存中是一模一样。压入参数的时候,实际都是压入 堆的地址。
void ChangeID1(Book *mybook,int id) { int temp=3; mybook->id=id; cout<<mybook<<endl; cout<<&mybook<<endl; } void ChangeID2(Book &mybook,int id) { int temp=3; mybook.id=id; //cout<<mybook<<endl; cout<<&mybook<<endl; } Book cplus=Book(); ChangeID1(&cplus,19); ChangeID2(cplus,10); changeid1 函数内 内存布局 0x00007fffffffe054: 0x13 0x00 0x00 0x00 : ???? 0x00007fffffffe058: 0xa0 0xe0 0xff 0xff : àÿÿ 0x00007fffffffe05c: 0xff 0x7f 0x00 0x00 : ÿ??? changeid2 函数内,内存布局。 0x00007fffffffe054: 0x0a 0x00 0x00 0x00 : ???? 0x00007fffffffe058: 0xa0 0xe0 0xff 0xff : àÿÿ 0x00007fffffffe05c: 0xff 0x7f 0x00 0x00 : ÿ???
3.更高级语言.c# 和 java 的传参
分析下更高级语言.c# 和 java。
更高级语言,基本都是把对象放入到堆中。不像 c++那样,对象还 可以放入到栈中。值得一提的是,早期的含有指针的语言object-c ,就非常明智的 限定了对象必须到堆中。为了更统一的编程风格,牺牲了栈性能,鼓掌。
c#下,典型的例子。2个对象赋值,会发生什么?
static void ChangeID(Book book)
{
book.id=4;
Book otherbook = new Book();
book = otherbook;
}
测试发现是,book的对象地址和otherbook的地址变成一样了。
java ,没有直接内存查看工具。所以只是打印了结果。现象表示和c#应该是一样。传递指针,函数内部拥有指针副本。 对象赋值,本质上是指针副本,复制指针而已。
这样看来,和c++的这个例子基本相似
void overfristbook(Book *mybook1,Book *mybook2)
{
int temp=1;
mybook1=mybook2;
}
和c++的引用反而根本不想。
所以。。。很郁闷。引用这个概念,java 和c#是一致的。但是他们和c++完全不一样。
java 和c#所说的引用,完全就是c++的指针,而不是引用。因为java 和c#可以更换引用类型所指的对象。而c++引用是不能变更对象的。
总结一下
c++:
指针:就是指针,指向一块内存。 指针赋值给另一个指针:指针复制给了后者也就是所谓的浅copy.
引用:某个对象的别名,发明它的意义是获得一个不 为空的对象。但在编译器层面还是由指针来实现,但无法获得引用的存储地址(虽然有)。只能获得指向地址。 、引用赋值给另外一个引用:后者指向的数据被复盖,也就是所谓的深拷贝。(是不是完全深拷贝,应该不是)
c#, java
指针:没有这个概念
引用:一个概念,但行为就像c++的指针,反而不像c++的引用。 为什么,看引用间的赋值。
引用赋值给另外一个引用:指针复制给了后者,也就是所谓的浅copy. 而c++的引用不是存在更改指针的情况。