c语言 补充内容_第1页
c语言 补充内容_第2页
c语言 补充内容_第3页
c语言 补充内容_第4页
c语言 补充内容_第5页
已阅读5页,还剩133页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C语言补充内容内容命令行参数可变长的实参列表输入输出重定向动态内存分配c预处理gcc编译参数编程中的注意事项编程举例:事务处理程序内容命令行参数可变长的实参列表输入输出重定向动态内存分配c预处理gcc编译参数编程中的注意事项编程举例:事务处理程序命令行参数以命令行运行程序时所带的参数intmain(intargc,char*argv[]);intmain(intargc,char**argv);几乎所有的实际应用程序都需要处理命令行参数举例:notepad.exea.txt7z命令行参数(2)intmain(int

argc,int*argv[]);argc:正整数,表示命令行参数的个数。注意:可执行文件名本身也是一个参数argv:指针数组,数组中的元素分别指向一个字符串,即命令行参数的各个字段例如在命令行终端下输入:

notepad.exe

a.txt

argc的值是2,argv[0],argv[1]的值分别是“notepad.exe”和“a.txt”/*Fig.14.3:fig14_03.cUsingcommand-linearguments*/#include<stdio.h>intmain(intargc,char*argv[]){FILE*inFilePtr;/*inputfilepointer*/FILE*outFilePtr;/*outputfilepointer*/intc;/*definectoholdcharactersinputbyuser*//*checknumberofcommand-linearguments*/if(argc!=3){printf("Usage:mycopyinfileoutfile\n");}/*endif*/else{/*ifinputfilecanbeopened*/if((inFilePtr=fopen(argv[1],"r"))!=NULL){/*ifoutputfilecanbeopened*/if((outFilePtr=fopen(argv[2],"w"))!=NULL){下面的程序将一个文件中的内容逐字符的复制到另一个文件中假如这个程序的可执行文件是mycopy,运行:mycopyinput.txtoutput.txt/*readandoutputcharacters*/while((c=fgetc(inFilePtr))!=EOF){fputc(c,outFilePtr);}/*endwhile*/}/*endif*/else{/*outputfilecouldnotbeopened*/printf("File\"%s\"couldnotbeopened\n",argv[2]);}/*endelse*/}/*endif*/else{/*inputfilecouldnotbeopened*/printf("File\"%s\"couldnotbeopened\n",argv[1]);}/*endelse*/}/*endelse*/return0;/*indicatessuccessfultermination*/}/*endmain*/内容命令行参数可变长的实参列表输入输出重定向动态内存分配c预处理gcc编译参数编程中的注意事项编程举例:事务处理程序可变长的实参列表函数接受的实参个数可以是不确定的例:intprintf(constchar*format,…);其中…表示这个函数可以接受可变数目的实参,省略号必须放在形参列表的末尾include<stdarg.h>stdarg.h中的宏和定义va_list:为了访问可变长实参列表中的实参必须声明一个类型为va_list的对象va_start:在一个可变长实参列表中的实参被访问前,先要调用这个宏。其功能:初始化用va_list声明的对象,以便让宏va_arg和va_end来使用va_arg:宏,展开成一个表示可变长实参列表中下一个实参的值和类型的表达式。va_end:正常返回/*Fig.14.2:fig14_02.cUsingvariable-lengthargumentlists*/#include<stdio.h>#include<stdarg.h>doubleaverage(inti,...);/*prototype*/intmain(void){doublew=37.5;doublex=22.5;doubley=1.7;doublez=10.2;printf("%s%.1f\n%s%.1f\n%s%.1f\n%s%.1f\n\n","w=",w,"x=",x,"y=",y,"z=",z);printf("%s%.3f\n%s%.3f\n%s%.3f\n","Theaverageofwandxis",average(2,w,x),"Theaverageofw,x,andyis",average(3,w,x,y),"Theaverageofw,x,y,andzis",average(4,w,x,y,z));return0;/*indicatessuccessfultermination*/}/*endmain*//*calculateaverage*/doubleaverage(inti,...){doubletotal=0;/*initializetotal*/intj;/*counterforselectingarguments*/va_listap;/*storesinformationneededbyva_startandva_end*/

va_start(ap,i);/*initializestheva_listobject*//*processvariablelengthargumentlist*/for(j=1;j<=i;j++){total+=va_arg(ap,double);}/*endfor*/

va_end(ap);/*cleanupvariable-lengthargumentlist*/returntotal/i;/*calculateaverage*/}/*endfunctionaverage*/

