zoukankan      html  css  js  c++  java
  • C/C++指针相关

    指针的运算和普通的算术运算是不同的,支持的运算符也很少,有时候你把两个指针相减得到的结果可能会不如你所想。今天来稍微总结一下:

    1. 指针的有限算术运算:自增(++), 自减(--), 加上一个整数(+, +=), 减去一个整数(-, -=), 以及减去另一个指针

    2. 指针加上或减去一个整数时,并非简单地加上或减去该整数值,而是加上该整数与指针引用的对象的大小的乘积。对象的大小(字节数)取决于对象的数据类型。

    3. 对于x=p1-p2,是把从p2到p1的数组元素的个数赋给x。因为除了数组元素外,我们不能认为两个相同类型的变量是在内存中连续存储的,所以指针算术运算除了用于数组外没有什么意义。两个指针相减的结果的类型是ptrdiff_t,它是一种有符号整数类型。

    4. 常见的程序设计错误有:

      (1)对不指向数组的指针进行算术运算。

      (2)把不指向同一数组的两个指针相减或比较。(C和指针P100 讲的比较形象)

      (3)指针算术运算的结果超出了数组的范围。

    5. 指针类型转换问题:如果两个指针类型相同,那么可以把一个指针赋给另一个指针,否则必须用强制类型转换,把赋值运算符右边的指针的类型转换为赋值运算符左边指针的类型。指向void *类型的指针是例外。任何类型的指针都可以赋给指向void类型的指针,指向void类型的指针也可以赋给任何类型的指针(:这种情况对于C++就是不对的,参见think in C++,也就是说void* 不能直接赋值给其他类型的指针必须进行强制类型转换)。这两种情况都不需要使用强制类型转换。

    先来看一个例子:

    #include <stdio.h>
    
    #include <stdlib.h>
    
    struct A
    {
    
            int a;
    
            int b;
    
    };
    
     
    #define offsetof(TYPE,MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
    
     
    #define container_of(ptr,type,member) ( {\
    
        const typeof( ((type*)0)->member ) *__mptr=(ptr);\
    
        (type*)( (char*)__mptr - offsetof(type,member) );} )
    
     
    int main() { struct A *p = (struct A *)malloc(sizeof(struct A)); p->a = 1; p->b = 2; int *p1 = NULL; p1 = &p->b;      //这里的目的是通过算术运算,从p1的地址(p->b的地址)去算出 p->a的地址 printf("p1-4: %x %d\n",((int )p1-sizeof(int)),*(int *)((int )p1-sizeof(int)));      //先将指针进行强制类型转换之后,进行算术运算,将得出的结果再转换成指针 printf("P: %x \n",p); printf("P1: %x\n",p1); p1--; printf("p1 suanshu: %x %d\n",p1,*p1);    //指针支持的算术运算‘--’ 和 '++' p1++; printf("Container_of p = %x %d\n",container_of(p1,struct A,b),*container_of(p1,struct A,b));     //通过container_of得出整个结构体的指针。 return 0; }

    说明:p1-4 打印的地址是:&p->a 也就是 struct *p 的起始地址 (从前两行打印可以看出来);p1的地址是&p->b:440f68;对p1进行‘--’运算使其指向p->a; 通过container_of得出整个结构体的指针。

     :(2012年6月16日20:21:33):以上程序我是在MinGW编译器下运行的,今天发现在微软的编译器下container_of这个宏得不到正确的解析。

    在来看一个使用二级指针分配空间的示例程序:

    #include <iostream>
    
    #include <iterator>
    
    #include <algorithm>
    
    #include <iomanip>
    
    using namespace std;
     
    #define out cout<<setw(4)
    
     
    int main() 
    {
    
    //int **p =  (int **)new int [10];  //这里和下面的方式都可以
    
       int **p = new int *[10];
    
       int aa = 0;
    
        for(int i = 0; i < 10 ;i++)
    
            p[i] = new int[10]; //由于每次这样分配,每个一维数组的地址内部是连续的,外部不是连续的,所以不能一次memset那么多
    
                           //只有每次使用memset设置一个数组,且memset的特性要注意
    
     
    
       int bb = 0;
    
       int cc = 0xff;
    
        int temp = cc;
    
         int count = 0;
    
     
    
      for(int i = 0; i < 10 ;i++)
    
        for(int j = 0; j < 10 ; j++)
    
          p[i][j] = count++;
    
     
    
    /*while(aa < 10)
    
     {
    
       memset(p[aa++],temp,sizeof(int) * 10);//不要乱用,memset每次将一个byte设置为temp值
    
    }*/
    
     
    
      cout << "Array internal Address : "<<endl;
    
      cout << &p[0][8]<<" "<< p[0][8]<<endl;
    
      cout << &p[0][9]<<" "<< p[0][9]<<endl;
    
      cout << &p[0][10]<<" "<< p[0][10]<<endl;
    
      cout << &p[0][11]<<" "<< p[0][11]<<endl;
    
      cout << &p[0][12]<<" "<< p[0][12]<<endl;
    
     
    
      cout<<"Array external Address:  "<<endl;
    
      cout << &p[0][0]<<" "<< p[0][0]<<endl;
    
      cout << &p[1][0]<<" "<< p[1][0]<<endl;
    
      cout << &p[2][0]<<" "<< p[2][0]<<endl;
    
     
    
      cout<<"For loop: "<<endl;
    
    /*
    
    //注意和二维数组的区分!
    
        for(int i = 1; i <= 100; i++)  //这样来打印全部是不行的!
    
        {
    
            cout<< p[0][i-1] << " ";
    
            if(i % 10 == 0 && i != 0)
    
                cout << endl;
    
        }
    
    */
    
     
    
      for(int i = 0; i < 10; i++)
      {
    
        for(int j = 0; j < 10 ; j++)
        {
    
          cout<<setw(4) << p[i][j] << " ";
    
        }
    
        cout << endl;
    
       }
    
     
    
      cout<<"STL Copy: "<<endl;
    
      int a = 0;
     
      while(a < 10)
        {
    
        copy(*p,*p+10,ostream_iterator<int>(cout,"   "));
    
        //这里必须使用*p,我开始一直写错,可以参见copy的实现
    
        cout << endl;
    
        a++;
    
        p++; //这里就注意的每次‘++’就会跳到下一个“数组”的头, '++'直接是跳转到了下一个“对象”引用的位置
    }
    
      return 0;
    
    }

    注意:这里虽然使用二级指针分配了一个类似二维数组的空间,请注意和二维数组进行区别。

    memset的用法:

    void * memset ( void * ptr, int value, size_t num );

    Fill block of memory

    Sets the first num bytes of the block of memory pointed by ptr to the specified value (interpreted as an unsigned char).

    上面程序中我试图使用memset对分配出来的空间进行初始化,但是请注意memset初始化是按字节来的,如果初始化的结果不是0,结果将会非常意外,比如初始化的结果为10,则最后每个元素的值为:0x0a0a0a0a, %d打印的结果为:168430090。而且上面使用二级指针分配的空间并非连续,想要使用memset初始化的话,必须进行多次,也就说按内纯分配的次数来初始化。

    :(copy的实现)

    template<class InputIterator, class OutputIterator>

      OutputIterator copy ( InputIterator first, InputIterator last, OutputIterator result )

    {

      while (first!=last) *result++ = *first++;

      return result;

    }

    复杂函数指针

    Think in C++中介绍了一种复杂函数指针的分析方法:

    右左法则:首先从最里面的圆括号看起,然后往右看,再往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程直到整个声明解析完毕。

    To define a pointer to a function that has no arguments and no

    return value, you say:

    void (*funcPtr)();

    funcPtr是一个函数指针,该指针指向的函数没有参数,没有返回值。

    复杂函数申明:

    void * (*(*fp1)(int))[10];

    解释:

    “fp1 is a pointer to a function that takes an integer argument and returns a pointer to an array of 10 void pointers.”

    fp1是一个指向函数的指针,该函数拥有一个int型的参数,返回值是一个指向拥有10元素的指针数组,数组中每个元素都是void * 类型。

     float (*(*fp2)(int,int,float))(int);

    解释:

    fp2 is a pointer to a function that takes three arguments (int, int, and float) and returns a pointer to a function that takes an integer argument and returns a float

    fp2是一个指向函数的指针,该函数拥有三个参数,分别是int,int和float类型,返回一个指向函数的指针,该函数拥有一个int型的参数,返回值是float类型。

     typedef double (*(*(*fp3)())[10])();

     fp3 a;

    解释:

     fp3 is a pointer to a function that takes no arguments and returns a pointer to an array of 10 pointers to functions that take no arguments and return doubles.

     fp3 是一个指向函数的指针,该函数没有参数,返回一个指向拥有十个元素的数组,数组的每个元素是一个函数,这10个函数都没有参数,返回值是double类型。

    int (*(*f4())[10])();

    解释:

    f4 is a function that returns a pointer to an array of 10 pointers to functions that return integers.

    f4是一个函数,返回值是一个指向拥有10个元素的数组,每个数组元素是一个函数指针,这些指针指向的函数返回值为int类型。

    函数如何返回一个数组? (C编程专家P230)

    有了上面的分析可以简单构造为:

    int (*func())[20]

    func是一个函数,它的返回值是一个指向20个int元素的数组的指针。

    函数里面可以定义一个指向20 int 元素的数组的指针:

    int (*p)[20];

    然后动态分配内存之后,在函数结尾就可以返回了。

    注意和这个区别:int *p[20] : p是一个拥有20个元素的数组,数组中的每个元素是一个指向int型的指针。

  • 相关阅读:
    ES6深入浅出-5 新版对象-1.如何创建对象
    ES6深入浅出-4 迭代器与生成器-5.科班 V.S. 培训
    ES6深入浅出-4 迭代器与生成器-4.总结
    ES6深入浅出-4 迭代器与生成器-3.生成器 & for...of
    ES6深入浅出-4 迭代器与生成器-2.Symbol 和迭代器
    Spring cloud微服务安全实战-3-2 第一个API及注入攻击防护
    Spring cloud微服务安全实战-3-1 API安全 常见的安全机制
    Spring Cloud微服务安全实战- 2-1 环境安装
    Spring cloud微服务安全实战_汇总
    ES6深入浅出-4 迭代器与生成器-1.字面量增强
  • 原文地址:https://www.cnblogs.com/zhuyp1015/p/2538959.html
Copyright © 2011-2022 走看看