C语言第六章:指针

 一,什么是指针

 1.要想了解指针的定义,必须首先了解计算机地址的定义。

  • 计算机地址是指内存条的内存单元的编号。是从0开始的非负整数。
  • 地址的范围是由内存条的大小决定的。
  • 4G内存的地址是在:0-4G-1范围内

2.其次要了解计算机的CPU和内存条之间的关系

  • CPU要对内存条进行控制的话需要三根线:控制总线,数据总线,地址总线
  • 控制总线:控制总线是控制CPU是对内存条是进行只读操作,还是只写操作,还是可读可写操作的。
  • 数据总线:数据总线是CPU和内存条进行数据交换的通道。
  • 地址总线:地址总线是CPU进行寻址的,即CPU要对那个内存地址进行操作,例如地址总线有32根,即可以对2的32次方个内存地址进行操作,也就是4G,此时如果是4G内存条,就可以完全做到寻址

  

 3.指针的含义

  • 指针就是地址,地址就是指针。
  • 指针变量就是存放内存单元编号的变量,或者说指针就是存放地址的变量。
  • 指针和指针变量是两个完全不同的概念。
  • 通常我们叙述时,会把指针变量简称为指针,实际它们的含义并不一样,指针本质其实是一个操作受限的非负整数

二,指针的定义

 1.指针变量的基本定义:

# include<stdio.h>

int main(void)
{
    int i = 10;
    int * p;// p是变量名,int *是数据类型,表示p变量只能存放int类型变量的地址
    p = &i; // 把变量i的地址赋值给p,
    return 0;
}

 

 2.指针变量的解释:

  •  指针变量p保存了变量i的地址,因此可以说指针p指向变量i。
  • p不是i,i也不是p。修改p的值不会影响i的值,修改i的值不会影响p的值。
  • 如果一个指针变量p指向一个普通变量i,则*p == i。*p的准确解释就是:*p表示的是以p的内容为地址变量。

 3.经典的指针程序:

 互换两个变量的值

# include<stdio.h>

void fun(int * p,int * q){
    int temp;
    temp = *p;
    *p = *q;
    *q = temp;
}

int main(void)
{
    int i = 10;
    int j = 20;

    fun(&i,&j);

    printf("%d %d",i,j);

    return 0;
}

  三,一维数组与指针的关系

1.一维数组的名称:

  • 一维数组的名称其实就是一个常量,它表示一维数组的第一个元素的地址。
# include<stdio.h>

int main(void)
{
    int arr[5] = {1,2,3,4,5};

    printf("%#X  %#X \n",arr,&arr[0]);
    /*
        打印的结果相同:因此说明arr其实就是数组第一个元素的地址
    */
    return 0;
}

 

2.一维数组下标和指针的关系:

  • 如果指针变量p指向数组arr,则p[i] = arr[i]。
  • 数组下标与指针的关系:arr[i] = *(p+i);
  • 当p+1的时候,实际上是指针p移动到下一个地址,这个地址不是简单的加1运算,而是根据数据类型的长度而计算出来的,例如int数组,则p+1实际上是指针p移动了4个字节
# include<stdio.h>

int main(void)
{
    int arr[5] = {1,2,3,4,5};
    int * p;
    
    p = arr; // p指向arr,因此p[i] == arr[i]
    printf("%d \n",p[3]);

    int temp0 = *p; // *p 表示的是数组的第一个元素
    printf("%d \n", temp0);

    int temp1 = *(p+1); // *(p+1) 表示的是数组的第二个元素
    printf("%d \n", temp1);

    // 根据上面的结果我们推算出:arr[i] == *(p+i);

    return 0;
}

 

3.一个函数处理一个数组用指针需要接收几个参数:

  • 这个函数的形参需要两个参数:数组的名称和数组的长度。
  • 通过上面两个参数,就可以让指针完全代替数组进行运算。

4.指针变量的运算:

  • 指针变量不能相加,不能相减,不能相乘,不能相除。
  • 如果指针变量指向的是同一块连续空间的不同内存单元,则可以进行加法和减法。

5.一个指针变量占几个字节:

  • sizeof(数据类型/变量名)函数,返回该数据类型/变量名所占的字节数。
  • 指针变量无论它指向的数据类型占几个字节,其所占字节数都为4个字节。
# include<stdio.h>

int main(void)
{
    int a = 0;
    char b = 'A';
    double c = 23.12;

    printf("%d %d %d \n",sizeof(a),sizeof(b),sizeof(c));
    // 输出结果为:4 1 8

    int * x = &a;
    char * y = &b;
    double * z = &c;

    printf("%d %d %d \n",sizeof(x),sizeof(y),sizeof(z));
    // 输出结果为:4 4 4

    return 0;
}

 四,动态内存与静态内存

 1.什么是多级指针:

  • 存放指针的指针就是多级指针 