内容命令行参数可变长的实参列表输入输出重定向动态内存分配c预处理gcc编译参数编程中的注意事项编程举例:事务处理程序输入输出重定向所谓重定向,就是不使用系统的标准输入端口、标准输出端口或标准错误端口,而进行重新的指定,所以重定向分为输出重定向、输入重定向和错误重定向:STDIN0标准输入键盘命令在执行时所要的输入数据通过它来取得

STDOUT1标准输出显示器命令执行后的输出结果从该端口送出

STDERR2标准错误显示器命令执行时的错误信息通过该端口送出输入输出重定向对于标准输入输出的程序允许将输入重定向为从一个文件中输入,或者将输出重定向到一个文件中输出>:输出重定向符cmd>data.out<:输入重定向符cmd<data.in输出重定向把执行相应DOS/UNIX命令时本应在屏幕上显示的内容输出到重定向命令所指向的文件或设备中去。输出重定向命令>、>>:>原文件会被删除,重新生成新的文件>>它以追加的方式,将命令的输出写入文件的末尾,原文件内容会被保留,新的内容会添加到原文件内容的后面输入重定向输入重定向使输入信息来自文件小于号<是输入重定向操作符,在<之后的文件名或设备名是重定向的输入源如果一个程序需要输入较多数据,使用输入重定向可以提高效率。如more、sort和find命令。其中more进行分屏显示;find查找符合条件的内容;sort(按行)排序实例more<c:\readme.txtcmd>file把stdout重定向到file文件中cmd>>file把stdout重定向到file文件中(追加)cmd1>file把stdout重定向到file文件中cmd2>file把stderr重定向到file文件中cmd2>>file把stderr重定向到file文件中(追加)cmd<file>file2cmd命令以file文件作为stdin,以file2文件作为stdoutprog.c#include<stdio.h>intmain(void){charc;while(scanf("%c,&c)==1){printf("%c",c);

printf("%c",c);}

return0;}尝试一下命令cmdcmd<infile.txtcmd>outfile.txtcmd<infile.txt>outfile.txt输出重定向的一个应用如在批处理命令执行期间为了禁止命令或程序执行后输出信息而扰乱屏幕,可用DOS重定向功能把输出改向NUL设备(NUL不指向任何实际设备)例如:C:\>copya.txtb.txt>NUL命令执行结束不显示"1file(s)copied"的信息。输入重定向的一个应用有的交互程序在执行时要求很多键盘输入,但有时输入是固定不变的,为加快运行速度,可预先建立一个输入文件,此文件的内容为程序的键盘输入项,每个输入项占一行。假如有一个程序cx其输入项全部包括在文件in.dat中,执行C:\>cx<in.dat>NUL程序就自动执行。管道进程从“管道”的一端发送另一端接收,也就是说将若干命令用输入输出“管道”串接在一起,这就是管道;管道在某种程度上是输入和输出重定向的结合,前一个命令的输出,作为下一个命令的输入,而不需要经过任何中间文件。竖线字符”|”是管道操作符,管道命令经常与一些过滤命令联合使用。DOS的管道功能是使一个程序或命令的标准输出用做另一个程序或命令的标准输入。typeaaa|DEBUG>bbb如把DEBUG的输入命令写入文件aaa,用type命令通过管道功能将aaa的内容传输给DEBUG,在DEBUG执行期间不再从控制台索取命令参数,从而提高了机器效率。dir|more使得当前目录列表在屏幕上逐屏显示。dir的输出是整个目录列表,它不出现在屏幕上而是由于符号“|”的规定,成为下一个命令more的输入,more命令则将其输入,more命令则将其输入一屏一屏地显示,成为命令行的输出。dir|find”hello”>file其中dir的输出是当前目录列表,不出现在屏幕上而是成为find命令的输入。find命令在输入文件中寻找指定字符串“hello”并输出包含这个字符串的行,由于输出重定向符号>的规定,最后的输出已存入文件file,不出现在屏幕上。dir|find"<dir”>file则是将当前目录项中的子目录项寻找出来并存入文件file中。内容命令行参数可变长的实参列表输入输出重定向动态内存分配c预处理gcc编译参数编程中的注意事项编程举例:事务处理程序动态内存分配静态内存分配在编译时完成分配,自动释放——方便、效率高栈空间,大小有限制(默认情况下,windows系统1M,Linux下8M)动态内存分配数据结构的存储空间不是在程序运行前事先分好,而是在程序运行中根据需要临时分配程序员自己申请并指明大小,自己释放——灵活堆空间,空间大小几乎没有什么限制(只受到计算平台存储空间资源和操作系统对存储空间资源分配策略的限制)c/c++程序内存分配栈(stack)程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。程序结束时由编译器自动释放。堆(heap)在内存开辟另一块存储区域。一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事。全局区(静态区)(static)编译器编译时即分配内存。全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放文字常量区程序代码区堆栈实例代码比较实例:#include<stdlib.h>

