zoukankan      html  css  js  c++  java
  • 数组与指针

    谣言终结:数组名并不是一个指针,也不是指针常量,它只是一个符号。但是很多资料为了方便理解把数组名当作指针常量来对待,这就说明一个问题,数组名可以代表一首个元素的地址,但是数组名不能进行算术运算

    程序:

     1 #include<iostream>
     2 using namespace std;
     3 
     4 int main()
     5 {
     6     int a[] = {1,2,3,4};
     7     cout<<a<<endl;        //I
     8     cout<<&a<<endl;     //II
     9     cout<<a+1<<endl;    //III
    10     cout<<&a+1<<endl;   //IV
    11     return 0;
    12 } 

    输出结果:

    引用《C和指针》p141中的理论:

    在C中, 在几乎所有使用数组的表达式中,数组名的值是个指针常量,也就是数组第一个元素的地址。 它的类型取决于数组元素的类型: 如果它们是int类型,那么数组名的类型就是“指向int的常量指针“。

    对于II 和 IV 则是特殊情况,在《C和指针》p142中说到,在以下两中场合下,数组名并不是用指针常量来表示,就是当数组名作为sizeof操作符和单目操作符&的操作数时。 sizeof返回整个数组的长度,而不是指向数组的指针的长度。 取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量的指针。

    所以&a后返回的指针便是指向数组的指针,跟a(一个指向a[0]的指针)在指针的类型上是有区别的,可以这么理解

    a == &a[0]

    &a+1 == a + sizeof(a) 程序中一个指向int的指针地址占4字节,数组中有四个元素 0x30 + 4*4 =0x40

    以下为中兴内部资料:

    数组和指针之区别列表
    以下面代码为例:
    char *p;
    char array[100];
    说明如下:
    1 因为任何类型的指针变量(针对我们用的32位CPU而言)的长度为4,故sizeof(p)为4;但sizeof(array)为100. 这一点是由编译器在编译阶段确定的,即编译器对于sizeof(p)是直接以常数4替换的,对sizeof(array)是直接以100替换的。由此也可知道,用sizeof不但可读性、可扩展性好,而且丝毫不降低代码效率(包括时间和空间上的)。
    2 sizeof(*p)和sizeof(array[0])则相同,都为1
    3 设n为一整数,p[n]等价于*(p + n),array[n]等价于*(array + n),即不管是指针还是数组,都有这两个等效的表示方法
    4 数组形式的形参本质上是指针。后面详述。
    5 array在许多情况下代表第一个元素的地址,即&array[0]。例如
    p = array; /* 等价于 p = &array[0]; */
    fun(array); /* 等价于 p = fun(&array[0]); */
    这个现象让许多人得出“数组即指针”的错误结论。
    6 但数组毕竟是数组,当array取sizeof或地址时,array代表整个数组,这时它不能替换为 &array[0]。例如:
    sizeof(array)为100,而 sizeof(&array[0])等于sizeof(char*);
    &array表示整个数组的地址,它可以赋值给一个指向同类型数组(即100个char变量组成的数组)的指针。而 &(&array[0])表示什么?什么也不是,根本不合语法(编译不过)。


    使用数组名时,前面加不加&符号有何区别?
    void main()
    {
    char a[4];
    void *b;
    void *c;

    b=a;
    c=&a;
    }
    上面的代码中,b=a; c=&a;二者都获得的是数组a的首地址,所以b与c的值相同。

    但从本质上说,a和&a的类型是不同的:在b=a中,a等价于&a[0],所以此处是当作char*(即指向char变量的指针)类型使用的;而&a表示数组a的地址,所以其类型是“指向含4个char元素的数组的指针”。

    看下面的例子(VC下编译的结果),就说明问题
    void main()
    {
    char a[20];
    char *b, (*c)[20]; /* c的类型为“指向含20个char元素的数组的指针” */
    b=&a; /* 编译不过,因类型不匹配 */
    b=a; /* OK */
    c=&a; /* OK */
    c=a; /* 编译不过,因类型不匹配 */
    }
    由于c指向的是含20个char元素的数组,故c+1其实是对c的值加了20,可作如下测试:
    printf(“%u ”, (unsigned int)(c + 1) - (unsigned int)c);
    观察打印为20.

    但下面的代码编译则无任何问题,因为b c为void*,可接收任何类型的指针的赋值
    void main()
    {
    char a[20];
    void *b, *c;
    b=&a;
    b=a;
    c=&a;
    c=a;
    }

    指向数组的指针的应用
    指向数组的指针应用不多,但偶尔也会碰到,下举一例(部门人员碰到的):
    char a[10][20], b[10][20];

    if (..)
    {
    a[i][j]…
    }
    else
    {
    b[i][j]… /* 此分支与上一分支的唯一区别是:前者是对a,后者是对b操作 */
    }
    两分支代码除a b之分外,完全相同。这种代码浪费空间,且不易维护——两处要同步更改。
    借用数组指针,可以这样:
    char (*p)[10][20];/*括号不能省略,否则p变成了指针构成的数组*/
    if (..)
    {
    p = &a;
    }
    else
    {
    p = &b;
    }
    (*p)[i][j]… /* 仅一份代码 */
    或者这样:
    char (*p)[20];/*括号不能省略,否则p变成了指针构成的数组*/
    if (..)
    {
    p = a;/* 将a看成&a[0],就知道其类型与p是匹配的 */
    }
    else
    {
    p = b;
    }
    p[i][j]… /* 仅一份代码 */



    数组形式的形参本质上是指针

    void Fun1(char v[100])
    {
    int n = sizeof(v);

    v++;
    }
    完全应该这样理解:
    void Fun1(char *v)
    {
    int n = sizeof(v);

    v++;
    }
    1 Fun1中v++是合法的。
    2 sizeof(v)的值不是100,而是等于sizeof(char*),即4

    void Fun1(char v[100])与void Fun1(char *v)以及void Fun1(char v[])是完全等价的写法。我们应按void Fun1(char *v)来理解,编译器也都是按这种方式体现的意义来编译的。写代码时,从语法上说可以随便用哪种形式。但是从习惯上来说,如果设计该函数时是打算接受上层函数(即调用者)的数组名作为实参,则通常定义为数组形式(char数组除外);如果设计该函数时是打算接受上层函数(即调用者)的某一个变量的地址作为实参,则通常定义为指针形式。

  • 相关阅读:
    P1071 潜伏者
    P2199 最后的迷宫
    推荐
    T150191 求和
    T150189 养猪
    P1106 删数问题
    NOIP 2018 提高组初赛试题胡谈
    结题报告
    博客迁移公告
    简单实用算法——用队列实现栈
  • 原文地址:https://www.cnblogs.com/raichen/p/4157974.html
Copyright © 2011-2022 走看看