频道栏目
首页 > 资讯 > C语言 > 正文

C语言指针声明探秘

13-04-23        来源:[db:作者]  
收藏   我要投稿

前言
我对C指针的理解一直停留在:指针本身是一块内存,它保存了一块内存的地址,可以引用,但是最近在读代码的时候,各种指针的声明搞得我异常苦恼,赶紧去学习了一番,也只是明白了最基本的使用,总结如下。

基本知识
指针的基本使用正如前言中说的,先看这样一段代码:
[cpp]
#include <stdio.h>  
 
int main() 

        int i = 10; 
        int * p; 
        printf("p的地址:%d\n",&p); 
        printf("未初始化时p的内容:%d\n",p); 
//      printf("未初始化访问p指向的内存:%d\n",*p); // 这行代码访问了个野指针,必然发生段错误  
        p = &i; 
        printf("--初始化p完毕--\n"); 
        printf("p里面保存的地址:%d\n",p); 
        printf("p指向的内存的内容:%d\n",*p); 
        printf("p的大小:%d\n",sizeof(p)); 
        printf("p指向的内存大小:%d\n",sizeof(*p)); 
        return 0; 

#include <stdio.h>

int main()
{
        int i = 10;
        int * p;
        printf("p的地址:%d\n",&p);
        printf("未初始化时p的内容:%d\n",p);
//      printf("未初始化访问p指向的内存:%d\n",*p); // 这行代码访问了个野指针,必然发生段错误
        p = &i;
        printf("--初始化p完毕--\n");
        printf("p里面保存的地址:%d\n",p);
        printf("p指向的内存的内容:%d\n",*p);
        printf("p的大小:%d\n",sizeof(p));
        printf("p指向的内存大小:%d\n",sizeof(*p));
        return 0;
}

输出结果为:[plain] view plaincopyprint?p的地址:1439276008 
未初始化时p的内容:0 
--初始化p完毕-- 
p里面保存的地址:1439276020 
p指向的内存的内容:10 
p的大小:8 
p指向的内存大小:4 

p的地址:1439276008
未初始化时p的内容:0
--初始化p完毕--
p里面保存的地址:1439276020
p指向的内存的内容:10
p的大小:8
p指向的内存大小:4

这就是指针的基本使用,可用下图来说明:

 


指针与数组
首先看这两个声明语句:
[cpp]
char (*a) [100]; 
char* a [100]; 

char (*a) [100];
char* a [100];
第一个是声明了一个指向有100个char元素的数组的指针(注意和指向数组首地址的char型指针分开);第二个是声明了一个有100个char*元素的数组,数组里面装的是char *。
为了理解,我们来看这样一段代码:
[cpp]
#include <stdio.h>  
 
int main() 

        int arr[10][100]; 
        printf("sizeof(arr[0]) = %lu\n", sizeof(arr[0])); 
        printf("sizeof(arr[0][0]) = %lu\n", sizeof(arr[0][0])); 
        int *p; 
        int (*q)[100]; 
        p = &arr[0][0]; 
        q = &arr[0]; 
        printf("p = %d\n",p); 
        printf("q = %d\n",q); 
        printf("sizeof((*p)) = %lu\n", sizeof((*p))); 
        printf("sizeof((*q)) = %lu\n", sizeof((*q))); 
        p++; 
        q++; 
        printf("after add 1, p = %d\n", p); 
        printf("after add 1, q = %d\n", q); 
        return 0; 

#include <stdio.h>

int main()
{
        int arr[10][100];
        printf("sizeof(arr[0]) = %lu\n", sizeof(arr[0]));
        printf("sizeof(arr[0][0]) = %lu\n", sizeof(arr[0][0]));
        int *p;
        int (*q)[100];
        p = &arr[0][0];
        q = &arr[0];
        printf("p = %d\n",p);
        printf("q = %d\n",q);
        printf("sizeof((*p)) = %lu\n", sizeof((*p)));
        printf("sizeof((*q)) = %lu\n", sizeof((*q)));
        p++;
        q++;
        printf("after add 1, p = %d\n", p);
        printf("after add 1, q = %d\n", q);
        return 0;
}
这端代码运行后结果如下:
[plain]

sizeof(arr[0]) = 400 
sizeof(arr[0][0]) = 4 
p = 1411443800 
q = 1411443800 
sizeof((*p)) = 4 
sizeof((*q)) = 400 
after add 1, p = 1411443804 
after add 1, q = 1411444200 

