标签:发送 编号 正是 idt int win32 函数 size 怎么
C++ 中内存单元内容与地址
内存由很多内存单元组成,这些内存单元用于存放各种类型的数据。
计算机对内存的每个内存单元都进行了编号,这个编号就称为内存地址,地址决定了内存单元在内存中的位置。
记住这些内存单元地址不方便,于是C++语言的编译器让我们通过名字来访问这些内存位置。
举例
如果使用地址那我们编程会很不方便,使用变量名代替地址
指针的定义和间接访问操作
指针定义的基本形式:指针就是一个变量,其符合变量定义的基本形式,它存储的是值的地址。对类型T,T*是“到T的指针”类型,一个类型为T*的变量能保存一个类型T的对象的地址。
如
int a = 112; float c = 3.14; int* d = &a; float* e = &c; // 指针变量保存的是地址,使用取地址符&
通过一个指针访问它所指向地址的过程称为间接访问(indirection)或者引用指针(dereferencing the point)
这个用于执行间接访问的操作符是单目操作符*
cout << (*d) << endl; cout << (*e) << endl;
一个变量有三重要信息:
指针变量也是变量,其特殊之处在于所存的信息是地址和类型
指针变量是一个专门用来记录变量的地址的变量,通过指针变量可以间接访问另一个变量的值
C++的原生指针
数组与指针
数组名对应一个地址空间
int main() { char aStr[] = {"helloworld"}; char aStr1[] = "helloworld" ; // 同aStr1 char* pStr = (char*)"helloworld"; pStr = aStr; // 指针变量的值允许改变 // aStr = pStr; // 数组变量的值不允许改变 aStr[0] = ‘H‘; // aStr[index]的值可以改变 }
pStr[index]的值可变不可变,取决于所指区间的存储区域是否可变。
左值与右值
概念:
一般说法,编译器为其单独分配了一块存储空间,可以取其地址的,左值可以放在赋值运算符左边
右值指的是数据本身,不能取到其自身地址,右值只能赋值运算右边。
左值最常见的情况如函数和数据成员的名字
右值是没有标识符、不可以取地址的表达式,一般也称之为“临时对象”。
比如:a=b+c
&a是允许的操作,而&(b+c)不能通过编译,因此a是一个左值,而(b+c)是一个右值。
一般类型指针T*
T泛指任何一种类型
*的两种意义:
指针的数组
array of pointers
一组指针,数组的元素是指针
T* t[]
可看成
T* (t[])
数组的指针
a pointer to an array
指针指向一个数组
T(*T)[]
int main() { // 4字节的最小的整数,-1,0,4字节最大的整数 int c[4] = {0x80000000, 0xFFFFFFF, 0x00000000, 0x7FFFFFFF}; int* a[4]; // 指针的数组 存的是指针 int(*b)[4]; // 数组的指针 b = &c; // b指向数组c for (unsigned int i = 0; i < 4; i++) { a[i] = &(c[i]); // 将每个元素的地址赋值给指针的数组中的元素,即指针 } cout << *(a[0]) << endl; // -2147483648 cout << (*b)[3] << endl; // 2147483647 (*b)取出数组,然后[3]对数组进行访问 }
const与指针
const修饰
int main() { char strH[] = { "helloworld" }; char const* pStr1 = "helloworld"; // 左侧最近是char,修饰char,pStr1指针指向的地址可变,但是该地址下字符值不可变 const char* pStr11 = "helloworld"; // 左侧没有看右侧,等价于pStr1 char* const pStr2 = (char*)"helloworld"; // 左侧最近是*,修饰指针,pStr2一旦指向某个地址,就不能再指向其他地址,该指向就不许变 char const* const pStr3 = "helloworld"; // 指向和所指向的值都不可变 pStr1 = strH; // pStr2 = strH; // pStr2不可修改 // pStr3 = strH; // pStr3不可修改 }
指向指针的指针
int main() { int a = 123; int* b = &a; int** c = &b; }
*操作符具有从右向左结合性 **这个表达式相当于*(*c),必须从里向外逐层求值 *c得到的是c指向的位置,即b **c相当于*b,得到变量a的值
未初始化和非法的指针
int main() { int *a; // a指向哪里?发送什么? *a = 12; }
运气好的话,定位到一个非法地址,程序会出错,从而终止
最坏的情况,定位到一个可以访问的地址,无意修改了它,这样的错误难以捕捉,引发的错误可能与原先用于操作的代码毫不相干,无法跟踪。
指针一定要初始化并被恰当赋值。
Null指针
一个特殊的指针变量,表示不指向任何东西。
int* a = NULL;
NULL指针的概念非常有用,它给了一种方法,来表示特定的指针目前未指向任何东西
注意事项:
对于一个指针,如果已经知道将被初始化为什么地址,那么请赋给它这个地址,否则请把它设为NULL。在对一个指针进行间接引用前,请先判断这个指针的值是否为NULL。
int main() { int a = 123; int* p = NULL; p = &a; if (p != NULL) { cout << *p << endl; }LL p = NULL; // 不用时置为NULL return 0; }
杜绝野指针
指向“垃圾”内存的指针,if等判断对它们不起作用,因为没有置NULL
一般有三种情况:
指针使用注意事项:
&与*操作符
char ch = ‘a‘; char* cP = &ch;
&ch取地址,会将ch的地址取出来,放到一个空间中(不是cp变量),我们不知道该空间地址,不能使用&,不能作为左值,&ch可以取到地址,但是存储这个地址的空间我们得不到,只能作为右值。
char ch = ‘a‘; char* cP = &ch;
int main() { char ch = ‘a‘; // & // &ch = 97; // &ch左值不合法 char* cp = &ch; // &ch右值 // &cp = 97; // &cp左值不合法 char** cpp = &cp; // &cp右值 // * *cp = ‘a‘; // *cp左值取变量ch位置 char ch2 = *cp; // *cp右值取变量ch存储的值 // *cp + 1 = ‘a‘; // *cp + 1 左值不合法的位置 ch2 = *cp + 1; // *cp + 1 右值取到字符做ASCII码+1操作 *(cp + 1) = ‘a‘; // *(cp+1)左值语法上合肥,取ch后面位置 ch2 = *(cp + 1); // *(cp + 1)右值语法上合法,取ch后面位置的值 return 0; }
++与--操作符
关于++++,----等
揭密数组名 https://www.cnblogs.com/fenghuan/p/4775023.html
现在到揭露数组名本质的时候了,先给出三个结论:
(1)数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组;
(2)数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量;
(3)指向数组的指针则是另外一种变量类型(在WIN32平台下,长度为4),仅仅意味着数组的存放地址!
1、数组名指代一种数据结构:数组
现在可以解释为什么第1个程序的这一行:sizeof(str);的输出为10的问题,根据结论1,数组名str的内涵为一种数据结构,即一个长度为10的char型数组,所以sizeof(str)的结果为这个数据结构占据的内存大小:10字节。
再看:
第2行的输出结果为40(整型数组占据的内存空间大小)。
如果C/C++程序可以这样写:
我们就都明白了,intArray定义为int[10]这种数据结构的一个实例,可惜啊,C/C++目前并不支持这种定义方式。
2、数组名可作为指针常量
根据结论2,数组名可以转换为指向其指代实体的指针,所以程序1中的第5行数组名直接赋值给指针,程序2第7行直接将数组名作为指针形参都可成立。
下面的程序成立吗?
读者可以编译之,发现编译出错。原因在于,虽然数组名可以转换为指向其指代实体的指针,但是它只能被看作一个指针常量,不能被修改。
而指针,不管是指向结构体、数组还是基本数据类型的指针,都不包含原始数据结构的内涵,在WIN32平台下,sizeof操作的结果都是4。
顺 便纠正一下许多程序员的另一个误解。许多程序员以为sizeof是一个函数,而实际上,它是一个操作符,不过其使用方式看起来的确太像一个函数了。语句 sizeof(int)就可以说明sizeof的确不是一个函数,因为函数接纳形参(一个变量),世界上没有一个C/C++函数接纳一个数据类型(如 int)为"形参"。
3、数组名可能失去其数据结构内涵
到这里似乎数组名魔幻问题已经宣告圆满解决,但是平静的湖面上却再次掀起波浪。请看下面一段程序:
#include <iostream> using std::cout; using std::endl; void arrayTest(char str[]) { cout << "sizeof(str):" << sizeof(str) << endl; } int main() { char str1[10] = "I love U."; arrayTest(str1); system("pause"); return 0; }
程序的输出结果为:
一个可怕的数字,前面已经提到其为指针的长度!
结论1指出,数组名内涵为数组这种数据结构,在arrayTest函数体内,str1是数组名,那为什么sizeof的结果却是指针的长度?这是因为:
(1)数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针;
(2)很遗憾,在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。
所以,数组名作为函数形参时,其全面沦落为一个普通指针!它的贵族身份被剥夺,成了一个地地道道的只拥有4个字节的平民。
以上就是结论4。
指针对于一个类型T,T*就是指向T的指针类型,也即一个T*类型的变量能够保存一个T对象的地址,而类型T是可以加一些限定词的,如const、volatile等等。见下图,所示指针的含义
char c = ‘a‘; char *p = &c; //p里面存放的是c的地址
引用是一个对象的别名,主要用于函数参数和返回值类型,符号X&表示X类型的引用。见下图,所示引用的含义:
int i=1; int &r = i; //此时i=r=1;
若执行r=2;//此时i=r=2;
int *p = &r; //p指向r;
指针和引用的区别
1.首先,引用不可以为空,但指针可以为空。前面也说过了引用是对象的别名,引用为空——对象都不存在,怎么可能有别名!故定义一个引用的时候,必须初始化。因此如果你有一个变量是用于指向另一个对象,但是它可能为空,这时你应该使用指针;如果变量总是指向一个对象,i.e.,你的设计不允许变量为空,这时你应该使用引用。如下图中,如果定义一个引用变量,不初始化的话连编译都通不过(编译时错误)
而声明指针是可以不指向任何对象,也正是因为这个原因,使用指针之前必须做判空操作,而引用就不必。
2.其次,引用不可以改变指向,对一个对象"至死不渝";但是指针可以改变指向,而指向其它对象。说明:虽然引用不可以改变指向,但是可以改变初始化对象的内容。例如就++操作而言,对引用的操作直接反应到所指向的对象,而不是改变指向;而对指针的操作,会使指针指向下一个对象,而不是改变所指对象的内容。
3.再次,引用的大小是所指向的变量的大小,因为引用只是一个别名而已;指针是指针本身的大小,4个字节
4.最后,引用比指针更安全。由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全。对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。const 指针虽然不能改变指向,但仍然存在空指针,并且有可能产生野指针(即多个指针指向一块内存,free掉一个指针之后,别的指针就成了野指针)
总之,用一句话归纳为就是:指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名,引用不改变指向。
标签:发送 编号 正是 idt int win32 函数 size 怎么
原文地址:https://www.cnblogs.com/aidata/p/13023157.html