Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1317520
  • 博文数量: 168
  • 博客积分: 2124
  • 博客等级: 大尉
  • 技术积分: 2590
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-16 23:51
文章分类

全部博文(168)

文章存档

2014年(6)

2013年(74)

2012年(71)

2011年(17)

分类: C/C++

2011-11-13 15:26:00

这是个老生常谈的问题,但是似乎总是犯错,(今天还犯了一个),再学习一下。
 
 
~~如何将数值存储到指定的内存地址
 
设现在需要往内存0x12ff7c 地址上存入一个整型数0x100。我们怎么才能做到呢?
 
  1. 1.int *p = (int *)0x12ff7c;
  2. *p = 0x100;
  3. 2.*(int *)0x12ff7c = 0x100;//其实这和上面没有本质区别~
 
 
~~一个小问题,
  1. int a[5];
  2. sizeof(a[5]);//对嘛?

虽然并不存在a[5]这个元素,但是这里也并没有去真正访问a[5],而是仅仅根据数组元素的类型来确定其值。所以这里使用a[5]并不会出错。

~~

  1. int main()
  2. {
  3. int a[5]={1,2,3,4,5};
  4. int *ptr=(int *)(&a+1);
  5. printf("%d,%d",*(a+1),*(ptr-1));
  6. }

 

对指针进行加1 操作,得到的是下一个元素的地址,而不是原有地址值直接加1。就是在原有地址上加上一个元素的大小。

&a + 1: 取数组a 的首地址,该地址的值加上sizeof(a) 的值,即&a + sizeof(a),也就是下一个数组的首地址,显然当前指针已经越过了数组的界限.(&a可以认为是指向比a高一级的数组的指针,那么&a+1,显然就是&a加上其“元素大小”)

(&a可认为是比a高一级的指针,是指向数组的指针,假如要赋值的话,先定义char ** p;p = &a;或者可认为&a是一个指向指针的指针)

(int *)(&a+1): 则是把上一步计算出来的地址,强制转换为int * 类型,赋值给ptr。


*(a+1): a,&a 的值是一样的,但意思不一样,a 是数组首元素的首地址,也就是a[0]的
首地址,&a 是数组的首地址,a+1 是数组下一元素的首地址,即a[1]的首地址,&a+1 是下一
个数组的首地址。所以输出2


*(ptr-1): 因为ptr 是指向a[5],并且ptr 是int * 类型,所以*(ptr-1) 是指向a[4] ,
输出5。

在Visual C++6.0编译器里,假如用Watch窗口看,a+1是我们想要的结果,但是&a+1并不是的,所以我们就打印出来看,所以我们可以认为这是编译器的一个小BUG。

 

~~p 的值为0x100000

(unsigned long)p + 0x1 = 0x___?
(unsigned int*)p + 0x1 = 0x___?

(unsigned long)p + 0x1 的值呢?这里涉及到强制转换(转成int,unsigned,long等各种整形数,都是数~!!),将指针变量p 保存的值强制转换
成无符号的长整型。任何数值一旦被强制转换,其类型就改变了。所以这个表达式其实就
是一个无符号的长整型数加上另一个整数。所以其值为:0x100001。(一个数加上另一个数,不要与整形数组元素加上一个数混了,这里是强制装换!!!)


(unsigned int*)p + 0x1 的值呢?这里的p 被强制转换成一个指向无符号整型的指针。所
以其值为:0x100000+sizof(unsigned int)*0x1,等于0x100004。

 

看这样一个例子

 

  1. intmain()
  2. {
  3. int a[4]={1,2,3,4};
  4. int *ptr1=(int *)(&a+1);
  5. int *ptr2=(int *)((int)a+1);
  6. printf("%x,%x",ptr1[-1],*ptr2);
  7. return 0;
  8. }

 

有了以上的基础在这里我们要注意的就是判断系统的大小端

 

可以用函数

 

  1. int checkSystem( )
  2. {
  3. union check
  4. {
  5. int i;
  6. char ch;
  7. } c;
  8. c.i = 1;
  9. return (c.ch ==1);
  10. }

如果当前系统为大端模式这个函数返回0;如果为小端模式,函数返回1。
也就是说如果此函数的返回值为1 的话,*ptr2 的值为0x2000000。
如果此函数的返回值为0 的话,*ptr2 的值为0x100。

~~函数传递数组的问题

 

  1. void fun(char a[10])
  2. {
  3. int i = sizeof(a);
  4. char c = a[3];
  5. }


如果数组b 真正传递到函数内部,那i 的值应该为10。但是我们测试后发现i 的值竟然
为4!为什么会这样呢?难道数组b 真的没有传递到函数内部?是的,确实没有传递过去,
这是因为这样一条规则:
C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元
素首地址的指针。

 

