《C语言教程》11章 指针


指针是C语言中最难掌握的内容,因此小雅将放慢进程,让大家真正明白指针。

一、指针符号『*』和地址符号『&』

『&』符号是取变量的地址,『*』符号是取地址的内容(即:值)。两个操作正好相反。例如:“&i”就是取变量i的地址,“*(&i)”就是取“&i”这个地址的值,其实就是变量i。即然如此,为什么还要定义指针呢?原来,用『&』所取到的地址,自身只能用而不能修改。因此,直接把『&』取到的地址放到指针变量中去,既然指针变量也是变量,这个变量就可以任意存放其它地址。


#include <stdio.h>

int main(void)
{
    int i = 100, j=200;
    int *p;

    p = &i;     //变量i的地址赋给p
    printf("&i=%p   *(&i)=%d\n",  &i, *(&i));
    printf(" p=%p   *p   =%d\n\n", p,  *p);

    p = &j;     //变量j的地址赋给p
    printf("&j=%p   *(&j)=%d\n", &j, *(&j));
    printf(" p=%p   *p   =%d\n", p,  *p);

    return 0;
}

二、指针变量的赋值和指针的赋值

上例中p是指针变量,*p是p的指针,p存放的是某个变量的地址,*p存放的是某个变量的值。当*p的内容改变时,p所指的变量的内容也发生改变,因为是同一个地址的存贮单元的值发生改变。同理,当p所指的变量的值发生改变时,*p的内容也随之改变。


#include <stdio.h>

int main(void)
{
    int i = 100, j=200;
    int *p;

    p = &i;     //变量i的地址赋给p
    *p = 500;   //将500赋给p指针
    //变量i的内容也随之改变为500
    printf("&i=%p   *(&i)=%d\n",  &i, *(&i));
    printf(" p=%p   *p   =%d\n\n", p,  *p);

    p = &j;     //变量j的地址赋给p
    j++;        //将p指针的内容+1
    //指针*p的内容也随之改变201
    printf("&j=%p   *(&j)=%d\n", &j, *(&j));
    printf(" p=%p   *p   =%d\n", p,  *p);

    return 0;
}

三、被初始化的是指针变量还是指针

上面2例,指针变量都是用的p,初学者不要认为只能用p,既然是变量,只要不违反命名规则都可以。当指针变量被定义时立即赋值,这时被赋值的是指针变量还是指针呢?下面这段程序请大家千万注意!

#include <stdio.h>

int main(void)
{

    char str[] = "http://www.quanxue.cn/";
    char *ptr = "http://www.51minge.com/";
    char *point;

    point = str;  //将str数组的地址赋给指针变量point
    point[11] = 'Q', point[15] = 'X';
    printf("str=%s\n",str);

    ptr[13] = 'M', ptr[16] = 'G';  //这句是错误的,删除后结果如下
    printf("ptr=%s\n",ptr);

    return 0;

}
  1. char str[] = "http://www.quanxue.cn/";中str是数组变量,当地址赋给point之后,point[11]就是str[11],所以其内容可以改变。
  2. char *ptr = "http://www.51minge.com/";中赋值的性质和上面的str不同。这并不是将"http://www.51minge.com/"赋给*ptr指针,而是先定义一个常量"http://www.51minge.com/",这个常量是定义在“栈”里面,然后将这个常量的地址赋给ptr,而不是*ptr。常量是不能被修改的,因此ptr[13]也就出错了。这是初学者经常犯的错误。
    int i = 129;
    int num[] = {50, 25, 75, 100};

    int *pt1 = 125;    //错误1: 125作为地址赋给pt1,  这段内存是OS用的
    int *pt2 = i ;     //错误2: 129作为地址赋给pt2,  这段内存是OS用的
    int *pt3 = &i;     //正确:  变量i的地址赋给pt3,  因为i是基本类型,所以要加&符号
    int *pt4 = num;    //正确:  数组num的地址赋给pt4,因为num是数组,变量名就代表地址

    char *pt5 ;
   *pt5 = "ABCDE";     //错误3:  "ABCDE"是字符串,也是数组, 此处更是常量
    pt5 = "ABCDE";     //正确:   赋值时赋的是地址,因此只能赋给指针变量pt5

    int *pt6 ;
    pt6 = i;           //错误4:  129作为地址赋给pt6,  这段内存是OS用的
   *pt6 = i;           //错误5:  129赋给指针pt6,但pt6尚未分配地址,没有空间存放i的值

四、不赋值的指针和NULL

未赋值的指针变量是不能被使用的,其地址指向未不能使用的空间。建议定义时如果暂不使用,先赋NULL。为一个指针申请空间时,一定义要判断其是否为空,因为分配内存失败时返回NULL。不仅如此,甚至在使用指针时都应该判断一下是否为空。下面讲到的内存分配是重要内容,在下下章详细介绍。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char *pchar;

    //下面这句出错,忽略之后继续
    printf("pchar=%p\n", pchar);  //未分配的指针不可用

    pchar = NULL;
    printf("pchar=%p\n\n", pchar);  //空指针可用于表达式中

    //pchar为空时,动态分配内存
    if (!pchar) {
        pchar = (char *)malloc(20);  //动态分配内存
        if (!pchar) {  //初学者不要忘记, 这是必要的判断
            printf("内存分配失败。");
            exit(-1);  //退出
        }
        gets(pchar);
        printf(" pchar=%p\n*pchar=%s\n", pchar, pchar);
        free(pchar);   //内存释放后,指针变量的地址不变
        if (pchar) {   //pchar并不为空
            printf("\n pchar=%p\n*pchar=%s\n", pchar, pchar);  //pchar成了“野指针
        }
    }

    return 0;
}