C语言总结_4.指针和数组

4.指针和数组

 指针就是指针,指针变量在 32 位系统下,永远占 4 个 byte,其值为某一个内存的地址。指针可以指向任何地方,但是不是任何地方你都能通过这个指针变量访问到。
 数组就是数组,其大小与元素的类型和个数有关。定义数组时必须指定其元素的类型和个数。数组可以存任何类型的数据,但不能存函数。

  • 指针
    在 32 位系统下,不管什么样的指针类型,其大小都为 4byte。

    int *p = NULL;
    这句代码的意思是:定义一个指针变量 p,其指向的内存里面保存的是 int 类型的数据;在定义变量 p 的同时把 p 的值设置为0x00000000,而不是把*p 的值设置为 0x00000000。这个过程叫做初始化,是在编译的时候进行的。
    int *p;
    *p = NULL;
    同样,我们可以在编译器上调试这两行代码。第一行代码,定义了一个指针变量 p,其指向
    的内存里面保存的是 int 类型的数据;但是这时候变量 p 本身的值是多少不得而知,也就是
    说现在变量 p 保存的有可能是一个非法的地址。第二行代码,给*p 赋值为 NULL,即给 p
    指向的内存赋值为 NULL;但是由于 p 指向的内存可能是非法的,所以调试的时候编译器可
    能会报告一个内存访问错误。
    
    tips:NULL(NUL or null) == 数字0 == "\0" == " " ASCII 码值为 0  "0"ASCII码为48  
  • 将数值存储到指定的内存地址

    int *p = (int *)0x12ff7c;
    *p = 0x100;
       ==
    *(int *)0x12ff7c = 0x100;
  • 数组

  • 数组名 a 作为左值和右值的区别
    a 作为右值时其意义与&a[0]是一样,代表的是数组首元素的首地址,而不是数组的首地址。
    a 不能作为左值,我们只能访问数组的某个元素而无法把数组当一个总体进行访问。

  • a 和&a 的区别

    main()
    {
      int a[5]={1,2,3,4,5};
      int ptr=(int )(&a+1);
      printf("%d,%d",(a+1),(ptr-1));
    }打印出来的值为多少呢? 这里主要是考查关于指针加减操作的理解。

    对指针进行加 1 操作,得到的是下一个元素的地址,而不是原有地址值直接加 1。所以,一个类型为 T 的指针的移动,以 sizeof(T) 为移动单位。 因此,对上题来说, a 是一个一维数组,数组中有 5 个元素; ptr 是一个 int 型的指针。

    &a + 1: 取数组 a 的首地址,该地址的值加上 sizeof(a) 的值,即 &a + 5sizeof(int),也 就是下一个数组的首地址,显然当前指针已经越过了数组的界限。 (int )(&a+1): 则是把上一步计算出来的地址,强制转换为 int * 类型,赋值给 ptr。

    *(a+1): a,&a 的值是一样的,但意思不一样, a 是数组首元素的首地址,也就是 a[0]的 首地址, &a 是数组的首地址, a+1 是数组下一元素的首地址,即 a[1]的首地址,&a+1 是下一 个数组的首地址。所以输出 2

    (ptr-1): 因为 ptr 是指向 a[5],并且 ptr 是 int 类型,所以 *(ptr-1) 是指向 a[4] , 输出 5。

    tips:指针变量与一个整数相加减并不是用指针变量里的地址直接加减这个整数。

  • 指针和数组的定义与声明
    文件 1 中定义的数组在文件 2 中声明为指针会发生错误。
    文件 1 中定义为指针,而在文件中声明为数组也会发生错误:

  • 指针和数组的对比

    指针 
    1.保存数据的地址,任何存入指针变量 p 的数据都会被当作地址来处理。 p 本身的地址由
    编译器另外存储,存储在哪里,我们并不知道。
    2.间接访问数据,首先取得指针变量 p 的内容,把它作为地址,然后从这个地址提取数据或向这个地址写入数据。指针可以以指针的形式访问*(p+i);
    也可以以下标的形式访问 p[i]。但其本质都是先取 p 的内容然后加上i*sizeof(类型)个 byte 作为数据的真正地址。
    3.通常用于动态数据结构
    4.相关的函数为 malloc 和 free。 
    5.通常指向匿名数据(当然也可指向具名数据) 
    数组
    1.保存数据,数组名 a 代表的是数组首元素的首地址而不是数组的首地址。 &a 才是整个数组的首地址。 a 本身的地址由编译器另外存储,存储在哪里,我们并不知道。
    2.直接访问数据,数组名 a 是整个数组的名字,数组内每个元素并没有名字。只能通过“具名+匿名”的方式来访问其某个元素,不能把数组当一个整体来进行读写操作。
    数组可以以指针的形式访问*(a+i); 也可以以下标的形式访问 a[i]。但其本质都是 a 所代表的数组首元素的首地址加上 i*sizeof(类型)个 byte 作为数据的真正地址。
    3.通常用于存储固定数目且数据类型相同的元素。
    4.隐式分配和删除
    5.自身即为数组名
  • 指针数组和数组指针

    1.指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身
    决定。它是“储存指针的数组”的简称。 eg: int *p1[10];
    2.数组指针:首先它是一个指针,它指向一个数组。在 32 位系统下永远是占 4 个字节,
    至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。eg:int (*p2)[10];


    tips: C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元
    素首地址的指针。
       函数本身是没有类型的,只有函数的返回值才有类型。

  • 无法把指针变量本身传递给一个函数

    void GetMemory( char * p, int num)
    {
      p = (char *)malloc(num*sizeof(char));
    }
    int main()
    {
      char *str = NULL;
      GetMemory( str, 10) ;
      strcpy(str,”hello”);
      free( str); //free 并没有起作用,内存泄漏
    return 0;
    }
    在运行 strcpy(str,”hello”)语句的时候发生错误。这时候观察 str 的值, 发现仍然为 NULL。
    也就是说 str 本身并没有改变,我们 malloc 的内存的地址并没有赋给 str,而是赋给了_str。
    而这个_str 是编译器自动分配和回收的,我们根本就无法使用。所以想这样获取一块内存是
    不行的。那怎么办? 两个办法:
    第一:用 return。
    char * GetMemory( char * p, int num)
    {
      p = (char *)malloc(num*sizeof(char));
      return p;
    }
    int main()
    {
      char *str = NULL;
      str = GetMemory( str, 10) ;
      strcpy(str,”hello”);
      free( str);
      return 0;
    }
    这个方法简单,容易理解。
    第二:用二级指针。
    void GetMemory( char ** p, int num)
    {
      *p = (char *)malloc(num*sizeof(char));
      return p;
    }
    int main()
    {
      char *str = NULL;
      GetMemory( &str, 10) ;
      strcpy(str,”hello”);
      free( str);
      return 0;
    }
    注意,这里的参数是&str 而非 str。这样的话传递过去的是 str 的地址,是一个值。在函
    数内部,用钥匙( “ *”)来开锁: *(&str),其值就是 str。所以 malloc 分配的内存地址是真正
    赋值给了 str 本身。
  • 二维数组参数与二维指针参数

    数组参数                         等效的指针参数
    数组的数组: char a[3][4]    数组的指针: char (*p)[4]
    指针数组: char *a[5]       指针的指针: char **p      

    tips:1.C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析
    成一个指向其首元素首地址的指针。这条规则并不是递归的,也就是说只有一维数组才是
    如此,当数组超过一维时,将第一维改写为指向数组首元素首地址的指针之后,后面的维
    再也不可改写。比如: a[3][4][5]作为参数时可以被改写为( *p) [4][5]。
    2.作为参数时,一维数组“ []”号内的数字完全可以省略:void fun( char a[ ][4]),不过第二维的维数不可省略。

  • 函数指针
    形如: char (fun1)(char p1,char p2);

    (*(void(*) ())0)()
    第一步: void(*) (),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。
    第二步: (void(*) ())0,这是将 0 强制转换为函数指针类型, 0 是一个地址,也就是说一
    个函数存在首地址为 0 的一段区域内。
    第三步: (*(void(*) ())0),这是取 0 地址开始的一段内存里面的内容,其内容就是保存
    在首地址为 0 的一段区域内的函数。
    第四步: (*(void(*) ())0)(),这是函数调用。
    
  • 引用和指针的区别

