【C语言学习趣事】_32_平胸的尴尬,嫁不出去的姑娘

  为什么写这篇文章呢? 为什么要弄这么个题目呢?

  首先解释为什么用这个题目。这一切都要从那天在QQ群中的讨论说起,那天在群中,一个哥们问了一个关于(void)0 的问题。然后大家说到了

(void)0和(void*)0; 大家看看(void)0 和(void*)0 ,是不是一个像个平胸的小妹,一个像个丰韵的美女。

  他问的的问题如下:

(void)0//这个语句在C语句中可以执行吗?  

  我想了一下这个语句应该没有什么问题,在没有用(void)0,去影响内存对象的情况下,应该是可以的。下面是我在FC 14中进行测试的结果。

[volcanol@volcanol c]$ ls
a.out  test.c
[volcanol@volcanol c]$ cat test.c 
#include <stdio.h>

int main(int argc,char** argv)
{
   int test;

   test=0;

   (void)0;

   getchar();

   return 0;
}

[volcanol@volcanol c]$ gcc -Wall test.c 
[volcanol@volcanol c]$ 

  可以发现,编译过程没有警告和错误信息。

        然后,我们就讨论是否可以将(void)0 赋值给其他的对象,于是我就修改了一下,将其改成赋值。

[volcanol@volcanol c]$ cat -n test.c 
     1    #include <stdio.h>
     2    
     3    int main(int argc,char** argv)
     4    {
     5       int test;
     6    
     7       test=(void)0;
     8    
     9       getchar();
    10    
    11       return 0;
    12    }
    13    

       编译输出信息:

[volcanol@volcanol c]$ gcc -Wall test.c 
test.c: 在函数‘main’中:
test.c:7:8: 错误:void 值未如预期地被忽略

     可以看出,void类型不能通过隐式类型转换为int类型。

      既然我们不能将void类型的值赋值,那么定义void类型的变量呢?

[volcanol@volcanol c]$ cat -n test.c 
     1    #include <stdio.h>
     2    
     3    int main(int argc,char** argv)
     4    {
     5       void  test;
     6    
     7       test=(void)0;
     8    
     9       getchar();
    10    
    11       return 0;
    12    }
    13    

     编译器输出信息如下所示:     

[volcanol@volcanol c]$ gcc -Wall test.c 
test.c: 在函数‘main’中:
test.c:5:10: 错误:变量或字段‘test’声明为 void
test.c:7:8: 错误:void 值未如预期地被忽略

     可以发现,不能将变量定义为void类型。

  下面我们来讨论一下整个过程。

1、void类型

      void表示为无类型,在 K&R C 里面有明确的说明。

      因为不能在Linux下上传图片,所以就不上传图片里,可以到 K&R 第二版的 附录 A.6.7 查看相关的内容。

      K&R C里面明确的指出: void类型对象的值不能以任何方式使用,也不能显式或者隐式的转换为非空类型。void类型表示

一个不存在的值,任何对象转换为void类型都将返回一个不存在的值,因此也就不能将void转换为其他非空类型。

2、void类型的作用

      【1】声明函数不需要返回任何值

        这个需要注意下面两个函数定义的差别:

getx();   //函数1

void getx(); //函数2

       注意:函数1和函数2是两个不一样的函数。

      【2】声明函数不需要传递任何

getx();  //函数1

getx(void); //函数2

        注意:函数1和函数2 一样。

       【3】返回void对象的表达式语句,作用和空语句一样。

(void)x; // 空对象表达式语句

;  //空语句

         在编译的时候,编译优化后,这两个语句都不产生任何实际代码。

3、void* 类型

      指向任何对象的指针都可以转换为void* 类型,而且在转换过程中不会丢失信息;一个被转换为void* 类型的指针可以转换为初始类型,

并且在转换的过程中不会丢失信息。

char *p="Hello world"char *pVoid = (void*)p;

  下面是我的测试代码:

[volcanol@volcanol c]$ cat -n test.c 
     1    #include <stdio.h>
     2    
     3    int main(int argc,char** argv)
     4    {
     5       char *p="Hello world";
     6    
     7       char* pVoid=(void*)p;
     8    
     9       puts(pVoid);
    10    
    11       getchar();
    12    
    13       return 0;
    14    }
    15    
