tag 标签: c/c++

相关博文
  • 热度 20
    2015-3-25 09:09
    1058 次阅读|
    0 个评论
    【转自http://blog.csdn.net/geekcome/article/details/6249151】 void 的含义 void 即“无类型”, void * 则为“无类型指针”,可以指向任何数据类型。 void 指针使用规范 ① void 指针可以指向任意类型的数据,亦即可用任意数据类型的指针对 void 指针赋值。例如: int * pint; void *pvoid; pvoid = pint; /*  不过不能  pint= pvoid; */ 如果要将 pvoid 赋给其他类型指针,则需要强制类型转换如: pint= (int *)pvoid; ②在 ANSIC 标准中,不允许对 void 指针进行算术运算如 pvoid++ 或 pvoid+=1 等,而在 GNU 中则允许,因为在缺省情况下, GNU 认为 void * 与 char * 一样。 sizeof(*pvoid )== sizeof( char). void 的作用 ①对函数返回的限定。 ②对函数参数的限定。 当函数不需要返回值时,必须使用 void 限定。例如:  void func(int, int); 当函数不允许接受参数时,必须使用 void 限定。例如:  int func(void) 。 由于 void 指针可以指向任意类型的数据,亦即可用任意数据类型的指针对 void 指针赋值,因此还可以用 void 指针来作为函数形参,这样函数就可以接受任意数据类型的指针作为参数。例如: void * memcpy( void *dest, const void *src, size_t len ); void * memset( void * buffer, int c, size_t num); ------------------------------------------------------------------------------   1. 综述 许多初学者对 C/ C++ 语言中的 void 及 void 指针类型不甚理解,因此在使用上出现了一些错误。本文将对 void 关键字的深刻含义进行解说,并详述 void 及 void 指针类型的使用方法与技巧。 2.void 的含义    void 的字面意思是“无类型”, void* 则为“无类型指针”, void* 可以指向任何类型的数据。 void 几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个 void 变量,让我们试着来定义: void a;   这行语句编译时会出错,提示“ illegal use of type ’ void ’”。不过,即使 voida 的编译不会出错,它也没有任何实际意义。    void 真正发挥的作用在于:   ( 1 )对函数返回的限定;   ( 2 )对函数参数的限定。    众所周知,如果指针 p1 和 p2 的类型相同,那么我们可以直接在 p1 和 p2 间互相赋值;如果 p1 和 p2 指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。   例如: float * p1; in t* p2; p1 = p2;   其中 p1 = p2 语句会编译出错,提示“’ = ’ :cannotconvertfrom ’ int * ’ to ’ float * ’”,必须改为: p1=(float*)p2; 而 void* 则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换: void * p1; int * p2; p1 = p2;   但这并不意味着, void* 也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包容“无类型”。道理很简单,我们可以说“男人和女人都是人”,但不能说“人是男人”或者“人是女人”。下面的语句编译出错: void * p1; int * p2; p2 = p1;   提示“’ = ’ :cannotconvertfrom ’ void* ’ to ’ int* ’”。 3.void 的使用   下面给出 void 关键字的使用规则:    规则一如果函数没有返回值,那么应声明为 void 类型   在 C 语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。但是许多程序员却误以为其为 void 类型。例如: add(inta,intb) { return a+b; } int main(int argc,char * argv 规则二如果函数无参数,那么应声明其参数为 void   在 C++ 语言中声明一个这样的函数: int function(void) { return1; }   则进行下面的调用是不合法的: function(2);   因为在 C++ 中,函数参数为 void 的意思是这个函数不接受任何参数。 我们在 TurboC2.0 中编译: #include"stdio.h" fun() { return1; } main() { printf(/"%d/",fun(2)); getchar(); }   编译正确且输出 1 ,这说明,在 C 语言中,可以给无参数的函数传送任意类型的参数,但是在 C++ 编译器中编译同样的代码则会出错。在 C++ 中,不能向无参数的函数传送任何参数,出错提示“’ fun ’ :functiondoesnottake1parameters ”。   所以,无论在 C 还是 C++ 中,若函数不接受任何参数,一定要指明参数为 void 。    规则三小心使用 void 指针类型   按照 ANSI(AmericanNationalStandardsInstitute) 标准,不能对 void 指针进行算法操作,即下列操作都是不合法的: void * pvoid; pvoid ++;//ANSI :错误 pvoid += 1;//ANSI :错误 ANSI 标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。例如: int * pint; pint ++;//ANSI :正确 pint++ 的结果是使其增大 sizeof(int) 。 但是大名鼎鼎的 GNU(GNU ’ sNotUnix 的缩写 ) 则不这么认定,它指定 void *  的算法操作与 char *  一致。 因此下列语句在 GNU 编译器中皆正确: pvoid ++;//GNU :正确 pvoid += 1;//GNU :正确    pvoid++ 的执行结果是其增大了 1 。   在实际的程序设计中,为迎合 ANSI 标准,并提高程序的可移植性,我们可以这样编写实现同样功能的代码: void * pvoid; (char*)pvoid ++;//ANSI :正确; GNU :正确 (char*)pvoid += 1;//ANSI :错误; GNU :正确    GNU 和 ANSI 还有一些区别,总体而言, GNU 较 ANSI 更“开放”,提供了对更多语法的支持。但是我们在真实设计时,还是应该尽可能地迎合 ANSI 标准。    规则四如果函数的参数可以是任意类型指针,那么应声明其参数为 void*   典型的如内存操作函数 memcpy 和 memset 的函数原型分别为: void * memcpy(void*dest,constvoid*src,size_tlen); void * memset(void*buffer,intc,size_tnum);   这样,任何类型的指针都可以传入 memcpy 和 memset 中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。如果  memcpy 和 memset 的参数类型不是 void* ,而是 char* ,那才叫真的奇怪了!这样的 memcpy 和 memset 明显不是一个“纯粹的,脱离低级趣味的”函数! 下面的代码执行正确: // 示例: memset 接受任意类型指针 int intarray ; memset(intarray,0,100*sizeof(int));// 将 intarray 清 0 // 示例: memcpy 接受任意类型指针 int intarray1 ,intarray2 ; memcpy(intarray1,intarray2,100*sizeof(int));// 将 intarray2 拷贝给 intarray1   有趣的是, memcpy 和 memset 函数返回的也是 void* 类型,标准库函数的编写者是多么地富有学问啊!    规则五 void 不能代表一个真实的变量   下面代码都企图让 void 代表一个真实的变量,因此都是错误的代码: void a;// 错误 function(void a);// 错误    void 体现了一种抽象,这个世界上的变量都是“有类型”的,譬如一个人不是男人就是女人(还有人妖?)。    void 的出现只是为了一种抽象的需要,如果你正确地理解了面向对象中“抽象基类”的概念,也很容易理解 void 数据类型。正如不能给抽象基类定义一个实例,我们也不能定义一个 void (让我们类比的称 void 为“抽象数据类型”)变量。 4. 总结   小小的 void 蕴藏着很丰富的设计哲学,作为一名程序设计人员,对问题进行深一个层次的思考必然使我们受益匪浅。
  • 热度 18
    2014-4-27 19:30
    1687 次阅读|
    0 个评论
    1.下载MinGW:http://ishare.iask.sina.com.cn/f/23746562.html?retcode=0 2.安装MinGW; 3.添加环境变量中的Path值等于MinGW的安装路径C:\MinGW\bin 4.运行CMD; 5.切换到源文件路径,键入g++ hello_world.cpp -o hello_world.  
  • 热度 18
    2014-4-26 23:21
    1303 次阅读|
    0 个评论
    我在电脑是安装了VS2010。在用命令行调用cl.exe来编译cpp文件时遇到如题的问题。最后查了网络资料。解决方法如下: 1直接从C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\下复制“msobj80.dll,mspdb80.dll,mspdbcore.dll,mspdbsrv.exe”这四个文件到VC/Bin/下;   2 添加系统变量(Path),这样:我的电脑-属性-高级-环境变量-系统变量,在path中添加cl.exe所在的路径C:\Program Files\Microsoft Visual Studio 9.0\VC\bin;,注意结尾最后用“;”隔开!   3在cmd中切换到cpp文件路径,键入cl xx.cpp (其中xx为文件名)
  • 热度 17
    2012-7-17 10:15
    1813 次阅读|
    1 个评论
      有如下的两个结构体: struct A                                       struct B          {                                                {  int a;                                           int a;  unsigned __int64 b;                      short c;     short c;                                       unsigned __int64 b; };                                               };   那么 sizeof(A) 和 sizeof(B) 一样吗?让我在编译器里试一下,啊 ? 怎么不一样?两个结构体明明相同,只是第二和第三个成员变量的位置颠倒了结果却大相径庭。到底是因为什么呢? 答案是编译器的数据对齐方式在作怪。以 vc6.0 为例,默认情况下的对其方式是 8 位。所以 struct A 的大小为 24 , struct B 的大小为 16, 下面就具体分析一下数据空间占用情况。 在 Struct A 中的 , 编译器首先检测所有的成员变量中的 size 最大值。很显然 unsigned __int64 最大, sizeof(unsigned __int64) 为 8 ,然后第一个变量 a 为 int 型只占 4 个字节,但是为了对齐其被补上了四个字节 , 接着变量 b 在 变量 a 有效位置之后被放置,但是目前只有 4 个空闲的字节,根本放不下变量 b ,于是编译器就再申请了 8 字节的空间大小,将变量 b 放在 4 个空闲字节之后,也就是说变量 b 的起始位置在第九个字节。由于变量 b 需要 8 个字节所以没有留给变量 c 任何的剩余空间,于是变量 c 再次申请 8 个字节的空间用于存储自己,当然它本可以只申请 2 字节的空前就行了,但是为了对齐他只能申请 8 字节。那么最后我们就可以看到如下图所示的数据存储结构: 0 … … … 8 … … … 15 16 … 23 a … 多申请的空间 b … … … ...   c 多申请的空间 在 Struct B 中的 , 编译器前几步的处理也和 struct A 的一样,直到该处理变量 c 时,编译器依然要先看看为变量 a 分配的空间是否还有多余并且多余的空间是否足以容纳下变量 c, 由于变量 c 只需要两个字节,而 a 却有 4 个字节的剩余空间,所以变量 c 就很轻松的被放置在 a 之后的 4 个字节内而不需要再申请空间。变量 b 依然申请 8 字节的空间并跟随在变量 a 空余空间之后。最后我们就可以看到如下图所示的数据存储结构:      0 1 2 3 4 5 6 7 8 … … … 15 a       c   多申请的空间 b … … …   通过以上的分析我们明白了结构体内部 ( 也可以引伸到类的内部! ) 成员变量的声明顺序并不是随意的,尤其是在内存需求特别紧张的开发环境中。 对齐的目的和原理,计算( 什么是对齐,以及为什么要对齐 ) 对齐的作用和原因 : 各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平 台可能没有这种情况, 但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为 32 位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节 进行拼凑才能得到该int数据。显然在读取效 率上下降很多。这也是空间和时间的博弈。 对齐的算法 : 由于各个平台和编译器的不同,现以本人使用的gcc version 3.2.2编译器(32位x86平台)为例子,来讨论编译器对struct数据结构中的各成员如何进行对齐的。 设结构体如下定义: struct A {  int a;  char b;  short c; }; 结构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一个。所以A用到的空间应该是7字节。但是因为编译器要对数据成员在空间上进行对齐。 所以使用sizeof(strcut A)值为8。 现在把该结构体调整成员变量的顺序。 struct B {  char b;  int a;  short c; }; 这时候同样是总共7个字节的变量,但是sizeof(struct B)的值却是12。 下面我们使用预编译指令#progma pack (value)来告诉编译器,使用我们指定的对齐值来取代缺省的。 #progma pack (2) /*指定按2字节对齐*/ struct C { char b; int a; short c; }; #progma pack () /*取消指定对齐,恢复缺省对齐*/ sizeof(struct C)值是8。 修改对齐值为1: #progma pack (1) /*指定按1字节对齐*/ struct D { char b; int a; short c; }; #progma pack () /*取消指定对齐,恢复缺省对齐*/ sizeof(struct D)值为7。 对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。 这里面有四个概念值: 1.数据类型自身的对齐值:就是上面交代的基本数据类型的自身对齐值。 2.指定对齐值:#progma pack (value)时的指定对齐值value。 3.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。 4. 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。 有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。 有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".