C与指针
一、数组的指针、指针数组以及指向指针的指针
考虑数组的指针的时候我们要同时考虑类型和维数这两个属性。换一句话,就是说一个数组排除在其中存储的数值,那么可以用类型和维数来位置表示他的种类。
1、一维数组
在c和c++中数组的指针就是数组的起始地址(也就第一个元素的地址),而且标准文档规定数组名代表数组的地址(这是地址数值层面的数组表示)。
例如:
int a[10];
int *p;
p=&a[0]//和p=a是等价的
因为a是数组名,所以他是该数组的地址,同时因为第一个元素为a[0],那么&a[0]也代表了该数组的地址。但是我们是不是就说一个数组名和该数组的第一个元素的&运算是一回事呢?在一维的时候当时是的,但是在高维的时候,我们要考虑到维数给数组带来的影响。
a[10]是一个数组,a是数组名,它是一个包含10个int类型的数组类型,不是一般的指针变量噢!(虽然标准文档规定在c++中从int[]到int*直接转换是可以的,在使用的时候似乎在函数的参数为指针的时候,我们将该数组名赋值没有任何异样),a代表数组的首地址,在数字层面和a[10]的地址一样。这样我们就可以使用指针变量以及a来操作这个数组了。
所以我们要注意以下问题:
(1) p[i]和a[i]都是代表该数组的第i+1个元素;
(2) p+i和a+i代表了第i+1个元素的地址,所以我们也可以使用 *(p+I)和*(a+I)来引用对象元素;
(3)p+1不是对于指针数量上加一,而是表示从当前的位置跳过当前指针指向类型长度的空间,对于win32的int为4byte;
a[10]是一个数组,a是数组名,它是一个包含10个int类型的数组类型,不是一般的指针变量噢!(虽然标准文档规定在c++中从int[]到int*直接转换是可以的,在使用的时候似乎在函数的参数为指针的时候,我们将该数组名赋值没有任何异样),a代表数组的首地址,在数字层面和a[10]的地址一样。这样我们就可以使用指针变量以及a来操作这个数组了。
所以我们要注意以下问题:
(1) p[i]和a[i]都是代表该数组的第i+1个元素;
(2) p+i和a+i代表了第i+1个元素的地址,所以我们也可以使用 *(p+I)和*(a+I)来引用对象元素;
(3)p+1不是对于指针数量上加一,而是表示从当前的位置跳过当前指针指向类型长度的空间,对于win32的int为4byte;
2、多维数组
对于二维数组a[4][6];由于数组名代表数组的起始地址,所以a(第一层)和第一个元素a[0][0]地址的数字是相同的,但是意义却是不同的。对于该数组我们可以理解为:a的一维数组(第一层),它有四个元素a[0]、a[1]、a[2]、a[3](第二层),而每个元素又含有6个元素a[0][0],a[0][1],a[0][2],a[0][3],a[0][4],a[0][5](第三层),…到此我们终于访问到了每个元素了,这个过程我们经历了:a->a[0]->a[0][0]。
整体来讲:a是一个4行5列的二维数组,a表示它指向的数组的首地址(第一个元素地址&a[0]),同时a[0]指向一行,它是这个行的名字(和该行的第一个元素的首地址相同(第一个元素为地址&a[0][0]))。所以从数字角度说:a、a[0]、&a[0][0]是相同的,但是他们所处的层次是不同的。
既然a代表二维数组,那么a+i就表示它的第i+1个元素*(a+i)的地址,而在二维数组中*(a+i)又指向一个数组,*(a+i)+j表示这个数组的第j+1个元素的地址,所以要访问这个元素可以使用 *(*(a+i)+j)(也就是a[i][j])。
整体来讲:a是一个4行5列的二维数组,a表示它指向的数组的首地址(第一个元素地址&a[0]),同时a[0]指向一行,它是这个行的名字(和该行的第一个元素的首地址相同(第一个元素为地址&a[0][0]))。所以从数字角度说:a、a[0]、&a[0][0]是相同的,但是他们所处的层次是不同的。
既然a代表二维数组,那么a+i就表示它的第i+1个元素*(a+i)的地址,而在二维数组中*(a+i)又指向一个数组,*(a+i)+j表示这个数组的第j+1个元素的地址,所以要访问这个元素可以使用 *(*(a+i)+j)(也就是a[i][j])。
3、指针数组
一个指针数组是指一个数组中的每个元素都是一个指针,例如:
int *p[10];//而不能是int (*p)[10]
4、指针的指针
一个指针变量内部可以存储一个值,这个值是另外一个对象的地址,所以我们说一个指针变量可以指向一个普通变量,同样这个指针变量也有一个地址,也就是说有一个东西可以指向这个指针变量,然后再通过这个指针变量指向这个对象。那么如何来指向这个指针变量呢?由于指针变量本身已经是一个指针了(右值),那么我们这里就不能用一般的指针了,需要在指针上体现出来这些特点,我们需要定义指针的指针(二重指针)。
int *p1=&i;
int**p2=&p1;
综合以上的所有点,下面是我们常常看到一些匹配(也是经常出错的地方):
int a[3],b[2][3],c,*d[3];
void fun1(int *p);
void fun2(int (*p)[3]);
void fun3(int **p);
void fun4(int p[3]);
void fun5(int p[]);
void fun6(int p[2][3]);
void fun7(int (&p)[3]);
二、函数指针、函数的指针参数以及返回指针的函数
1、函数指针
C++规定,一个函数的地址就是这个函数的名字。我们需要指出的就是一个指针需要指定类型是为了后来的指针解析时候使用,通过指针有效快速访问对象。那么对于函数的指针,它要表示出该函数的那些特性才能满足解析的唯一性呢?答案就是一个函数的特性有它的参数列表和返回类型。
下面是一个函数指针的例子:
不能是:
这样就变成了返回指针的函数声明了。
int (*p)(int I,int j);
不能是:
int *p(int I,int j);
这样就变成了返回指针的函数声明了。
char* GetMemory()
{
char *p = new char[100];
return p;
}
void main()
{
char *str = GetMemory();
delete [] str; //防止内存泄露!
}
在C++中处于对安全性的考虑,指针和它指向的对象要类型一致,也就说上面的指针所指向的函数的特性要和它一模一样:例如指向int min(int I,int j);是可以的。但是指向int min(double I ,double j);是不可以。函数指针也和其他的指针一样,在使用的时候很怕发生"悬空",所以在使用的时候同样要判断有效性,或者在定义的时候就初始化。
2、函数的指针参数
函数指针变量是一个变量,可以作为某个函数的参数来使用的。
void GetMemory(char* p)
{
char *p = new char[100];
}
void main()
{
char *str;
GetMemory(str);
strcpy(str, "hi"); // str = NULL
}
3、返回指针的函数
char* GetMemory()
{
char *p = new char[100];
return p;
}
void main()
{
char *str = GetMemory();
delete [] str; //防止内存泄露!
}
三、typedef
typedef struct t_node {
int Value;
struct t_node *next;
} Node;
typedef strcut t_node Node;
struct t_node {
int Value;
Node *next;
};
typedef int MyIntArray [100];
MyIntArray ia;
int ia[100];
===================================================================================
结束。