#include<string.h>inta=0;//全局初始化区char*p1;//全局未初始化区voidmain()

{

intb=1; //栈chars[]="abc"; //栈char*p2; //栈char*p3="123456"; //"123456\0"在常量区,p3在栈上。staticintc=0; //全局(静态)初始化区p1=(char*)malloc(10);p2=(char*)malloc(20); //分配得来得10和20字节的区域就在堆区。strcpy(p1,“123456”); //123456\0放在常量区,编译器可能会将它与p3 //所指向的"123456"优化成一个地方

system("pause");}动态内存分配malloc函数和free函数Cvoid*malloc(size_tsize);int*gradeArray=malloc(sizeof(int));voidfree(void*ptr);free(gradeArray);new/deleteC++运算符调用类的构造函数(new)或者析构函数(delete)int*gradeArray=newint[10];delete[]gradeArray;堆/栈的不同堆和栈的主要区别由以下几点:

1、管理方式不同;

2、空间大小不同;

3、能否产生碎片不同;

4、生长方向不同;

5、分配方式不同;

6、分配效率不同;

堆栈的不同管理方式不同;管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memoryleak。空间大小不同;空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M。当然,这个值可以修改。

堆栈的不同能否产生碎片不同;碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构。堆栈的不同生长方向不同;生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。分配方式不同;分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由malloc函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。堆栈的不同分配效率不同;分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。函数调用的栈操作寄存器ebp用做帧指针(framepointer) esp用作栈指针(stackpointer)函数调用操作包括从一块代码到另一块代码之间的双向数据传递和执行控制转移。数据传递通过函数参数和返回值来进行。在进入函数时为函数的局部变量分配存储空间,并且在退出函数时收回这部分空间函数调用的栈操作voidswap(int*a,int*b){

intc;

c=*a;*a=*b;*b=c;

}

intmain(){

inta,b;

a=16;b=32;

swap(&a,&b);

return(a-b);

}内容命令行参数可变长的实参列表输入输出重定向动态内存分配c预处理gcc编译参数编程中的注意事项编程举例:事务处理程序C预处理#include预处理命令文件包含#define预处理命令符号常量和宏条件编译#error和#pragma预处理命令assert#include文件#include“文件名“或#include<文件名>

文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。尖括号与双引号差别:目录查找次序不同一个include包含一个文件允许嵌套包含

条件编译#define符号常量和宏#undef条件编译的三种形式

条件编译#ifdef标识符

程序段1

#else

程序段2

#endif

#ifndef标识符

程序段1

#else

程序段2

#endif

#if常量表达式

程序段1

#else

程序段2

#endif

如果标识符已被#define命令定义过则对程序段1进行编译;否则对程序段2进行编译。,如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。如常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。条件编译#ifdef/#else/#endif#defineNUMok

main(){

ps=(structstu*)malloc(sizeof(structstu));

ps->num=102;

ps->name="Zhangping";

ps->sex='M';

ps->score=62.5;

#ifdefNUM

printf("Number=%d\nScore=%f\n",ps->num,ps->score);

#else

printf("Name=%s\nSex=%c\n",ps->name,ps->sex);

#endif

free(ps);

}structstu

{

intnum;

char*name;

charsex;

floatscore;

}*ps;条件编译#ifndef/#define/#endif避免头文件的重定义 #ifndef_TEST_H

#define_TEST_H//一般是文件名的大写

头文件结尾写上一行:

#endif

这样一个工程文件里同时包含两个test.h 时,就不会出现重定义的错误了。条件编译#ifdef/#else/#endif判断表达式真假的条件编译 #defineR1

main(){

floatc,r,s;

printf("inputanumber:");

scanf("%f",&c);

#ifR

r=3.14159*c*c;

printf("areaofroundis:%f\n",r);

#else

s=c*c;

printf("areaofsquareis:%f\n",s);

#endif

}预处理命令#error主要的作用是在编译的时候输出编译错误信息token-sequence,从方便程序员检查程序中出现的错误。该指令用于程序的调试,当编译中遇到#error指令就停止编译。 #defineCONST_NAME1"CONST_NAME1"

