深度解析C语言中的指针、指针数组与函数指针

5星 · 超过95%的资源 需积分: 47 23 下载量 127 浏览量 更新于2024-07-27 4 收藏 86KB DOC 举报
"这篇文档详细介绍了C/C++编程语言中的指针、指针数组、数组指针、指针函数和函数指针的概念及其用法,旨在帮助读者彻底理解这些复杂的数据结构和操作。" 指针是C/C++编程中非常核心的概念,它允许我们直接操作内存地址,从而实现高效的数据操作和复杂的数据结构。以下是对标题和描述中提到的各种指针类型的详细解释: 1. **普通指针**:`int *p;` 定义了一个指向整型变量的指针`p`,它可以存储一个整型变量的内存地址。 2. **指针数组**:`int *p[3];` 定义了一个包含三个元素的数组,每个元素都是一个整型指针。这意味着`p`是一个数组,其中的每个元素都可以指向一个整型变量。 3. **数组指针**:`int (*p)[3];` 这里`p`是一个指针,但它并不直接指向一个整型变量,而是指向一个包含三个整型元素的数组。因此,`p`可以被视为指向数组的指针。 4. **多级指针**:`int **p;` 这是一个二级指针,`p`首先是一个指向指针的指针,意味着`p`可以存储一个指向整型指针的地址,而这个被指向的指针又可以指向一个整型变量。 5. **指针函数**:`int p(int);` 这定义了一个名为`p`的函数,它接受一个整型参数并返回一个整型值。 6. **函数指针**:`int (*p)(int);` `p`是一个指针,但它并不指向一个变量,而是指向一个函数,这个函数接受一个整型参数并返回一个整型值。因此,`p`可以被视为指向函数的指针。 在实际编程中,理解这些概念非常重要,因为它们可以用于实现动态内存分配、回调函数、数据结构(如链表和树)等复杂功能。指针数组和数组指针尤其在处理多个相同类型的数据时非常有用,而函数指针则可以用于传递函数作为参数,实现函数式编程的特性。 需要注意的是,虽然文档中提到了`int*(*p(int))[3];`这种非常复杂的数据类型,但通常在实际编程中并不常见,因为它涉及到函数返回一个指针数组,这在大多数情况下是不必要的复杂。不过,理解这个类型可以帮助开发者了解指针和函数的组合可以达到的灵活性。 彻底理解指针、指针数组、数组指针、指针函数和函数指针是掌握C/C++编程的关键,它们是这些语言的强大工具,也是理解和编写复杂程序的基础。
2011-09-29 上传
指针数组和数组指针的区别 数组指针(也称行指针) 定义 int (*p)[n]; ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。 如要将二维数组赋给一指针,应这样赋值: int a[3][4]; int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。 p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0] p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][] 所以数组指针也称指向一维数组的指针,亦称行指针。 指针数组 定义 int *p[n]; []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。 如要将二维数组赋给一指针数组: int *p[3]; int a[3][4]; for(i=0;i<3;i++) p[i]=a[i]; 这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2] 所以要分别赋值。 这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。 还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。 比如要表示数组中i行j列一个元素: *(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j] 就指向指针的指针,很早以前在说指针的时候说过,但后来发现很多人还是比较难以理解,这一次我们再次仔细说一说指向指针的指针。  先看下面的代码,注意看代码中的注解: #include <iostream>  #include <string>  using namespace std;    void print_char(char* array[],int len);//函数原形声明    void main(void)    {  //-----------------------------段1-----------------------------------------      char *a[]={"abc","cde","fgh"};//字符指针数组      char* *b=a;//定义一个指向指针的指针,并赋予指针数组首地址所指向的第一个字符串的地址也就是abc\0字符串的首地址      cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<<endl;  //-------------------------------------------------------------------------    //-----------------------------段2-----------------------------------------      char* test[]={"abc","cde","fgh"};//注意这里是引号,表示是字符串,以后的地址每加1就是加4位(在32位系统上)      int num=sizeof(test)/sizeof(char*);//计算字符串个数      print_char(test,num);      cin.get();  //-------------------------------------------------------------------------  }    void print_char(char* array[],int len)//当调用的时候传递进来的不是数组,而是字符指针他每加1也就是加上sizeof(char*)的长度  {      for(int i=0;i<len;i++)      {          cout<<*array++<<endl;      }  }   下面我们来仔细说明一下字符指针数组和指向指针的指针,段1中的程序是下面的样子: char *a[]={"abc","cde","fgh"};  char* *b=a;  cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<<endl;   char *a[]定义了一个指针数组,注意不是char[], char[]是不能同时初始化为三个字符的,定义以后的a[]其实内部有三个内存位置,分别存储了abc\0,cde\0,fgh\0,三个字符串的起始地址,而这三个位置的内存地址却不是这三个字符串的起始地址,在这个例子中a[]是存储在栈空间内的,而三个字符串却是存储在静态内存空间内的const区域中的,接下去我们看到了char* *b=a;这里是定义了一个指向指针的指针,如果你写成char *b=a;那么是错误的,因为编译器会返回一个无法将char* *[3]转换给char *的错误,b=a的赋值,实际上是把a的首地址赋给了b,由于b是一个指向指针的指针,程序的输出cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<<endl;   结果是 abc cde fgh   可以看出每一次内存地址的+1操作事实上是一次加sizeof(char*)的操作,我们在32位的系统中sizeof(char*)的长度是4,所以每加1也就是+4,实际上是*a[]内部三个位置的+1,所以*(b+1)的结果自然就是cde了,我们这时候可能会问,为什么输出是cde而不是c一个呢?答案是这样的,在c++中,输出字符指针就是输出字符串,程序会自动在遇到\0后停止.   我们最后分析一下段2中的代码,段2中我们调用了print_array()这个函数,这个函数中形式参数是char *array[]和代码中的char *test[]一样,同为字符指针,当你把参数传递过来的时候,事实上不是把数组内容传递过来,test的首地址传递了进来,由于array是指针,所以在内存中它在栈区,具有变量一样的性质,可以为左值,所以我们输出写成了,cout<<*array++<<endl;当然我们也可以改写为cout<<array[i]<<endl,这里在循环中的每次加1操作和段1代码总的道理是一样的,注意看下面的图!   到这里这两个非常重要的知识点我们都说完了,说归说,要想透彻理解希望读者多动手,多观察,熟能生巧。   下面是内存结构示意图: