zoukankan      html  css  js  c++  java
  • C指针典例

    C指针典例

    2015-03-10 李海沿

    一、指针的算术运算

    例一、

    1、 char a[20];

    2、 int *ptr=a;

    3、 ptr++;

    在上例中,指针ptr的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr的值加上了sizeof(int),在32位程序中,是被加上了4。由于地址是用字节做单位的,故ptr所指向的地址由原来的变量a的地址向高地址方向增加了4个字节。由于char类型的长度是一个字节,所以,原来ptr是指向数组a的第0号单元开始的四个字节,此时指向了数组a中从第4号单元开始的四个字节。

    例二、

    1、 char a[20];

    2、 int *ptr=a;

    3、 ptr+=5;

    在这个例子中,ptr被加上了5,编译器是这样处理的:将指针ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。由于地址的单位是字节,故现在的ptr所指向的地址比起加5后的ptr所指向的地址来说,向高地址方向移动了20个字节。

    在这个例子中,没加5前的ptr指向数组a的第0号单元开始的四个字节,加5后,ptr已经指向了数组a的合法范围之外了。

    虽然这种情况在应用上会出问题,但在语法上却是可以的。这也体现出了指针的灵活性。

    如果上例中,ptr是被减去5,那么处理过程大同小异,只不过ptr的值是被减去5乘sizeof(int),新的ptr指向的地址将比原来的ptr所指向的地址向低地址方向移动了20个字节。

    总结一下,一个指针ptrold加上一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值增加了n乘sizeof(ptrold所指向的类型)个字节。

    就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向高地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。

    一个指针ptrold减去一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值减少了n乘sizeof(ptrold所指向的类型)个字节,就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向低地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。

    二、指针表达式

    例一、

    int array[10]={0,1,2,3,4,5,6,7,8,9},value;

    value=array[0];//也可写成:value=*array;

    value=array[3];//也可写成:value=*(array+3);

    value=array[4];//也可写成:value=*(array+4);

    上例中,一般而言数组名array代表数组本身,类型是int [10],但如果把array看做指针的话,它指向数组的第0个单元,类型

    是int *,所指向的类型是数组单元的类型即int。因此*array等于0就一点也不奇怪了。同理,array+3是一个指向数组第3个

    单元的指针,所以*(array+3)等于3。其它依此类推。

    例二、

    char *str[3]={

    "Hello,this is a sample!",

    "Hi,good morning.",

    "Hello world"

    };

    char s[80];

    strcpy(s,str[0]);//也可写成strcpy(s,*str);

    strcpy(s,str[1]);//也可写成strcpy(s,*(str+1));

    strcpy(s,str[2]);//也可写成strcpy(s,*(str+2));

    上例中,str是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str当作一个指针的话,它指向数组的第0号单元,它的类型是char**,它指向的类型是char *。

    *str也是一个指针,它的类型是char*,它所指向的类型是char,它指向的地址是字符串"Hello,this is a sample!"的第一个字

    符的地址,即'H'的地址。 str+1也是一个指针,它指向数组的第1号单元,它的类型是char**,它指向的类型是char *。

    *(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向"Hi,good morning."的第一个字符'H',等等。

    三、指针与结构类型

    例一:

    struct MyStruct{

    int a;

    int b;

    int c;

    }

    MyStruct ss={20,30,40};//声明了结构对象ss,并把ss的三个成员初始化为20,30和40。

    MyStruct *ptr=&ss;//声明了一个指向结构对象ss的指针。它的类型是

    MyStruct*,它指向的类型是MyStruct。

    int *pstr=(int*)&ss;//声明了一个指向结构对象ss的指针。但是它的类型和它指向的类型和ptr是不同的。

    请问怎样通过指针ptr来访问ss的三个成员变量?

    答案:

    ptr->a;

    ptr->b;

    ptr->c;

    又请问怎样通过指针pstr来访问ss的三个成员变量?

    答案:

    *pstr;//访问了ss的成员a。

    *(pstr+1);//访问了ss的成员b。

    *(pstr+2)//访问了ss的成员c。

    四、指针与函数的关系

    可以把一个指针声明成为一个指向函数的指针。

    int fun1(char*,int);

    int (*pfun1)(char*,int);

    pfun1=fun1;

    int a=(*pfun1)("abcdefg",7);//通过函数指针调用函数。

    可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。

    五、指针类型转换

    如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP*和TYPE,那么语法格式是:

    (TYPE*)p;

    这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都没有被修改。一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,也会发生指针类型的转换。

    int a=123,b;

    int *ptr=&a; //此时ptr指针的内容是a的地址,此处有点绕

    char *str;        

    b=(int)ptr;        //把指针ptr的值当作一个整数取出来。

    str=(char*)b;        //把这个整数的值当作一个地址赋给指针str。

    好了,现在我们已经知道了,可以把指针的值当作一个整数取出来,也可以把一个整数值当作地址赋给一个指针。

    六、指针的安全问题

    例一、

    char s='a';

    int *ptr;            

    ptr=(int*)&s;

    //ptr指向的是S的地址,将原先的1个字节(有效地址)拓展为4个字节(其他三个字节为非法地址)

    *ptr=1298;        

    //1298将s指向的地址的后面三个非法地址给重新赋值,超出了允许的地址,重则引起系统奔溃

    指针ptr是一个int*类型的指针,它指向的类型是int。它指向的地址就是s的首地址。在32位程序中,s占一个字节,int类型占四个字节。最后一条语句不但改变了s所占的一个字节,还把和s相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。

    例二、

    1、 char a;

    2、 int *ptr=&a; //ptr指向a的地址,有效地址长度为1个字节

    3、 ptr++;            //此处指针自加后,指向a的地址的下一个字节,为非法地址

    4、 *ptr=115;        //此处对非法地址进行了非法操作

    该例子完全可以通过编译,并能执行。但是看到没有?第3句对指针ptr进行自加1运算后,ptr指向了和整形变量a相邻的高地址方向的一块存储区。这块存储区里是什么?我们不知道。有可能它是一个非常重要的数据,甚至可能是一条代码。而第4句竟然往往这片存储区里写入一个数据!这是严重的错误。所以在使用指针时,程序员心里必须非常清楚:我的指针究竟指向了哪里。

        在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。

    注意:

    在指针的强制类型转换:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的类型)大于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是安全的。如果sizeof(ptr2的类型)小于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是不安全的

    看看以下代码,想想运行结果:

    #include <stdio.h>
    
    int *p;
    
    pp(int a,int *b);
    
    main()
    {
        int a=1,b=2,c=3;
        p=&b;
        printf("(1) %d %d %d 
    ",a,b,*p);
        pp(a+c,&b);
        printf("(3) %d %d %d 
    ",a,b,*p);
    }
    
    pp(int a,int *b)
    {
        int c=4;
        *p=*b+c;
        a=*p-c;
        printf("(2) %d %d %d 
    ",a,*b,*p);
    }

  • 相关阅读:
    vue-生命周期图示 注解
    vue-组件嵌套之——父组件向子组件传值
    vue-框架模板的源代码注释
    vue-小demo、小效果 合集(更新中...)
    Gulp-自动化编译sass和pug文件
    JS
    Node.js- sublime搭建node的编译环境
    sublime--package control的配置与插件安装
    git-常用命令一览表
    java面试题:jvm
  • 原文地址:https://www.cnblogs.com/lihaiyan/p/4328612.html
Copyright © 2011-2022 走看看