printf("%s\n",CONST_NAME1);

#undefCONST_NAME1

#ifndefCONST_NAME1

#errorNodefinedConstantSymbolCONST_NAME1

#endif预处理命令#pragma主要的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。其格式一般为:

#pragmapara

其中para为参数,

常用参数组合:#pragmamessage:编译信息输出窗口中输出相应的信息。例如#pragmamessage(“消息文本”)预处理命令当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法

#ifdef_X86#Pragma

message(“_X86macroactivated!”)#endif

当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_X86macroactivated!”。预处理命令#pragmacode_seg

#pragmacode_seg(["section-name"[,"section-class"]])

它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。#pragmaonce(比较常用)

只要在头文件的最开始加入这条指令就能够保证头文件被编译一次。预处理命令其他pragma的参数#pragmahdrstop#pragmaonce#pragmaresource#pragmawarningassert宏断言voidassert(intexpression)assert宏的原型定义在<assert.h>中,其先计算表达式expression,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用abort来终止程序运行。其优势是利于调试,缺点是频繁的调用会极大的影响程序的性能,增加额外的开销。调试结束后,可以通过在包含#include<assert.h>的语句之前插入#defineNDEBUG

来禁用assert调用。#include<stdio.h>

#defineNDEBUG

#include<assert.h>assert()使用实例 #include<stdio.h>

#include<assert.h>

#include<stdlib.h> intmain(void)