sizeof(arr[0]) = 400
sizeof(arr[0][0]) = 4
p = 1411443800
q = 1411443800
sizeof((*p)) = 4
sizeof((*q)) = 400
after add 1, p = 1411443804
after add 1, q = 1411444200
因为内存是线性的,C中所谓的二维数组不过是数组的数组,arr这个数组有10个元素,每个元素是一个长度为100的数组,在程序员的脑子里面,arr是一个有10行100列的二维数组。
代码里的p是一个指向int型的指针,q是一个指向“有100个int的int数组”的指针。所以p和q的初始化方式是不同的,但是开始的时候他们都指向了arr这个数组的数组的首地址(初始时是相等的),但是到后面分别执行自增操作之后,因为它们的类型不同,因此根据指针自增运算的含义,他们移动的步长也不相同,p移动了sizeof(int)个字节,而q移动了sizeof(int[100])个字节,于是它们的值也大不相同,可以用下图来说明:


另外要注意的就是字符二维数组的声明:
[cpp]
include <stdio.h>  
 
int main() 

        char* str[2] = {"liushuai","kobe"}; 
        printf("%s %s\n",str[0],str[1]); 
        return 0; 

#include <stdio.h>

int main()
{
        char* str[2] = {"liushuai","kobe"};
        printf("%s %s\n",str[0],str[1]);
        return 0;
}
输出结果显然:
[plain]
liushuai kobe 

liushuai kobe

以上是合法的字符二维数组的声明,str是一个有两个元素的数组,每个元素的类型是一个char*,结合上面所讲的,应该不难理解。
返回指针的函数和函数指针
来看下面两个声明语句:
[cpp]
int* foo(int i); 

int* foo(int i);
这个应该比较好理解,类比着装有指针的数组的声明char* a[100],这是个函数声明,声明了一个名字为foo的函数,这个函数接受一个类型为int的参数,返回一个指向int型的指针。
再看下面的声明:
[cpp]

void (*bar)(); 

void (*bar)();
类比着数组的声明,这个语句声明了一个指向函数的指针bar,它指向的函数要求返回值为void,且不接受任何参数。这是一个比较简单的函数的函数指针的声明。
函数既然可以返回一个指针,那么一个函数能不能返回一个指向函数的指针呢?答案是肯定的,看,指针是多么灵活。刚刚接触可能会有点不适应,我们来看一个例子:
[cpp]
int (*foo(int)) (double*,char); 

int (*foo(int)) (double*,char);
类比着上面的讲解,我们知道,这个语句声明了一个函数foo,它接受一个int类型的参数,返回一个指向函数的指针,要求指向的函数具有这样的形式:接受一个double类型的指针和char型的变量作为参数,返回一个int类型的值。
我们可以用C中的typedef简化这个声明:
[cpp]
typedef int (*ptf) (double*, char); 
ptf foo(int ); 

typedef int (*ptf) (double*, char);
ptf foo(int );

注意:typedef和#define是不同的,typedef是给“这样”的指针起了一个别名ptf,而不是简单的进行宏替换。
好吧,我们接着来个更变态的,如果一个函数的参数和返回值都是函数指针,那么声明就会更复杂,例如:
[cpp] view plaincopyprint?void (*signal (int sig, void (*func) (int siga)) ) ( int siga ); 

void (*signal (int sig, void (*func) (int siga)) ) ( int siga );
其实慢点分析也不难,我们可以用typedef来简化:
[cpp]
typedef void (*p_sig) (int); 
p_sig signal(int sig, p_sig func); 

typedef void (*p_sig) (int);
p_sig signal(int sig, p_sig func);
signal这个函数的参数func是一个函数指针,返回了一个函数指针,且两种指针要求指向的函数具有同一种形式(接受一个int型的参数,返回空值)。
通过函数指针调用函数
还是通过一个例子来说明问题:
[cpp]
#include <stdio.h>  
 
void printMyName(); 
 
int main() 

        void (*f)(); 
        f = printMyName; 
        f(); 
        f = &printMyName; 
        f(); 
        return 0; 

 
void printMyName() 

        printf("liushuaikobe\n"); 

#include <stdio.h>

void printMyName();

int main()
{
        void (*f)();
        f = printMyName;
        f();
        f = &printMyName;
        f();
        return 0;
}

void printMyName()
{
        printf("liushuaikobe\n");
}
是不是很容易呢。注意用“&函数名”和“函数名”初始化一个函数指针都是合法的,因为C中函数名会被转换为指向这个函数的指针。
指针真是充满智慧的产物,通过函数指针,可以轻松实现面向对象语言中多态等一些高级特性(例如Java的接口,C++的虚函数),真的太美妙了。
对于大神,这些东西可能都是小儿科,但是本人C真的没怎么用过,搞懂了这些,我也很高兴了。
最后送大家一句话:
不要因为走得太远,就忘了自己当初为什么出发。

 

相关TAG标签
上一篇:SQL distinct和group by
下一篇:利用Nmap对MS-SQLSERVER进行渗透
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站