再议C语言第二节(数组与指针)讲座整理

来源:互联网 发布:gulp js语法检查 编辑:程序博客网 时间:2024/04/29 08:18
首先先区分一下两个容易混淆的定义:
数组指针是指向数组首元素的地址的指针,其本质为指针(这个指针存放的是数组首地址的地址,相当于2级指针,这个指针不可移动); 指针数组是数组元素为指针的数组,其本质为数组。例如:*p[2]是指针数组,实质是一个数组,里面的两个元素都是指针 []的优先级比*的优先级高,p先与[]结合,形成数组p[2],有两个元素的数组,再与*结合,表示此数组是指针类型的,每个数组元素相当于一个指针变量。
指针数组中的每一个元素均为指针,即有诸形如“*ptr_array[i]”的指针。指针数组中的元素亦可以表示为“*(*(ptr_array+i))”。又因为“()”的优先级较“*”高,且“*”是右结合的,因此可以写作**(ptr_array+i)。由于数组元素均为指针,因此ptr_array[i]是指第i+1个元素的指针。
                                              ————以上摘自百度百科
数组和指针的区别
首先对于编译器而言,一个数组是一个地址,一个指针是一个地址的地址。 
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而 不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。一般变量在内存单元中存放的是数据,指针变量在内存单元中存放的是地址。无论何种类型的指针都占4个字节的内存空间。其中void和带有const的指针变量容易出错,void修饰指针代表的是一种不确定类型的指针,任何类型的指针都可以直接赋给它,无需类型转换,但是不能对void指针进行算术操作。当使用带有const的指针时其实有两种意思。一种指的是不能修改指针本身的内容,另一种指的是你不能修改指针指向的内容。先说指向const的指针,它的意思是指针指向的内容是不能被修改的。它有两种写法。
      const int* p; (推荐)
      int const* p;
      第一种可以理解为,p是一个指针,它指向的内容是const int 类型。p本身不用初始化它可以指向任何标示符,但它指向的内容是不能被改变的。
      第二种很容易被理解成是p是一个指向int的const指针(指针本身不能被修改),但这样理解是错误的,它也是表示的是指向const的指针(指针指向的内容是不能被修改的),它跟第一种表达的是一个意思。为了避免混淆推荐大家用第一种。
     再说const指针,它的意思是指针本身的值是不能被修改的。它只有一种写法
      int* const p=一个地址; (因为指针本身的值是不能被修改的所以它必须被初始化)
     这种形式可以被理解为,p是一个指针,这个指针是指向int 的const指针。它指向的值是可以被改变的如*p=3;
     还有一种情况是这个指针本身和它指向的内容都是不能被改变的,请往下看。
      const int* const p=一个地址;
      int const* const p=一个地址;
指向const的指针(指针指向的内容不能被修改)const关健字总是出现在*的左边而const指针(指针本身不能被修改)const关健字总是出现在*的右边,那不用说两个const中间加个*肯定是指针本身和它指向的内容都是不能被改变的。
  这次讲座欢神学长还讲了左值与右值的概念:
在常见的C风格的语言中,有一些细微的地方是容易被忽视的,如表达式:a=b 这样的表达式在大多数编程语言中都是合法的,它是一个简单的赋值表达式,站在编译器的角度它又是怎么样对待与处理的呢?
      我们先温习一下,“地址”和“地址的内容”两个概念。在编译器看来,赋值运算符两边的a和b并不是两个地位平等的代号。这里的等号“=”并不代表a和b的地位平等,这里符号a的含义是a所代表的地址,习惯上我们称之为左值。而符号b的含义是b所代表的地址的内容,习惯上我们称之为右值,确切的说右值就是除了左值以外的值,他们可能是立即数和变量的某些组合。左值在编译时可知,表示存储结果的地方。通常只有允许修改的左值可以合法用作赋值运算符左边的操作数。所以尽管数组的名字表示“地址”,它是“左值”,但是它不是一个可以修改的左值,所以它也就不能用作赋值运算符的左操作数了,同理,用关键字const修饰的变量也是如此。编译器为每个变量分配一个地址(左值),虽然具体什么时候分配这个地址各个语言的编译器可能有所不同,但是变量在运行时会一直保存于这个地址。
      相对应的,存储于变量中的值(它的右值)只有在运行时才可知。如果需要用到变量中存储的值,编译器就发出指令从指定的地址读入变量值并将它存于寄存器中。也就是说,右值是运行时才会需要去用的实际值。于是"a=b;"的背后发生的事情大致就是,编译器会先检查操作数的合法性,如果操作数合法,就生成如下的指令:取b的右值(可能是一个立即数),将其保存至a的左值(一个地址)。在这里,a作为左值出现,但是实际其作用的是存储a的地址,这条语句的作用就是将这个地址存储的值赋值为b所代表的常量。b作为右值出现的时候就代表的是存储b的地址所存储的值,用语句表达就是*(&b)。先取到地址,再对该地址反引用取得这个地址存储的值。