# include<stdio.h>

int main(void)
{
    int i = 10;
    int * p = &i; // 将变量i的地址赋值给指针p
    int **q = &p; // 将指针变量p的地址赋值给指针q(因为p的数据类型为int *,所以指针p的地址是int **)

    return 0;
}

 

 2.多级指针的理解:

  • 多级指针需要理解的是指针的定义,即指针是用来存放某种数据类型的地址,所以这个某种类型也可以是指针变量,所以会出现多个*叠加的情况,这个是需要理解的。

 3.传统数组的缺点:

  • 数组的长度必须事先指定,切不能为变量,必须为常整数。
  • 传统定义的数组,程序员无法进行内存的释放,只能等函数执行完成后,函数弹出栈后,内存才会释放。
  • 数组长度在函数运行过程中,不能动态的增加和缩减。
  • 数组的存在时间只能在函数的运行期间存在,函数弹栈释放内存后,无法再被使用。

 4.为什么需要动态内存:

  • 动态内存很好的解决了传统数组的4个缺陷问题。
  • 传统数组也叫做静态数组。
  • 所有函数内部定义的操作都是静态内存,由系统自己分配内存空间,由系统自己释放内存空间。 

 5.什么是动态内存:

  •  动态内存是允许我们自己向系统申请一块内存空间,该内存空间的创建和释放是掌握在程序员的手里
  •  动态内存是在堆内存中分配的
  •  静态内存是在栈内存中分配的

 6. malloc 函数

  • malloc是memory allocate的缩写,中文含义是内存分配
  • malloc(n)函数用于向系统中申请分配n个字节的连续内存空间。返回类型是void *类型。
  • void *类型表示未确定类型的指针,因为我们在申请内存空间的时候,并不知道这块内存空间用来存放什么样的数据类型。因此C/C++规定,void *类型可以通过数据类型的强制转换而成为其他类型的指针(例如:int * , char *)。
  • malloc函数使用之前,必须引入头文件:# include <malloc.h>

  7. 动态内存分配示例

  假设我们构造一个长度为len的int类型的一维数组。

  int * p = (int *)malloc(len);

      1. 这条语句分配了两块内存,一块是长度为len的动态内存,一块为静态内存。并且这块静态内存是p变量本身所占的内存,占4个字节(因为是一个指针变量)。

  2. malloc函数的功能是向系统申请len个字节的内存空间,如果请求分配成功,则返回第一个内存单元的地址,分配不成功则为null

  3. malloc函数只能返回第一个字节的地址,所以我们需要把这个没有实际含义的地址(俗称干地址)转换成一个有实际意义的地址,因此malloc函数前面必须加(数据类型 *),表示把这个无实际意义的地址转          换成相应数据类型的地址,这样我们的内存空间就会按照该数据类型进行划分(例如:int *则会将这个len为100字节的内存单元划分成实际存储25个int类型变量)。

  4. 例如:int * p = (int *)malloc(100); 

    表示将系统分配的100个字节的内存空间的第一个地址转换成int类型的地址,由于int类型是占4个字节,所以此时的第一个字节的地址转化成前四个字节的地址。所以p指向的是第一个4个字节的地址,p+1则          是第二个4个字节的地址,依次类推。。。申请的这个内存空间,可以存放25个int类型的变量。

 8. free 函数

  • 该函数是用于释放动态内存的函数,例如上面的分配的100个字节的动态内存,我们程序员如果想要释放的话,就可以调用此函数:free(p);就会将该内存释放掉。
  • free(p)的含义是指将p指针所指向的内存空间进行释放,p变量本身的内存是静态内存,是随着函数执行的结束而释放的。

 9. 动态内存和静态内存的比较

  •  静态内存是由系统进行分配,由系统进行释放。静态内存是在栈内存中进行分配的。
  •  动态内存是由程序员手动分配,由程序员手动释放。动态内存是在堆内存中进行分配的。
  •  静态内存不能够跨函数使用,因为静态内存是在栈内存中分配的,在函数执行完毕之后,函数出栈,系统释放掉函数所占的内存,所以无法被其他函数继续使用。只能够在函数运行期间进行使用。
  •  动态内存是可以跨函数使用的,因为动态内存是在堆内存中进行分配的,动态内存的生命周期由程序员进行控制,因此其他内存在函数执行完毕之后是依然可以继续使用的。

 

posted @ 2016-11-10 18:02  MetalSteel  阅读(369)  评论(0编辑  收藏  举报