{

FILE*fp;

fp=fopen(“test.txt”,“w”);//文件可创建,打开成功

assert(fp);//所以这里不会出错

fclose(fp);

fp=fopen(“noexitfile.txt”,“r”);//文件不存在,打开失败

assert(fp);//所以这里出错

fclose(fp);//程序永远都执行不到这里来 return0;

}运行后,错误提示为:badptr.c:14:main:Assertion`fp'failed.断言assert的用法assert用法在函数开始处检验传入参数的合法性;intresetBufferSize(intnNewSize){ assert(nNewSize>=0);

assert(nNewSize<=MAX_BUFFER_SIZE);}每个assert只检验一个条件,多个条件分别断言;assert(nOffset>=0&&nOffset+nSize <=m_nInfomationSize);劣assert(nOffset>=0);

assert(nOffset+nSize<=m_nInfomationSize);优断言内不可使用改变环境的语句;assert(i++<100);

劣-调试结束,取消断言后程序出错断言定义文件#include"cvidef.h"#include"cvirte.h"#ifndef_ASSERT_H_#define_ASSERT_H_#ifdef__cplusplusextern"C"{#endif#undefassert

#ifdefNDEBUG #defineassert(exp)((void)0)#else voidCVIANSI_assert(char*,char*,int); #defineassert(exp)((exp)?(void)0:_assert(#exp,__FILE__,__LINE__))#endif

#ifdef__cplusplus }#endif

#endif/*_ASSERT_H_*/内容命令行参数可变长的实参列表输入输出重定向动态内存分配c预处理gcc编译器编程中的注意事项编程举例:事务处理程序gcc编译器C/C++程序的编译运行gcc/g++是GNC的开源编译器,用来对c/c++程序进行编译、调试后缀为.c的程序,gcc当成c程序,g++当成c++程序;而后缀为.cpp的程序两者都当成c++程序//hello.c#include<stdio.h>

intmain(){

printf("helloworld\n");return0;}gcc编译过程预处理对源代码文件中的文件包含、预编译语句进行分析gcc-ohello.i

-Ehello.c编译及优化将预处理文件进行汇编,生成汇编语言程序

gcc-ohello.s-Shello.i汇编将汇编的代码进一步进行处理,生成相应的目标文件,以.o为扩展名gcc-ohello.o-chello.s链接将生成的目标文件与其他目标文件(或库文件)连接成可执行的二进制代码文件gcc-ohellohello.ogcchello.c–ohello多个文件的编译链接对于一个程序的多个源文件进行编译连接时,可以使用如下格式:gcc–otestfirst.csecond.cthird.c该命令将同时编译3个源文件,将它们连接成一个可执行文件,名为test常用编译选项(1)-c仅把源程序编译为目标代码而不做链接不生成最终的可执行程序,只生成一个与源程序文件名相同的以.o为后缀的目标文件。-o<file>将编译结果写入文件<file>中-vGcc的版本信息-xlanguage强制编译器指定的语言编译器来编译某个源程序,例如gcc-xc++test.c-I<DIR>库依赖选项,指定库及头文件路径l<library>在函数库<library>中查找需要链接的函数-L<DIR>将目录<dir>加入到搜索链接函数库的目录集合中常用编译选项(2)-w禁止输出警告信息-Wall在标准输出上显示所有的警告信息-O或-O<n>指定编译优化级别,<n>可以为1,2,3和s,-O等于-O1-g生成调试辅助信息,以便使用GDB等调试工具进行调试-pg加入运行剖面生成代码,以便生成可以被gprof解读的程序运行剖面数据gcc实例-----------------------

清单main.c

-----------------------

#include

<stdio.h>

#include

<unistd.h>

intfactorial(intn);

intmain(intargc,char**argv)

{

intn;

if(argc<2){

printf(“Usage:%sn\n”,argv[0]);

return-1;

}else{

n=atoi(argv[1]);

printf(“Factorialof%dis%d.\n”,n,factorial(n));

}

return0;

}-----------------------

清单factorial.c

-----------------------

intfactorial(intn)

{

if(n<=1)

return1;

else

returnfactorial(n-1)*n;

}Gcc-ofactorialmain.cfactorial.c/factorial5Factorialof5is120.

内容命令行参数可变长的实参列表输入输出重定向动态内存分配c预处理gcc编译参数编程中的注意事项编程举例:事务处理程序编程中的注意事项数值的表示和计算复杂数据类型的解读变量定义中的复杂类型说明强制转换中的复杂类型数值的表示和计算整型(定点型)有符号和无符号无符号数和标志位截断与扩展计算中的溢出整除引起的误差浮点数据类型表示方法有效数字和最低位当量比较大小计算中的溢出计算中的类型转换按位操作数值计算速度整型数据类型字符、短整型、整型、长整型C语言对各种整型程度没有限制仅约束sizeof(long)>=sizeof(int)>=sizeof(short)>=sizeof(char)Sizeof(int)>=2bytes,sizeof(int)>=2bytes,sizeof(long)>=4bytesLimits.h定义一些常数,表示各个数据类型的范围包括SCHAR_MIN,SCHAR_MAX,UCHAR_MAXSHRT_MIN,SHRT_MAX,USHRT_MAXINT_MIN,INT_MAX,UINT_MAXLONG_MIN,LONG_MAX,ULONG_MAX有符号和无符号数有符号数和无符号数,对最高位的解释不通如果是有符号,0-表示正,1-表示负大的无符号数转换成有符号数,可能变成负数一个负数转换成无符号数,变成一个整数000000011101022011331004-41015-31106-21117-1——补码的计算1的二进制码为001取反为110加上末尾1为111-1的补码为111有符号和无符号数例子intsi;unsignedui;si=-1;ui=(unsigned)INT_MAX+1;printf(“%#x=signed%d=unsigned%d\n”,si,si,si);printf(“%#x=signed%d=unsigned%d\n”,ui,ui,ui);0xffffffff=signed-1=unsigned42949672950x80000000=signed-2147483648=unsigned2147483648参考前页有符号和无符号数运算有符号/无符号,同时出现,隐含强制转换有符号到无符号也就是“窄”变””宽”Unsignedshortui=5;Shortsi=-5;Printf(“%d\n”,ui>si)输出0因为-5转换成无符号数,成为一个很大的数有符号和无符号数无符号数相减Unsignedintx=57,y=663;Printf(“x>y:%d,y>x:%d,x-y>0:%d,y-x>0:%d\n”,x>y,y>x,x-y>0,y-x>0);对于无符号数,x-y>0总是成立的

0111有符号和无符号数无符号数和有符号数使用原则尽量使用有符号数需要更大数值范围的处理时,使用更长位数的整数无符号数和标志位无符号数的使用数据掩码计算机网络的IP地址比如<sys/stat.h>定义了不同的文件类型#define_S_IFMT0170000//文件类型bit#define_S_IFDIR0040000//目录文件判断办法If(mode&_S_IFMT)==_S_IFDIR){……}整型的截断和扩展整型的截断Intn;shorts;N=0x12345678;S=n;Printf(“n:%d(%#x),s:%d(%#x)\n”,N,n,s,s);N:305441741(ox12345678),s:-21555(ox5678)高位字节被丢弃整型的截断和扩展整型的扩展无符号数/有符号数,扩展后不变Shortps=12345,ns=-12345;Unsignedshortpus=(unsignedshort)ps;Unsignedshortnus=(unsignedshort)ns;Intpi,ni;Unsignedintpui,nui,ui;Pi=ps;ni=ns;Pui=pus;nui=nus;Printf(“pi=%d(%#x),ni=%d(%#x)”,pi,pi,ni,ni);Printf(“pui=%d(%#x),nui=%d(%#x)”,pui,pui,nui,nui);整型的截断和扩展整型的扩展无符号数/有符号数,扩展后不变Shortps=12345,ns=-12345;Unsignedshortpus=(unsignedshort)ps;Unsignedshortnus=(unsignedshort)ns;Intpi,ni;Unsignedintpui,nui,ui;Pi=ps;ni=ns;Pui=pus;nui=nus;Printf(“pi=%d(%#x),ni=%d(%#x)”,pi,pi,ni,ni);Printf(“pui=%d(%#x),nui=%d(%#x)”,pui,pui,nui,nui);无符号扩Ps--pus-pui--pi(有符号)无符号扩Ns--nus--nui--ni(有符号)Pi=12345(0x3039),ni=-12345(0xffffcfc7)Pui=12345(0x3039),nui=53191(0xcfc7)整型计算的溢出溢出的情况两个符号相同的数值+两个符号相异的数值-两个大数×原因:整型是由一定范围的Inta=12,b=8388608,c=1024;Printf(“a*c=%d,b*c=%d\n”,a*c,b*c);A*c=12288,b*c=0整型计算的溢出判断加法是否溢出Inta,b,c;C=a+b;If((a>=0)==(b>=0)&&(a>=0)!=(c>=0)){//overflow)}乘出来的数值符号不对了,问:能否解决正数+负数,负数+负数的判断整型计算的溢出判断乘法是否溢出Intx,y,z;Z=x*y;If(x!=z/y){//overflow}结果除以y不等于x了整型计算的溢出计算平均值IntI,n,avg,sum,x[MAX_NUM];For(sum=i=0;i<n;i++)sum+x[i];Avg=n>0?Sum/n:0;问题:每个数值较大,中间结果溢出。整型计算的溢出计算平均值—解决办法For(rem_sum=avg=i=0;i<n;i++){avg+=x[i]/n;rem_sum+=x[i]%n;}If(n>0)avg+=rem_sum/n;整除的结果累加余数先整合起来,最后除以n,再加到avg里面,避免误差整除所引起的误差均分窗口大小For(i=1;i<n;i++){p_x[i]=i/n*win_w+win_x;prinft(“%d\t”,p_x[i])}Putchar(‘\n’);000000000因为:i/n一直为0整除所引起的误差均分窗口大小—改进W=win_w/n;For(i=1;i<n;i++){p_x[i]=w*i

+win_x;prinft(“%d\t”,p_x[i])}Putchar(‘\n’);当win_w=100,n=10, 102030405060708090当win_w=99,n=10, 9,18,27,36,45,54,63,72,81,81与右边界99差18像素,不均衡整除所引起的误差均分窗口大小—再改进For(i=1;i<n;i++){p_x[i]=win_w*i/n

+win_x;prinft(“%d\t”,p_x[i])}Putchar(‘\n’);当win_w=99,n=10, 9,19,29,39,49,59,69,79,89,89与右边界99差10像素,均分OK先win_w*i再除以n,保证精度整型数据的字节序和尾端大尾端bigendian高位字节-低端地址,低位字节在高端地址小尾端littleendian正好相反实例32位整数ox12345678从低地址到高地址大尾端:0x12,0x34,0x56,0x78小尾端:0x78,0x56,0x34,0x12整型数据的字节序和尾端考虑尾端的情况不同系统之间进行网络数据传输一个系统写入文件,另外一个系统读出文件解决办法Uint32_thtonl(uint32_thostlong);主机-网络Uint32_tntohl(uint32_thostlong);网络-主机Uint16_thtons(uint16_thostshort);主机-网络Uint16_tntohs(uint16_thostshort);网络-主机整型数据的字节序和尾端实例Union{Charbyte[4];Unsignedshortusi[2];Unsignedintu_int;}data;IntI;Data.byte[0]=‘a’;Data.byte[1]=‘b’;Data.byte[2]=‘c’;Data.byte[3]=‘d’;For(inti=0;i<4;i++){Putchar(data.byte[i]);putchar(‘‘);}Printf(“%#x:%d,%#x:%d\n”,data.usi[0],data.usi[0],data.usi[1],data.usi[1]);Printf(“%#x:%d”,data.u_int,data.u_int);整型数据的字节序和尾端实例Union{Charbyte[4];Unsignedshortusi[2];Unsignedintu_int;}data;IntI;Data.byte[0]=‘a’;Data.byte[1]=‘b’;Data.byte[2]=‘c’;Data.byte[3]=‘d’;For(inti=0;i<4;i++){Putchar(data.byte[i]);putchar(‘‘);}Printf(“%#x:%d,%#x:%d\n”,data.usi[0],data.usi[0],data.usi[1],data.usi[1]);Printf(“%#x:%d”,data.u_int,data.u_int);大尾端内存布局0x61,0x62,0x63,0x64

短整型0x61620x6364

整型0x61626364小尾端——内存布局一样,解释不一样内存布局0x61,0x62,0x63,0x64

短整型0x62610x6463

整型0x64636261浮点数表示方法——单精度和双精度的浮点数313023220符号指数(8)尾数(23)636252510符号指数(11)尾数(52)V=(-1)s*M*2E浮点数规格化浮点数指数部分不是全0,不是全1非规格化浮点数指数部分全0用处表示0.0表示非常接近0.0的数无穷大指数部分全1,小数部分全0NaN(NotaNumber)指数部分全1,小数部分非全0浮点数有效数字单精度:7位双精度:16位对尾数的有效位数内不能精确表示的数据会产生误差误差的灾难性后果1991年,海湾战争,爱国者导弹误炸美军兵营,30人丧生浮点数的加法和乘法满足单调性A>b且c>=0,则a+c>b+c,a*c>b*c浮点数的加法和乘法满足交换性A+b==b+a,a*b==b*a浮点数的加法不满足结合型(a+b)+c==a+(b+c)(1.23+1e17)–1e17=0较大的误差导致1.23被忽略1.23+(1e17-1e17)=1.23浮点数精度实例T=1.234;x=t*2.01;Y=t+t+t/100.0;If(x==y)Printf(“x(%.10f)==Y(%.10f)\n”,x,y);ElsePrintf(“x(%.10f)!=Y(%.10f)\n”,x,y);结果x(2.4803400000)!=Y(2.4803400000)比较奇怪,因为这两个实际上是不相等的,放大1016就能够看出差别来X(24803399999999996.00)!=y(24803400000000000.00)浮点数浮点数的比较下面的实例将死循环Doublei;For(i=0.0;i!=1.0;i+=0.1)Printf(“%.2f\n”,i);由于误差的累计10个0.1相加不等于1.00避免使用浮点数做循环变量浮点数浮点数的比较浮点数的相等/不相等If(fabs(a-b)<ESP)…浮点数是否等于0If(x<ESP&&x>-ESP)float.h定义一个最小单位,1.0+x不等于1.0的最小值#defineFLT_EPSILON1.19209290e07F#defineDBL_EPSILON2.2204460492503131e-16浮点数浮点数的上溢和下溢绝对值很大的数和绝对值很小的数同时参与运算的时候泊松分布实例P(m,k)=mk*e–m/K!#include<math.h>doublefactorial(intn){inti;doublef=1.0f;for(i=1;i<=n;i++)f*=(double)I;returnf;}doublep_1(doublem,intk){returnpow(m,k)*exp(-m)/factorial(k);}mk和K!都增长得较快,发生溢出,比如170!是一个很大的数浮点数泊松分布实例——改进P(m,k)=mk*e–m/K!Doublep_2(doublem,intk){Doublex=1.0;intI;If(m>k){For(i=0;i<k;i++)x*=m/M_E/(k-i);for(;i<m;i++)x/=M_E;returnx;}For(i=0;i<k;i++)x*=m/M_E/(k-i);for(;i<m;i++)x/=M_E;returnx;}

M_E在<math.h>中定义,接近e的一个近似值本改进试图解决计算过程的溢出,当m和K的数据较大时,仍然会产生溢出浮点数泊松分布实例——再改进P(m,k)=mk*e–m/K!doublep_2(doublem,intk){doublex=1.0;intem,mk,nk;Em=mk=nk=0;While(em<m||mk<k||nk<k){While(mk<k&&x<MAX_INTER){x*=m;mk++}if(x<MIN_INTER)return0.0;While(nk<k&&x>MIN_INTER){nk++;x/nk;}While(em<m&&x>MIN_INTER){x/M_E;em++}}Returnx;}X先长大,长的不能再大,则长小,长的不能再小,再长大。总是不产生溢出。MAX_INTER/MIN_INTER是符号常量,定义double所表示的范围的极限值数值计算中的类型转换目标—保证数值不变转换类型基本转换,char/short->int,float->double自动转换,较窄的数据类型向较宽的数据类型转换强制转换,用户显式要求转换Shortsa;Floatfb;Intx;x=sa+fb;基本转换:sa->int,fa->double自动转换:int转换为double再进行加法,最后给x赋值数值计算中的类型转换一些问题浮点型转换成整型,小数部分丢失绝对值超过2的32次方的double型,转换成整型,产生截断整型向float转换,由于有效数字的位数较少,可能造成数据精度丢失Int->double转换是安全的数值计算中的类型转换强制类型转换Intx;Doubled1,d2;X=(int)(d1*d2);X=(int)d1*d2);这两个强制类型转换的作用范围是不一样的没有确切理解和把握,不要进行类似的强制类型转换1996年阿丽亚娜火箭-5发射失败,使用了型号4的软件,但是型号5的参数和型号4不同,未经检验直接强制转换导致问题取整数和小数部分Intint_part;Floatx,decimal_part;Int_part=(int)x;Decimal_part=x-(int)x;Char的符号类型C语言没有规定char是有符号还是无符号如下代码在不同系统输出可能不同Charcc=0xc1;Inti=cc;Printf(“%d(%#x)\n”,i,i);无符号系统,输出193(0xc1)有符号系统,输出-63(0xffffffc1)Char的符号类型Char和int型的转换应用:图像处理避免使用char类型做转换在一些应用场合,不得不用则明确是无符号还是有符号unsignedintui=(unsignedchar)cc;intI=(signedchar)cc;变量符号的判断#defineTEST_UNSIGN(n)n=-1;if(n>0)printf(#n”isunsigned\n”);\Elseprintf(#n”issigned\n”);按位操作按位操作压缩,加密/解密外设控制主要的操作包括&按位与|按位或~按位取反^按位异或<<向左移位>>向右移位按位操作向左移位右边填0无符号数:等价于乘以2n,移位操作比乘法快不产生溢出的情况下,移位效果对有符号数和无符号数是一样的不管有无符号,数据的左移都可能溢出对于有符号数,左移改变了符号位,数值正负发生改变左移实例

signed0x2faf0800(800000000)<<4=0xfaf0800(-8490188)0xffffcfc7(-12345)<<4=0xfffcfc70(-197520)Unsigned0x3039(12345)<<4=0x30390(197520)0xffffcfc7(4294954951)<<4=0xfffcfc70(4294769776)按位操作向右移位无符号数,高位填0有符号数,正数高位填0,负数高位填1-5>>16=-1二进制码0000000000000101补码是1111111111111011右移16位左右的位都是1,对应-1左移实例

signed0x3039(12345)>>4=0x303(771)0xffffcfc7(-12345)>>4=0xfffffcfc(-772)Unsigned0x3039(12345)>>4=0x303(771)0xffffcfc7(4294954951)<<4=0xfffffcfc(268434684)按位操作标志位的设置操作系统、编译系统、图形界面用二进制位描述对象属性状态,这些对象的状态是2值的比如window图形界面,窗口具有若干属性#defineWIN_BORDER0x001#defineWIN_CAPTION0x002#defineWIN_CHILD0x004……Style|=WIN_CAPTIONIf((style&WIN_CAPTION)!=0)

设置WIN_CAPTION标志Else

没有设置WIN_CAPTION标志清楚某个标志Style&=~WIN_CAPTION按位操作常用位操作1)不借用第三个存储单元交换数据X^=y;Y^=x;X^=y;2)生成最右边n位为全1,其余各位为0的掩码X=(1<<n)-110000-1=011113)生成最右边n位为全0,其余各位为1的掩码X=~((1<<n)-1)4)将一个整数最右侧的1变成0X&=x-1X=10101100X-1=10101011结果101010005)分离出一个整数最右边的1X&=-xx=10101100,-x=01010100结果00000100按位操作常用位操作6)分离出一个整数最右侧的0X=~x&(x+1)X=10101111,x+1=01010000,x+1=10110000,结果就是000100007)构造一个整数最右侧连续的0对应的掩码X=~x&(x-1)X=10110000,~x=01001111,x-1=10101111,结果就是00001111按位操作判断获奖人员,6个人,可能有若干获奖组合,现在根据一定条件判断有哪些组合可以用6重循环,程序很臃肿用一个整数的6个bit来进行操作,000000->1111110–26-1Enum{A=0,B,C,D,E,F,NUM};#defineastatus[A]#definebstatus[B]#definecstatus[C]#definedstatus[D]#defineestatus[E]#definefstatus[F]按位操作Intstatus[NUM];Charinfo[][32]={{“

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论