版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、#是将其后的变量直接转换为字符串void WriteLog(arg)printf(%s=%d,#arg,arg)1. #的作用#的作用是连接两个变量如#define combine_converse_str(x,y) x#yint Err;int Num;printf(%s,combine_converse_str(Err,Num); 结果为: ErrNum再如 :int a=1;int b=2;int ab=3;printf(%d,a#b);结果为 : 3后面那句相当于 printf(%d,ab);2. do while(0) 作用1,空的宏定义避免 warning:#define foo(
2、) dowhile(0)2,存在一个独立的 block ,可以用来进行变量定义,进行比较复杂的实现。3,用在宏定义上并且当宏定义需要定义成多条语句的组合时,可以保证这几条语句是一个 整体的,并且可以消除使用这个宏定义后添加 ; 所带来的报错,例如:函数函数while(0)调用 aaa(x,y); 时不会报错并且如果使用 if (m)aaa(1,2);elseaaa(3,4); 时运行也不会使某些语句没有被运行到。3. 数组名的使用A 数组名作为函数参数 :数组名用在某个函数 A 的参数中时,处于函数传递效率原因,会 被强制转换成了指针,此后在函数A内就完全是一个值等于对应数组首地址的指针变量,
3、 可自加自减等 注意!只有在作为函数参数的情况下才会将数组名强制转换成指针,其他的都不会转换。另:数组名作为函数参数传递时,函数声明的写法有多种 int aaa(char x) int aaa(char x1) int aaa(char x100) int aaa(char *x) 作为函数参数时的参数声明也和普通的数组定义一样要合法,比如不能int aaa(charx0) ,因为定义一个数组也不能用 char x0 来定义,编译器在检查参数的声明合法 后,如果发现参数是数组,就会强制转换成指针,所以int aaa(char x1) 和 intaaa(char x100) 的结果是一样的。B
4、数组名用在 sizeof 上:结果返回的是整个数组所占空间的大小,而不是一个指针的长 度C.数组名与&和*: a与&a与&a0的值都相等,a是数组名,&a是整个数组地址,&a0是 数组首元素的地址,他们都相等int a5=1,2,3,4,5; printf(a=%xn,a); printf(&a=%xn,&a); printf(*&a=%xn,*&a); printf(&a0=%xn,&a0); printf(*&a0=%xn,*&a0); printf(*(&a0)=%xn,*(&a0); printf(*(int *)(&a)=%xn,*(int *)(&a); printf(*(*(&a
5、)=%xn,*(*(&a);结果: a=12ff34 &a=12ff34 *&a=12ff34-*与 &消掉, *&a=a=0x12ff34&a0=12ff34与 &消掉, *&a0=a0=1*&a0=1D.*(&a0)=1-*(i nt *)(&a)=1-*掉,所以按照括号次序来运算*(*(&a)=1指针数组:与&消掉,*(&a0)=*&a0=a0=1与&之间有个强制类型转换(int *),无法直接消(int *)(&a)=0x12ff34, *(int *)(&a)=*(0x12ff34)=1与&消掉,*(* (&a)=*a=1定义:基类型 *数组名使用:像普通的数组一样使用,数组名代表首
6、地址,数组名0表示第一个兀素的内容,以此类推。*常见的是字符串数组,例如:char *Names=实际内存中保存的就是即首地址 0x12ff0c0x00423018,0x0042f2f4,0042201c ,其中 0x00423018 地址所保存 的 就 是” Bill ”, 所 以 数 组 名 Name=0x12ff0c,& Name=0x12ff0c,*Name=0x00423018,*Name=0x42prin tf(Name=%xn ”,Name);printf(&Name=%xin,&Name);prin tf(*&Name=%xin,*&Name);printf(&Name0=%x
7、n,&Name0);prin tf(*Name=%xn,*Name);prin tf(*Name=%xn,*Name);prin tf(Name0=%xn,Name0);prin tf(*Name0=%x n,*Name0);prin tf(*&Name0=%xn,*&Name0);prin tf(*(&Name0)=%xn,*(&Name0);prin tf(*(i nt *)(&Name)=%xn,(*(i nt *)(& Name);prin tf(*(*(&Name)=%x n,(*(*(&Name);结果是Name=&Name=*&Name=&Name0=0x12ff0c*Name=
8、Name0=*&Name0=*(&Name0)=(*(i nt*)(&Name)=(*(*(&Name)=0x00423018*Name=*Name0=0x42注意!指针是一级,数组是又一级,所以指针数组是二级指针,定义对应的指针变量要用二级指针,比如int *p=Name;不能直接用int *p=Name4. 常量的定义和指针常量的定义及使用1. 常量的定义: const int a=10; 也可以写成 int const a=10; 针常量的定义和使用 : 有 3 种定义 : const int *pi; 和 int const *pi; 以及 int * const pi;1)前两种情况
9、一样的,没有区别。1)如果con st修饰在*pi前(前2种情况)则不能改的是*pi (即不能类似这样:*pi=50 ;赋值)而不是指 pi.2) 如果 const 是直接写在 pi 前(后一种情况) 则 pi 不能改(即不能类似 这样: pi=&i ; 赋值)。5. 函数参数传递的 4 种类型 :1. 值传递void Exchg1(int x, int y)int tmp;tmp = x;x = y;y = tmp;printf(x = %d, y = %dn, x, y);main()int a = 4,b = 6;Exchg1(a, b);printf(a = %d, b = %dn,
10、a, b);return(0);2. 地址传递void Exchg2(int *px, int *py)int tmp = *px;*px = *py;*py = tmp;printf(*px = %d, *py = %d.n, *px, *py); main()int a = 4;int b = 6;Exchg2(&a, &b);printf(a = %d, b = %d.n, a, b);return(0);3. 引用传递void Exchg3(int &x, int &y)int tmp = x;x = y;y = tmp;printf(x = %d,y = %dn, x, y);mai
11、n()int a = 4;int b = 6;Exchg3(a, b);printf(a = %d, b = %dn, a, b);return(0);4. 变长参数传递:使用省略号指定参数表 使用的宏及其定义( x86 中): #define va_start _crt_va_start #define va_arg _crt_va_arg #define va_end _crt_va_end typedef char * va_list;#define _ADDRESSOF(v) ( &(v) )#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int
12、) - 1) & (sizeof(int) - 1) )#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) #define _crt_va_arg(ap,t) ( *(t *)(ap += _INTSIZEOF(t) - _INTSIZEOF(t) ) #define _crt_va_end(ap) ( ap = (va_list)0 )实例:#include #include #include #include /* 至少需要一个确定的参数,注意括号内的省略号 */int demo(char
13、*fmt, .)va_list argp;int argno = 0;char *para;va_start(argp, fmt);while (1)para = va_arg(argp, char *);if (strcmp(para, ) = 0)break;printf(Parameter #%d is: %sn, argno, para);argno+;va_end(argp);return 0;int main()demo(DEMO1, This, is, a, demo!, );return 0;运行结果 :详解:1. 函数的参数和局部变量都是存储在栈里2. 栈的内存分配是按从高地
14、址到低地址分配的 ( 而其他的正常是从小到大分配,比如全局 变量定义等 ) ,即先分配高地址的,再分配低地址的内存3. 所有的函数参数(不管是定长参数还是变长参数)入栈都是从右到左入栈,如调函数a(x,y,z), 则 z 先入栈,并存在栈底(高地址) ,其次 y, 其次 x 。4. 数据在内存里的存储默认都按照字节对齐来存储( x86 按照 4 字节对齐),如:int aaa(char a, char b, char c)return (int)(a+b+c);调用函数 aaa(5,6,7); 时,内存中是这样的:所以,如果是数值, 则会直接存储在栈中, 如果是指针类型 (如字符串) ,则会把
15、指针值 (即 字符串首地址的值)存储在栈中,从右到左依次按照从高地址到低地址存储。如实例的程序调用 demo(DEMO1, This, is, a, demo!, );得到的堆栈为:即 0x0012FEFb 开始往低地址方向的部分为此函数的栈空间,存入的值分别为 0x00422038 ,则其中的数字 1,2 直接保存在栈0x00422058 , 0x0042204C,0x00422034 , 0x00422044 , 0x0042203C,这 6 个地址分别所指向 的实际上就是静态存储区存储” ,demo!, a, is,This,DEMO1 的地方,见图 另:如果改成 demo(DEMO1,
16、 1, 2, a, demo!, );中,见图5. #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & (sizeof(int) - 1) )的运算结果实际上就是当1=sizeof(n)=4时取4, 5=sizeof(n)=1);return 0; 这个函数编译出来为,然后在 dos 命令行中调用,得到: D: aaa bbb ccc argc=4argv= argv=aaa argv=bbb argv=ccc 其中,文件名本身也算一个参数, 所以 dos 命令行调用时, 操作系统传递给的实参实际上就 是参数个数是 4,参数为“”,
17、“ aaa” , ” bbb” , ”ccc”,如13. 哪些语句句尾要用分号?C语言规定,所有的语句必须要以分号;结尾,而预编译命令不属于语句,末尾不加分号#include #if #else #elif #endif #define等14. volatile 关键字的作用volatile 修饰的量是不稳定的,可能会经常被改变或者被其他线程使用并改变其值,所以 用 volatile 关键字就限制了执行器在执行的时候每次访问其值必须去内存中去取,而不能 被编译器优化在内部寄存器中。15. typedef 的用法1. 用于对普通数据类型定义别名:例如, typedef unsigned int
18、UINT;2. 用于定义指针:例如,typedef int *PINT;(注意!虽然*与PINT之间没有空格,但是实际上跟 typedef int* PINT 是一样的, *是跟前面的结合的。 )3. 用于对结构体 / 枚举/联合体类型定义别名:例如 :typedef unionint uid;unsigned char mid; ID_U;4. 用来定义数组:例如 ,typedef int aNum2; aNum x;5. 用来定义函数相关 , 一般都是定义函数指针:例如typedef int (*pf)(int, char);6. 用来简化复杂的定义,其实就是前面几个的综合。注意!定义普通
19、类型和指针以及结构体 / 联合体 / 枚举,都是“ typedef 原定义 别名 ; 的方式,而定义数组和函数指针是按照“替换原定义的变量”的定义方式来定义,是不 同的。typedef 使用范例 :#include #include typedef unsigned int UINT;typedef structUINT uid;char mid; STU_T;typedef STU_T *STU_T_REF;typedef int aArray2;typedef void (*fFunc)(int);void print1(int x)printf(%d,x); int main (i nt
20、 argc, char* argv) UINT i=3;STU_T st1;STU_T_REF tStu; aArray b;fFunc fTestPri nt;tStu = &st1; tStu-uid = i;b0=1;b1=2;fTestPri nt = &prin t1; fTestPri nt(tStu-uid); return 0;16. typedef 和 define 区别1. 作用上,typedef是定义一个类型,使用时与普通的变量类型用法完全相同;define是宏替换2. 语法上,typedef: typedef char INT8;defi ne:#defi ne INT
21、8 chartypedef在执行时执行-#defi ne在预编译时执行typedef是一条语句,后要分号-#defi ne是预编译指令,后无需分号typedef别名/替换者在后,原名在前-#defi ne别名/替换者在前,原名在后(这点可以这样记忆:执行前,使用前;执行后,使用后。#define是预编译,typedef 是语句,所以#define的执行是前,我们使用的那个也在前,即#define INT8 char ,INT8在前,原名在后;typedef的执行是后,我们使用的那个也在后,即typedef charINT8;我们使用的INT8也在后)typedef只能用于类型定义-#defin
22、e 不受限制,除了类型外还可以是数值等typedef不能带参数-#define 可以带参数3. 作用范围上,语句typedef (int *) PINT; PINT a,b;与 #defi ne PINT (int *)PINT a,b;而后者相当于int *a,b; 即中的PINT a,b;是不一样的,前者相当于int *a;int *b;int *a; int b;17. 复杂定义的剖解方法和用 typedef 简化可以用“右左法则”来分析,所谓右左法则,就是先找到没有定义的变量,这个就是分析的 起点,也是变量的最终属性,从这个起点,根据优先级顺序开始分析,如优先级相同,则按 照先右再左分
23、析, 遇到 () 就根据 () 是最高优先级来分析, 跳出 () 后同样按先右后左分析, 以 此类推。例如:int* (*a5)(int, char*); 找到 a 是未定义变量,所以这就是起点 , 优先级比 * 高, 所以a与5先结合,a5是一个具有5个元素的数组,再与*结合,得到每个元素都是指针, 跳出() 再向右结合 () ,得到前面的指针 (数组里的每个都是指针的元素 )指向一个函数, 函数 的参数是 (int, char *), 返回值是 int * 。void (*b10) (void (*)();b 是一个有 10 个元素的数组,每个元素都是指针,这些指针中每一个指针指向一个函数
24、,函数的参数是 void(*)() (实际上它又是个函数指针,指 针所指向的函数的参数为空,返回值为 void ),返回值是 voiddouble(*)()(*pa)9;pa 是一个指针,这个指针指向具有 9个元素的数组, 9 个元素中每一个都是 double(*)() ,即指针,指针指向一个函数,函数的参数为空,返回值为 double 。使用 typedef 简化:用 typedef 来对复杂定义做简化很简单,从小到大,抠出合适的定义单位 ( 从阅读理解和使 用上合适 ) ,然后用别名代替,最后在前面加个 typedef 关键字即可。例如:1) int* (*a5)(int, char*);
25、 - 首先从小到大,抠出合适的定义单位, a5 是个数组,方便理解,别名用 ARRAYS代,则成了 int* (*ARRAY)(int, char*);前加一个typedef即可,typedef int *(*ARRAY)(int,char*)2) . void (*b10)(void (*)();typedef void (*pfA)(void (*)(); pfA b10;进一步化简 -typedef void (*pfB)(); typedef void (*pfA)(pfB); pfA b10;3) . double(*(*pa)9)();typedef double(*PF)();
26、PF (*pa)9;18. struct enum 和 union 的定义以及用typedef 来定义定义:struct/enum/union 类型名成员类型 成员名;成员类型 成员名;;( 其中如果直接在 后定义变量的话,可以省略类型名不写 )声明: struct/enum/union 类型名 变量名 ;例如:定义 :struct student_schar * name; int age; int class; int number;声明 :struct student_s stu_a;用 typedef 来定义 :typedef struct/enum/union 类型名成员类型 成员名
27、;别名;( 其中可以省略类型名 ) 声明:别名 变量名 ; 例如:定义 :typedef struct student_tdchar *name; int age; int class; int number; STUDENT_T;使用 :STUDENT_T stu_a;用 typedef 来定义结构体 / 枚举 / 联合体实际上就是: typedef 原定义 别名 ; 与定义普通 的类型是一样的,如 typedef char INT8;19. typedef 的 2 个使用陷阱1. 用在 const 或 volatile 修饰的语句时的陷阱typedef 用在 const 修饰的语句和用在
28、volatile 修饰的语句的注意点是一样的,下面以 const 为例说明。typedef int *PINT;const PINT p1;上面 2 条语句, 先用 typedef 声明了之后, 再用 const 修饰, 实际结果不是 const int * p1; 而是 int * const p1; 根我们想象的不一样的!不能直接类似 #define 那样替换的! 因为 typedef 定义的 PINT 是地址, 所以 const 修饰的是地址, 也就是 p1 不能被修改, 而不 是整个 (int *p1) !二维的也类似, typedef int *PPINT; int i=2; int
29、 *p=&i; PPINT p1=&p; 是 p1 不能被修改,而不是 (*p1)!2. typedef 不能跟存储类关键字同时使用typedef 在语法上属于存储类关键字(如 auto, static, extern, mutable,register 等),不能再跟另外的存储类关键字同时使用了,虽然它并不实际影响存储特性。如:不能 typedef static int SINT;编译会提示“ more than one storage classspecified 使用了一个以上的存储类关键字”20. 为什么要使用 extern “ C”extern“C”是用来修饰 extern声明的,使
30、extern按照C的形式去声明,而不是C+啲形式,是为了兼容C与C+混合编程时防止C+程序调用c程序时找不到对应函数而设置的。 详细解释:C+为了实现面相对象而引入函数重载(重载就是允许同样的函数名而参数不同,因为面相对象就要求能进行函数重载,因为面相对象是把一个行为(即方法 /函数 )对应的各种情况 ( 即不同类型的参数,比如说println 函数是打印,但是有时可能传递的参数只是字符串,有时候既有数字, 又有字符串, 不止一个, 可能各种使用情况参数类型和个数都不同 ) 都封装在对象内部,而不直接对外呈现) ,通过改变函数的编译方法来实现,实际上就是编 译时用函数构建符号表的时候生成的对应
31、的函数名不同,比如同样的函数 foo(int,int),c语言编译在符号表中是 foo,而C+编译在符号表中却是 _foo_int_int)。正是由于这点,所以.cpp代码在调用函数时总是寻找按照C+编译的符号表的形式来寻找函数名,而.C文件编译出来的放在符号表的函数名却不是C+所想象那样的,所以.cpp文件在调用C文件函数时就找不到函数了。所以需要使用extern“ C来修饰extern,意思是告诉 C+编译器,这句extern要按照c的方式去寻找函数。具体的结合实例的解析和extern“C”的使用方法如下: 以以下例子说明:/*/extern int add(int x,int y);in
32、t main(int argC, Char* argv)add(2,3);return 0;/*/int add( int x, int y )return x + y;过程分析:1. 在编译文件的时候,因为是 cpp文件,在编译的时候就会按照C+的方式去编译,也就是在寻找 add 函数的时候根据 extern int add(int x,int y);语句实际上是寻找_add_int_int符号表的,因为有这个 extern 语句,所以编译可以通过;2. 在编译的时候,因为是.c文件,所以按照c的方式去编译,得到add函数对应在符号表 上函数名是_add,当然因为没有语法错误编译也能通过。3
33、. 链接的时候,链接到.cpp生成的编译文件(linux下是.o)的调用add函数语句时,会去其他文件的符号表去找 _add_int_int 函数,实际上编程者的本意是函数在中定义的 add 函数,但是在对应的编译文件 (linux 下是的符号表中没有找到 _add_int_int符号(因为add函数被用c的方式编译成了 _add 了),在其他文件中也没有找到,所以就会报这个符号无法解析的错误。解决方法:在cpp文件中的extern函数的时候使用extern“ C,可以有2种书写方式:1. 用 extern“ C” 代替 extern 语句。改成extern “ C int add(int x,int y);即可2. 用 extern “ C
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 抽样调查工作协议书
- 器质性精神障碍护理查房
- 竞标咨询协议书范本
- 胃癌患者化疗治疗方案训练
- 孕妇低血糖科普
- 2026中科院生态环境研究中心生态环境研究中心科技和支撑岗位招聘备考题库(补充)含答案详解(b卷)
- 2026广东韶关市新丰县医共体招聘专业技术人员公30人告及参考答案详解(考试直接用)
- 2026海南海控乐城医院(四川大学华西乐城医院)招聘26人备考题库附参考答案详解(精练)
- 2026绵阳科达人才安居有限责任公司员工招聘1人备考题库及参考答案详解(综合题)
- 2026四川成都市新津区外国语实验小学校面向社会招聘教师18人备考题库及参考答案详解(a卷)
- 生猪屠宰厂可行性方案
- 景区旅游经营预测研究报告
- JB-T 14179-2022 带式输送机用托辊冲压轴承座
- 溢洪河大桥防洪评价报告
- 第四节喀斯特地貌最全课件
- 成都职业技术学院教师招聘考试历年真题
- 断绝亲情关系协议书
- 产褥期母婴的护理-产褥期妇女的生理变化(妇产科护理学课件)
- 安徽马鞍山市横望人力资源有限公司招考聘用劳务外包人员笔试题库含答案解析
- 低压电工试题库-含答案
- 森林抚育技术规程
评论
0/150
提交评论