~~~能否把指针变量本身传递给一个函数?

 

  1. void fun(char *p)
  2. {
  3. char c = p[3];//或者是char c = *(p+3);
  4. }
  5. int main()
  6. {
  7. char *p2 = “abcdefg”;
  8. fun(p2);
  9. return 0;
  10. }

这个函数调用,真的把p2 本身传递到了fun 函数内部吗?我们知道p2 是main 函数内的一个局部变量,它只在main 函数内部有效。

(这里需要澄清一个问题:main 函数内的变量不是全局变量,而是局部变量,只不过它的生命周期和全局变量一样长而已。全局变量一定是定义在函数外部的。初学者往往弄错这点。)

既然它是局部变量,fun 函数肯定无法使用p2 的真身。那函数调用怎么办?好办:对实参做一份拷贝并传递给被调用的函数。即对p2 做一份拷贝,假设其拷贝名为_p2。那传递到函数内部的就是_p2 而并非p2 本身。(那么我们怎么传递呢,1.用地址 2.用返回值,上个帖子说过这个问题)

~~~二维数组参数与二维指针参数


前面详细分析了二维数组与二维指针,那它们作为参数时与不作为参数时又有什么区别呢?看例子:
void fun(char a[3][4]);

C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。这条规则并不是递归的,也就是说只有一维数组才是如此,当数组超过一维时,将第一维改写为指向数组首元素首地址的指针之后,后面的维再也不可改写。比如:a[3][4][5]作为参数时可以被改写为(*p)[4][5]。

传参时的等效

      数组参数                    等效的指针参数

数组   :   char a[10]           指针   : char * p;
数组的数组:char a[3][4]         数组的指针:char (*p)[4]
指针数组: char *a[5]            指针的指针:char **p

 

~~~

函数指针

A),char * (*fun1)(char * p1,char * p2);
B),char * *fun2(char * p1,char * p2);
C),char * fun3(char * p1,char * p2);

后两个都不难理解,那么第一个是什么呢?

这里fun1 不是什么函数名,而是一个指针变量,它指向一个函数。这个函数有两个指针类型的参数,函数的返回值也是一个指针。

函数指针使用的例子
上面我们定义了一个函数指针,但如何来使用它呢?先看如下例子:

  1. #include
  2. #include
  3. char * fun(char * p1,char * p2)
  4. {
  5. int i = 0;
  6. i = strcmp(p1,p2);
  7. if (0 == i)
  8. {
  9. return p1;
  10. }
  11. else
  12. {
  13. return p2;
  14. }
  15. }
  16. int main()
  17. {
  18. char * (*pf)(char * p1,char * p2);
  19. pf = &fun;
  20. (*pf) ("aa","bb");//注意这的调用方式,同样用pf("aa","bb")也行
  21. return 0;
  22. }


这里需要注意到是,在Visual C++6.0 里,给函数指针赋值时,可以用&fun 或直接用函数名fun。这是因为函数名被编译之后其实就是一个地址,所以这里两种用法没有本质的差别。

 

~~函数指针数组


现在我们清楚表达式“char * (*pf)(char * p)”定义的是一个函数指针pf。既然pf 是一个指针,那就可以储存在一个数组里。把上式修改一下:char * (*pf[3])(char * p);

 

    char * fun1(char * p);
    char * fun2(char * p);
    char * fun3(char * p);
  1. int main()
  2. {
  3. char * (*pf[3])(char * p);
  4. pf[0] = fun1; // 可以直接用函数名
  5. pf[1] = &fun2; // 可以用函数名加上取地址符
  6. pf[2] = &fun3;
  7. pf[0]("fun1");
  8. pf[0]("fun2");//注意用函数指针调用函数的方法
  9. pf[0]("fun3");
  10. return 0;
  11. }

~~~函数指针数组的指针

char * (*a[3])(char * p);
char * (*(*pf)[3])(char * p);
pf = &a;

这里的关系就相当于定义一个数组,再定义一个指向数组的指针。当然是这样了

 

      char * fun1(char * p);
      char * fun2(char * p);
      char * fun3(char * p);
  1. int main()
  2. {
  3. char * (*a[3])(char * p);
  4. char * (*(*pf)[3])(char * p);
  5. pf = &a;
  6. a[0] = fun1;
  7. a[1] = &fun2;
  8. a[2] = &fun3;
  9. pf[0][0]("fun1");
  10. pf[0][1]("fun2");
  11. pf[0][2]("fun3");
  12. return 0;
  13. }

 

 

参考《C语言深度解剖》---林正冲

阅读(1867) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~