指针

指针应该是c语言中的核心了。。但是自己不管怎么学都始终云里雾里,在做完后大作业后才发现,如果单独去理解指针确实是有困难,但是如果将指针与其他的数据结构结合在一起,或许可以理解的更加深刻一点点吧(大概╮(╯▽╰)╭)

指针的定义

(点击代码框的text可以放大代码)
先看下面几行行语句

    int a;
    int *p;
    p=&a;     

指针的作用是存储一个地址,更确切的说是存储一个内存空间的地址,在上面的代码中,指针p利用&运算符取得了一个变量a的地址 那么内存空间该怎么理解呢?看下面这段代码

    int  var1;
    char var2[10];

printf("var1 变量的地址: %p\n", &var1  );
printf("var2 变量的地址: %p\n", &var2  );

return 0;
下列的代码输出结果为
    var1 变量的地址: 0x7fff5cc109d4
    var2 变量的地址: 0x7fff5cc109de

每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址

指针的作用

那么,为什么访问一个变量要用指针呢,在我刚开始学习时感到非常的不解,但是现在,我当时的想法是有问题的,指针并没有访问一个变量 ,它访问的永远是变量的地址,而不是一个单纯的值,所以在模块化设计中,指针的作用便体现出来了,首先我们必须要明确一个概念,在函数的传递值是单向的,不可逆的,如果没有return的话,你在函数对数做的任何操作,对主函数是没有影响的,所以有两句话
1,以数组元素做实参,向形式参数传递的是数组的值
2、以数组名做实参,向形式参数传递的是数组的地址
值和地址可不一样,值只是第一个单一的数,而地址是整个数组 ,所以这句话的数组名可以理解成数组名的首地址,
所以看下面的代码

        int balance[5] = {1000, 2, 3, 17, 50};
        avg = getAverage( balance, 5 ) ;
   记住 ,数组名为该数组首元素的地址,访问到首个地址就可以访问所有数组的地址。 
        double getAverage(int *arr, int size)
        {
        int    i, sum = 0;      
        double avg;          

        for (i = 0; i < size; ++i)
        {
            sum += arr[i]; //注意 arr[i]和*(arr+i)等价  记住规则即可,本质是利用指针从头部开始遍历。
        }
avg = (double)sum / size;

return avg;}

这段代码,以数组名做实参后,既然这是一个地址,自然需要一个指针去访问,所以在形式参数中,定义了一个*arr的指针。
这便是指针的基本性质 ,通过指针作形式参数,可以直接对原函数的数组进行修改,完成模块化设计中各种数据的交接。后文会提到

链表

在了解了指针的基本性质后,我们便可以对链表进行一个基本的了解了,那么,什么是链表呢?请参考这幅图有一个基本的了解。

440546183546917240.jpg
哈 ,通俗的说,链表就是由数个由结构体节点组成的一个数据结构,而每个结构体分为两个部分,第一个我们需要存放的数值,第二个是则是指向下个节点的该结构体类型的指针,
就像这样

    struct node
    {
      int data;
      struct node *next;

    }

在上面的代码中,我们定义了一个名字为node结构体类型,他由两部分组成,一个数据data,一个是指针,用来指向下一个节点的地址,因为下个节点的类型为struct node,所以指针类型自然为struct *node啦。

那么现在让我们开始建立一个链表,首先,任何数据都要有一个开始访问的路,所以我们需要建立一个头指针。
struct node *head 去指向链表的第一个结点。 一开始为空head=null。
接下来,我们来创建第一个节点,并用临时指针p去指向他们。

    struct node*p;
    p=(struct node *)malloc(sizeof(struct node));//动态申请一个空间去存放节点,

这时,我们就可以利用指针p来操作这个空间勒,
接下来,设置这个结点的左半部分和右半部分。就是存放数据啦。

    scanf("%d",&a);
    p->data=a;
    p->next=null;

其中,->叫做结构体运算符,用来访问结构体的内部成员的,因为p是一个指针,所以不能用结构体符号.来访问

接下来我们可以设置头指针并设置新新创建的节点指向空,头指针的作用是遍历整个链表。
同时,因为后续需要利用for循环去创建多个结点。所以需要去判断结点的插入情况。是头部的结点?还是中间,还是尾部

if(head==null)

  head=p;  //如果这是头一个结点,则把p指向的地址传入head;
else
  q->next=p //如果不是第一个结点,则把上一个结点的next指针指向当前结点,完成两个链表的拼接

最后,也是我忽略的一步,q=p;需要将指针q指向当前的p结点,因为指针p只是临时的,待会还要用来指向其他的数据,这时候,p的地址传入q,q就正式获取了我们的数据啦
先看完整的代码。

#include<stdio.h>
#include<stdlib.h> 
struct node
{
  int data;
  struct node *next;




} ;


int main()
{  struct node *head ,*p,*q,*t;

    int i,n,a;
    scanf("%d",&n); 
  head=NULL;
          for(i=1;i<=n;i++)
      {

        scanf("%d",&a);
        p=(struct node*)malloc(sizeof(struct node)); // 
        p->data=a;
        p->next=NULL;


        if(head==NULL)
        head=p;

        else 
        q->next=p;


          q=p;

      }    

        接下来,就是进行链表的遍历,这一块就比较方便了,只需要一个指针访问头指针然后从头开始遍历

    t=head;
    while(t!=null)
    {
      printf("%d",t->data);
      t=t->next;
    }
    getchar();
    return 0;
} 

这时候,我们又要强调一次指针的作用了,指向地址,看,我们用for循环输入了这么多数据,但是最终用一个指针就实现了对他们的操纵,在看上文提到的,指针访问的始终是一地 址,所以我们可以用指针去访问不同的地址,我们在for循环中利用for循环中的malloc分配了不同的内存空间,而指针q指向的最终的便是他们

这就是一个最基本的链表勒。0_o.