C++中的引用:
1.概念
 引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
 其格式为:类型 &引用变量名 = 已定义过的变量名。
2.特点:
 ① 一个变量可取多个别名。
 ② 引用必须初始化。
 ③ 引用只能在初始化的时候引用一次 ,不能更改为转而引用其他变量。

  • 引用和指针的区别和联系(笔试热点)
      1. 引用只能在定义时初始化一次,之后不能改变指向其它变量(从一而终);指针变量的值可变。
      2. 引用必须指向有效的变量,指针可以为空。
      3. sizeof指针对象和引用对象的意义不一样。sizeof引用得到的是所指向的变量的大小,而sizeof指针是对象地址的大小。
      4. 指针和引用自增(++)自减(--)意义不一样。
      5. 相对而言,引用比指针更安全。
    
    eg:
  • 普通引用
      a = 2;  
      b = 3;  
      int& c = b;// 引用一个引用变量,别名的别名  
    
  • const 引用
      const int & d6 = 5;//常量具有常性,只有常引用可以引用常量  
    
  • 引用传参
    1.【值传递】如果形参为非引用的传值方式,则生成局部临时变量接收实参的值  
    void Swap (int left, int right) //值传递的方式无法实现交换,因为传参时对于参数left和right拷贝一临时副本,交换的是副本值,因为其是临时变量函数退出,变量销 {                                //毁,并不会影响外部left和right的值。  
       int temp = left;  
       left = right ;  
       right = temp ;  
    }  
    2.【引用传递】如果形参为引用类型,则形参是实参的别名。  
    void Swap (int& left, int& right)//使用引用的话,不做临时拷贝,&的使用说明此处只是原参数的另一个名字而已,所以修改时直接在原参数的基础上修改变量值。  
    {  
       int temp = left;  
       right = left ;  
       left = temp ;  
    }  
    3.【指针传递】  
    void Swap (int* pLeft, int* pRight)//传入的是地址,因为地址是唯一的,所以指针通过地址的访问进而可修改其内容。  
    {  
       int temp = *pLeft;  
       *pLeft = *pRight;  
       *pRight = temp;  
    }  
    

- tips:引用和指针的区别和联系:

★不同点:
 1. 指针是一个实体,而引用仅是个别名;
 2. 引用使用时无需解引用(*),指针需要解引用;
 3. 引用只能在定义时被初始化一次,之后不可变;指针可变;
 4. 引用没有 const,指针有 const;const修饰的指针不可变;
 5. 引用不能为空,指针可以为空;
 6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
 7. 指针和引用的自增(++)运算意义不一样;
 8. 从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。

★相同点:两者都是地址的概念,指针指向一块儿内存,其内容为所指内存的地址;引用是某块儿内存的别名。

详情请见:http://blog.csdn.net/xiao__tian__/article/details/51814617

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值