[volcanol@volcanol c]$ gcc -Wall test.c 
[volcanol@volcanol c]$ ./a.out
Hello world

          可以看到,这样的结果符合原本的定义。  分析代码中的第7行语句, 首先将p指针显式的转换为(void*);  然后再将空类型指针(void*)p隐式的转换为char*, 可以看到

这个过程没有丢失信息。

           这里我们可以看到: 挺拔的void*可以任意的和别人交往,而平胸的void成了嫁不出去的姑娘。

4、关于void* 变量和常量

      在C语言库中,预定义了一个常量:NULL; 通常是如下定义的:

#define  NULL  ((void*)0)

      这个常量可以赋值给任意类型的指针。例如:

char* p=NULL;

     这个语句在编译时不会出错,当然如果程序中不再对p进行再赋值,这个指针p通常也不能进行操作,例如此时不能用于puts(p);

     这里还需要说一个特别的事情,就是关于0这个常量在C 语言中的应用,这个在获取结构体变量的成员offset地址非常有用。

[volcanol@volcanol c]$ cat -n test.c 
     1    #include <stdio.h>
     2    
     3    struct testType
     4    {
     5      int x;
     6      char y;
     7      struct testType *z;
     8    };
     9    
    10    int main(int argc,char** argv)
    11    {
    12       
    13       int x=((size_t)&((struct testType*)0)->y);
    14    
    15       printf("%d",x);   
    16    
    17       getchar();
    18    
    19       return 0;
    20    }
    21    
[volcanol@volcanol c]$ gcc -Wall test.c 
[volcanol@volcanol c]$ ./a.out
4

      这里内存地址0,可以引用为任意指针类型,这个内存地址是不允许应用程序引用的,这也就是如果将一个指针赋值为NULL后不能引用的原因。

     关于void* 变量,在网络上盛传一篇文章: http://wenku.baidu.com/view/22c4b8d86f1aff00bed51edc.html,

     这篇文章中对void* 指针某些描述,实际上是用某个平台的某个编译的特殊情况来描述的,存在一些偏颇之处,例如文章中说

   但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包容“无类型”。道理很简单,
我们可以说“男人和女人都是人”,但不能说“人是男人”或者“人是女人”。下面的语句编译出错:

    而且和多人还对里面的说法深信不疑, http://www.cnblogs.com/ComputerG/archive/2012/02/22/2363165.html

    下面的这个链接对void* 指针的描述比较详细:  http://learn.akae.cn/media/ch23s01.html

    我们可以看下面的代码和执行情况:

[volcanol@volcanol c]$ cat -n test.c 
     1    #include <stdio.h>
     2    
     3    int main(int argc,char** argv)
     4    {
     5       char *p="Hello world";
     6    
     7       void* pVoid=(void*)p;
     8    
     9       puts(pVoid);
    10    
    11       getchar();
    12    
    13       return 0;
    14    }
    15    
[volcanol@volcanol c]$ gcc -Wall test.c 
[volcanol@volcanol c]$ ./a.out
Hello world

       实际上: void* 指针也是一个指针,具有普通指针的某些特性,例如  sizeof(void *)具有和sizeof(char *)一样的大小,也就是说,void* 变量同样占用4个字节(32位系统),这个

内存位置,同样可以存储一个内存地址。而设置void*的一个目的就是无需进行显式地址转换就可以将void* 指针转换为其他任意指针, 因为最初的时候,是用char* 做这个功能的。

这样每一次进行转换的时候,都必须将char* 指针进行强制类型转换。

 

      这个地方,就不再细说了,在C语言中,(void)0注定有平胸姑娘的尴尬,成了嫁不出去的姑娘。

     

      文章的内容,均为一孔之见,如果有不同意见,欢迎拍砖。 另外,前两次有朋友说要我的电子书,这里我不太方便上传,如果您需要电子书,可以加我QQ,我的QQ在博客园

中的个人信息里面添了,加的时候请说明是要电子书的,我会根据您的需要给你传,要我上传,我的书太多(达50G之多,很多种类的,不只是计算机的,有需要的都可以索要)。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值