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型的指针。

原文地址:https://www.cnblogs.com/zhuyp1015/p/2538959.html