指针的运算和普通的算术运算是不同的,支持的运算符也很少,有时候你把两个指针相减得到的结果可能会不如你所想。今天来稍微总结一下:
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 %dn",((int )p1-sizeof(int)),*(int *)((int )p1-sizeof(int)));
//先将指针进行强制类型转换之后,进行算术运算,将得出的结果再转换成指针
printf("P: %x n",p);
printf("P1: %xn",p1);
p1--;
printf("p1 suanshu: %x %dn",p1,*p1);
//指针支持的算术运算‘--’ 和 '++'
p1++;
printf("Container_of p = %x %dn",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
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/archive/2012/06/06/2538959.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/52039
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!