版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C语言编程中的几个基本概念#includev>与#include””#include0和#include'"'有什么区别?这个题目考查大家的基础能力,#include<>用来包含开发环境提供的库,#include”"用来包含.c/.cpp文件所在目录下的头文件。注意:有些开发环境可以在当前目录下面自动收索(包含子目录),有些开发环境需要指定明确的文件路径名。switch()1.switch(c)语句中c可以是int,long,char,float,unsignedint类型?其实这个题H很基础,c应该是整型或者可以隐式转换为整型的数据,很明显不能是实型(float、double)。所以这个命题是错误的。constl. 8nst有什么用途?虽然const很常用,但是我相信有很多人仍然答不上来。(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。#ifndef/#define/#endif1.头文件中的#ifndef/#define/#endif干什么用?其实#ifndef、#define,#endif这些在u-boot、linux内核文件中经常见到,在这么大型的程序中大量使用,可见它的作用不可小觑。这些条件预编译多用于对代码的编译控制,增加代码的可裁剪性,通过宏定义可以轻松的对代码进行裁剪。#ifndef/#define/#endif最主要的作用是防止头文件被重复定义。全局变■和局部变全局变量和局部变量在内存中是否有区别?如果有,是什么区别?全局变量储存在静态数据库,局部变量在堆栈。其实,由于计算机没有通用数据寄存器,则函数的参数、局部变量和返I可值只能保存在堆栈中。提示:局部变量太大可能导致栈溢出,所以建议把较大数组放在main函数外,防止产生栈溢出。思考:如程序清单1.1所示。会出现怎样的情况?程序清单1.1大数组放在main函数中导致堆栈溢出intmain(intargc,char*argv[])(intiArray[1024*1024];return0;第:节数据存储与变量2.1变量的声明与定义如程序清单2.1所示会不会报错?为什么?如果不会报错,又是输出什么结果?程序清单2.1变量的声明与定义#include<stdio.h>staticinta;staticintb[];intmain(intargc,char*argv[])(printf("%d%d\nM,a,b[0]);return0;staticint a=8;staticint b[4] ;这个程序是不会报错的,并且连警告都不会出现。输出的结果是:80staticinta,这句程序是声明全局变量a;staticintb□,这句程序是声明全局数组变量b,并且是不完全声明,也就组下标。staticinta=8,这里才是定义全局变量a,staticintb[4],这里是定义全局变量b»局部变量与全局变■的较■请问如程序清单2.2所示输出什么?程序清单2.2局部变量与全局变量#include<stdio.h>staticinta=8;intmain(intargc,char*argv[])printf(n%d\nM,a);return0;)C语言规定,局部变量在自己的可见范围内会“挡住”同名的全局变量,让同名的全局变量临时不可见。即在J见范围内不能访问同名的全局变量。因此本程序输出为:4。char、int、float、double的数据存储请问如程序清单2.3所示,i和j输出什么?程序清单2.3数据存储floati=3;intj=*(int*)(&i);printf(ni=%f\nM,i);printf(0j=%#x\nn,j);i是毋庸置疑是:3.000000。但是j呢?3.000000?答案是否定的,j是输出:0x40400000.有人会问了,难道j瞎说,j输出0x40400000是有依据,是一个定值!由于i是float数据类型,而j是int数据类型。理论上说,j是取了i的地址然后再去地址,应该得到的就是ifr问题的关键就是float数据类型的存储方式和int数据类型不一样,float是占用4个字节(32位),但是float存储数法存储,最高位是存储数符(负数的数符是0,正数的数符是1);接下来8位是存储阶码;剩下的23位是存储局i=3.000000,那么3.000000(10进制)=11(2进制)=<v:shapeid=_x0000J1027style="WlDTH:40.5pt;HEKequationxml='121.1^<2Vtype="#_x0000_t75">(二进制)。数据在电脑中存储都是二进制,这个应该都没这里的数符为:0,阶码为:E-127=1,那么阶码为:E=128即为:10000000(2进制),尾数为:1000000000000。那么存储形式就是:01000000010000000000000000000000o这个数据转换成16进制就是(图2.1数据存储方式char、int、float,double的存储方式如图2.l所示。提问:如果i=-3.5的话,请问j输出多少?i=-3.500000j=0xc0600000这个希望读者自行分析。再问:如果如程序清单2.4所示。程序清单2.4数据存储doublei=3;intj=*(int*)(&i);printf("i=%lf\nn,i);printf(nj=%#x\nn,j);这样的话,j又输出多少呢?提示:double(8个字节(64位))的存储方式是:最高位存储数符,接下来11位存储阶码,剩下52位存储尾数是不是得不到你想要的结果?double是8个字节,int是4个字节。一定别忘记了这个。用这个方法也同时可以马式!容易忽略char的范围如程序清单2.5所示,假设&b=0x12ff54,请问三个输出分别为多少?程序清单2.5char的范围unsignedintb=0xl2ff60;printf(n((int)(&b)+1) =%#x\nn,((int)(&b)+1) );printf(n*((int*)((int)(&b)+1))=%#x\nn,*((int*)((int)(&b)+1)) );printf(n*((char*)((int)(&b)+1))=%#x\nH,*((char*)((int)(&b)+1)));很显然,&b是取无符号整型b变量的地址,那么(int)(&b)是强制转换为整型变量,那么力口1即为0x12ff54+1=(以((int)(&b)+1)^0x12ff55o图2.3指针加1取字符型数据由于((int)(&b)+1)是整型数据类型,通过(足*)(。明(&功+1)转化为了整型指针类型,说明要占4个字节,即:0x12ff56、0x12ff57,0x12ff58,再去地址*((int*)((int)(&b)+1))得到存储在这4个字节中的数据。但是很遗我们并不知道存储的是什么,所以我们只能写出0x**0012ff。**表示存储在0x12ff58中的数据。如图2.2所示。图2.2指针加1取整型数据以此类推,*((char*)((int)(&b)+1))=Oxff.如图2.3所示。但是,*((char*)((int)(&b)+1))输出的却是:Oxffffffff!问题出现了,为什么*((char*)((int)(&b)+1))不是Oxff,而是Oxffffffff?char型数据应该占用1个字节,为什ffffff?使用%d输出,printf(n*((char*)((int)(&b)+1))=%d\nH,★((char*)((int)(&b)+1)));结果为・1???问题出在signedchar的范围是:・128〜127,这样肯定无法储存Oxff,出现溢出。所以将printf(n*((char*)((int)(&b)+1))=%#x\nM,★((char*)((int)(&b)+1)));改成printf(n*((unsignedchar*)((int)(&b)+1))=%#x\n”,*((unsignedchar*)((int)(&b)+1)));就可以输出Oxff,因为unsignedchar的范围是:0〜255(0xff)。
1.iDa(34.04KB,卜,载次数:23)图2.1数据存储方式char||||||||-|8位(1个字节)Mllllllllllllllllllllllllll「ET32位(4个字节)X:由画皿皿画面面工版位"字节)double:||1I位| 52位 |:64位(8个字节)符号位阶码位 尾数2.ipg(25.49KB,卜载次数:20)图2.2指针加1取整型数据int4个字节int4个字节3.ipa(17.13KB.下载次数:17)图2.3指针加1取字符型数据第三节数学算法解决C语言问题3.1N!结果中0的个数1. 99!结果中后面有多少个0?谁跟你说过高数没用?数学是C语言的支撑,没有数学建模的支撑就没有那么经典的C语言算法!如果你能一个一个乘出来我算你狠!我也佩服你!0也就意味着乘积是10的倍数,有10的地方就有5.有5就不担心没2.10以内能被5整除的只有5,但是能被2整除多的去了。所以就简化成了这个数只要有一个因子5就一定对应一个0.所以可以得出下面结论:当0<n<5时,f(n!)=0;当n>=5时,f(n!)=k+f(k!),其中k=n/5(取整)。如程序清单3.1所示。程序清单3.1求N!中0的个数#include<stdio.h>intfun(intiValue)(intiSum=0;while(iValue/5!=0)(iSum+=(iValue/5);iValue/=5;}returniSum;)intmain(intargc,char*argv[])(intiNumberziZoreNumber;scanf("%d",&iNumber);iZoreNumber=fun(iNumber);printf(n%d\nn,iZoreNumber);return0;)所以99!后面有多少个0的答案是:99/5=19,19/5=3;3/5=0.也就是:19+3+0=22.这里延伸为N!呢,一样的,万变不离其宗!3.2N!结果中的位数1.请问N!结果中有几位数?数学!还是数学,数学真的博大精深,如果大学没有好好学数学,会成为你一辈子的遗憾。我们先假设:N!=10*A,我们知道10*ri0*2(不含10~2)之间是2位数,10.2~1。3(不含1(T3)之间是3位数,以此类推,(A+1)(不含10、(A+1))则是(A+1)位数,那就是说,我们只要求出A,即可知道N!有几位数。A=loglO(N!),N!=1*2*3 *N,那么A=Iogl0(l)+logl0(2)+ +loglO(N).这样好计算了吧!程序如程序清单6.2所示。程序清单6.2求N!结果中的位数#include<stdio.h>#include<math.h>intmain(intargczchar*argv[]){intiNumberzi=0;doublesum=0;printf("请输入iNumber:n);scanf(n%dn,&iNumber);for(i=1;i<(iNumber+1);i++){sum+=loglO(i);}printf(nN!有告d位\n”,(int)sum+1);return0;我们看下调试结果:请输入iNumber:10N!有7位请按任意键继续...第四节关键字、运算符与语句1.1static1. 如程序清单4.1所示,请问输出i、j的结果?程序清单4.1static#include<stdio.h>staticintj;voidfuni(void)(staticinti=0;i++;printf(Mi=%d",i);voidfun2(void){j=0;j++;printf(Mj=%d\nMzj);)intmain(intargc,char*argv[])(intk=0;for(k=0;k<10;k++){funl();fur)2();printf(M\nn);}return0;}答案:i=1 j=1i=2 j=1TOC\o"1-5"\h\zi = 3 j = 1i = 4 j = 1i = 5 j = 1i = 6 j = 1i = 7 j = 1i = 8 j = 1i = 9 j = 1i=10j=1请按任意键继续...很多人傻了,为什么呢?是啊,为什么呢?!由于被static修饰的变量总存在内存静态区,所以运行这个函数结束,这个静态变量的值也不会被销毁,函数候仍然能使用这个值。有人就问啊,为什么j一直是1啊。因为每次调用fun2()这个函数,j都被强行置。了。static的作用:(1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其4时仍维持上次的值;(2)在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;(3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块P(4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员11.2 for循环的深究如程序清单4.2所示,输出结果是什么?程序清单4.2for循环#include<stdio.h>intmain(intargc,char*argv[])(inti=0;for(i=0,printf("First=%d",i);printf(nSecond=%dMzi),i<10;i++,printf("Third=%d",i))(printf(nFourth=%d\nM,i);return0;这个题目主要考察对for循环的理解。我们先来看看运行程序会输出什么?TOC\o"1-5"\h\zFirst= 0 Second = 0 Fourth = 0Third= 1 Second = 1 Fourth = 1Third= 2 Second = 2 Fourth = 2Third= 3 Second = 3 Fourth = 3Third= 4 Second = 4 Fourth = 4Third= 5 Second = 5 Fourth = 5Third= 6 Second = 6 Fourth = 6Third= 7 Second = 7 Fourth = 7Third= 8 Second = 8 Fourth = 8Third= 9 Second = 9 Fourth = 9Third=10Second=10请按任意键继续...从输出我们就可以知道程序到底是什么运行:首先i=0,所以输出:Rrst=0;接着输出:Second=0;i<10成立,则输出:Fourth=0。就此完成接着i++,此时i=1,输出:Third=1;接着输出:Second=1;i<10成立,则输出:Fourth=1«••推。尺 sizeof如程序清单4.3所示,sizeof(a),sizeof(b)分别是多少?程序清单4.3sizeof#include<stdio.h>intmain(intargc,char*argv[])chara[2][3];shortb[2][3];printf(nsizeof(a)=%d\nn,sizeof(a));printf("sizeof(b)=%d\nn,sizeof(b));return0;)这个题目比较简单,由于char是1个字节、short是2个字节,所以本题答案是:sizeof(a)=6sizeof(b)=12请按任意键继续...好的,再来看看如程序清单4.4所示,sizeof(a),sizeof(b)分别是多少?程序清单4.4sizeof♦include<stdio.h>intmain(intargc,char*argv[])(char*a[2][3];short*b[2][3];printf(nsizeof(a)=%d\nn,sizeof(a));printf("sizeof(b)=%d\nn,sizeof(b));return0;}是数组指针呢,还是指针数组呢?这里涉及*和□和优先级的问题。我告诉大家的是这两个数组存放的都是指针什么,在后续章节会详细解释,然而指针变量所占的字节数为4字节,所以答案:sizeof(a)=2424sizeof(b)=24请按任意键继续..・++i和i++1.或许大家都知道,++i是先执行i自加再赋值,但是i++是先赋值再自加,但是还有隐藏在后面的东西呢inti=0;intiNumber=0;iNumber=(++i)+(++i)+(++i);C-Free编译输出是:7,有的编译器输出是:9。这两个答案都是对的,编译器不同所不同。7=2+2+3;9=3+3,答案是7的先执行(++i)+(++i)再执行+(++i),但是答案是9的是一起执行。这只是前奏,先看几个让你目瞪口呆的例子。编译环境是VS2010。inti=0;intiNumber=0;iNumber=(i++)+(++i)+(++i);printf(niNumber=%d\nn,iNumber);这里输出是:4!!!4=1+1+2。inti=0;intiNumber=0;iNumber=(++i)+(i++)+(++i);printf(MiNumber=%d\nn,iNumber);这里输出是:4!!!4=1+1+2ointi=0;intiNumber=0;iNumber=(++i) +(++i)+(i++);printf("iNumber=%d\nn,iNumber);这里输出是:6!!!6=2+2+2o这里至少能说明两个问题,其一,先执行前面两个,再执行第三个;其二,。++)这个i的自加是最后执行!inti=0;intiNumber=0;iNumber=(++i)+(i++)+(++i)+(++i)+(i++);printf(niNumber=%d\nn,iNumber);这个会是多少?!答案是:10!!!10=1+1+2+3+3!不同的编译器或许会存在不同的答案,希望读者自行进行验证。scanf()函数的输入如程序清单4.5所示,运行程序,当显示EnterDividend:,输入的是a,按下Enter之后程序会怎么运彳:程序清单4.5scanf()函数的输入#include<stdio.h>intmain(void)floatfDividend,fDivisorzfResult;printf(MEnterDividend:H);scanf(H%fnz&fDividend);printf(HEnterDivisor:n);scanf(H%fnz&fDivisor);fResult=fDividend/fDivisor;printf("Resultis:%f\nM,fResult);return0;这个问题有人会说,肯定是显示EnterDivisor:要我输入除数咯。是这样吗?答案是:如果你在日iterDividend:之后输入非数字,按下Enter之后显示的不是EnterDivisor:要你输入除数此就运行结束,显示一个不确定答案,这个答案每一次都会变。如果你在EnterDivisor:之后输入非数字,按显示的不是Reslut的结果,而是程序到此就运行结束,显示一个不确定答案,这个答案每一次都会变。由于scanf。使用了%f,当输入数字的时候,scanf()将缓冲区中的数字读入fDividend,并清空缓冲区。由于我数字,因此scanf。在读入失败的同时并不会清空缓冲区。最后的的结果是,我们不需要再输入其他字符,scant读取缓冲区,每次都失败,每次都不会清空缓冲区。当执行下一条scanf()函数读取除数时,由于缓冲区中有数等待用户输入,而是直接从缓冲区中取走数据。那么防止输入非数字的程序应该怎样呢?#include<stdio.h>intmain(intargc,char*argv[])float fDividend,fDivisor,fResult;intiRet;charcTmpl[256];printf("EnterDividend\nn);iRet=scanf(n%fn,&fDividend);if(1==iRet)(printf("EnterDivisor\nH);iRet=scanf( ,&fDivisor);if(1==iRet)(fResult=fDividend/fDivisor;printf("Resultis%f\nM,fResult);}else(printf(nInputerror,notanumber!\nn);gets(cTmpl);return1;}}elseprintf(HInputerror,notanumber!\n");gets(cTmpl);return1;return0;}scanf()函数的返回值如程序清单4.6所示,请问输出会是什么?程序清单4.6scanf()函数的返回值intazb;printf(\nn,scanf(n%d%dn,&a,&b));输出输入这个函数的返回值?!答案是:2。只要你合法输入,不管你输入什么,输出都是2。那么我们就要深这个函数。scanf()的返回值是成功赋值的变量数量。1.7 const作用下的变量阅读如程序清单4.7所示,想想会输出什么?为什么?程序清单4.7const作用下的变量constintiNumber=10;printf(niNumber=%d\n",iNumber);int*ptr=(int*)(&iNumber);*ptr=100;printf(niNumber=%d\nu,iNumber);8nst的作用在第四章已经详细讲了,这里就不再累赘,答案是:10,10。这里补充一个知识点:constint*p 指针变量p可变,而p指向的数据元素不能变int*constp 指针变量p不可变,而p指向的数据元素可变constint*constp 指针变量p不可变,而p指向的数据元素亦不能变1.8 *ptr++、*(ptr++),*++ptr、*(++ptr),++(*ptr)、(*ptr)++的纠缠不清1. 如程序清单4.8所示程序,输出什么?程序清单4.8*ptr++intiArray[3]={1,11,22);int*ptr=iArray;printf(n*ptr++=%d\nn,*ptr++);printf(M*ptr=\nH,*ptr);纠结啊,是先算*ptr还是ptr++;还是纠结啊,ptr是地址加1还是偏移一个数组元素!这里考查了两个知识点,其一:*与++的优先级问题;其二,数组i++和++i的问题。*和++都是优先级为2,算符,自右向左结合。所以这里的*ptr++和*(ptr++)是等效的。首先ptr是数组首元素的地址,所以ptr++是偏移一个数组元素的地址。那么ptr++运算完成之后,此时的ptr是由所以第二个输出*ptr=11。如图4.1所示。那么倒回来看第一个输出,ptr++是在执行完成*ptr++之后再执行的,=1O如程序清单4.9所示程序,输出会是什么?程序清单4.9*++ptrintiArray[3]={1,11,22);int*ptr =iArray;printf(n*++ptr=%d\nn,*++ptr);printf(M*ptr=%d\nn,*ptr);这个解释和上面解释差不多,就是++ptr和ptr++的差别,所以这里的两个输出都是:11。同样的道理,*++p是等效。再如程序清单4.10所示,输出又会是什么?程序清单4.10(*ptr)++intiArray[3]={1,11,22);int*ptr=iArray;printf(n(*ptr)++=%d\n”,(*ptr)++);printf(n*ptr=%d\nn,*ptr);这个的输出是:1,2。原因请读者分析。4.ipa(94.96KB,卜.载次数:11)图4.1ptr++11122ptrptr++ptr指向的是数组元素a[0]=1的地址,
ptr++指向的是数组元素a[1]=11的地址第五节C语言中的细节1.1“零值”比较写出floatx与''零值”比较的if语句。首先要知道float是有精度的,不能直接与0相比较或者两数相减与。相比较。float能保留几位小数格案是6位。既然如此,那么就应该这么写:if((x>0.000001)&&(x<-0.000001)).宏定义定义一个宏,返回X、丫中的较大值。这个题目其实很简单,但是在很多笔试中都会拿出来考试,并且出错率很高,原因只有一个,忽略细节(优先级的问题,实在搞不明白就加括号吧,你好理解,别人一看也懂)。终究还是细节决定成败。♦defineMAX((X),(Y)) ((X)>=(Y)?(X):(Y))宏定义两个数相加请问如程序清单5.1输出什么?程序清单5.1宏定义两数相加#defineDOUBLE(x)x+xintmain(intargc,char*argv[])(intiNumber=0;printf(n%d\nn,10*DOUBLE(10));return0;)其实这个程序非常简单,学习C语言一周就应该理解是什么意思,但是一般会出错的的地方都在细节。其实这个程序输出是110。可能有人会问,不是10先DOUBLE嘛,然后乘以10,不是200嘛。是啊,想法是好的,我想这个程序的“原意”也应该是这样,但是就是由于优先级的问题,打破了我们的愿望。如果要得到200,那么就应该是这样宏定义:#defineDOUBLRx)((x)+(x))。我想,无论我加多少层括号都不算违法吧。递归运算如程序清单5.2所示,输出什么?程序清单5.2递归运算#include<stdio.h>intfunc(inta)if(a==0)returna;)printf("为d\n",func(a++/2));returna;intmain(intargc,char*argv[])(printf(H%dn,func(7));return0;)答案:0,2,4,8这里把7送进去,那么func(a++/2),先执行7/2=3,然后a++=8,此时返回3;接着把3送进去,func(a++/2),先执行3/2=1,然后a++=4,此时返回1;接着把1送进去,func(a++/2),先执行1/2=0,然后a++=2,此时返回0;接着把。送进去,此时直接返回0,递归结束。递归最容易忽略的细节是,由于递归次数过多,容易导致堆栈溢出。让人忽略的贪心法如程序清单5.3所示,程序输出什么?程序清单5.3贪心法intk=8;inti=10;intj=10;k*=i+++j;
printf(n%d\nM,k);贪心法,就是一次性能尽可能多得吃运算符,那么这里k*=i+++j,加上括号之后就是这样:k=k*((i++)+j);这样的话就很简单可以得出答案为:160。性能优化.对如程序清单5.4所示进行性能优化,使得效率提高。程序清单5.4性能优化intiValuel;intiValue2;iValuel=1234/16;iValue2=1234%32;对于嵌入式进行除法是很消耗效率的,能使用移位完成最好使用移位完成。iValuel=1234>>4;iValue2=1234-((1234»5)«5);1234/16=77;1234%32=18。而十进制:1234转化成二进制:010011010010.1234>>4=000001001101,转化为十进制即为:77;1234>>5=000000100110,((1234>>5)<<5)即为010011000000,转化为十进制即为:1120,1234-1216=18。第六节数组与指针数组、数组元素、指针的大小1.如程序清单1.如程序清单61所示,程序输出什么?程序清单6.1数组、数组元素、指针的大小#include<stdio.h>intmain(intargc,char*argv[])(int*p;TOC\o"1-5"\h\zprintf( nsizeof(p) = %d \nn , sizeof(p) );printf( 11sizeof(*p) = %d \nM , sizeof(*p));inta[100];printf( "sizeof(a) = %d \nn , sizeof(a) ) ;printf( "sizeof(a[100]) = %d \n" , sizeof(a[100]));printf( nsizeof(&a) = %d \nM , sizeof(&a) );printf( nsizeof(&a[0]) = %d \n" , sizeof(&a[0]));return0;}p是指针,任何数据类型的指针都是占4字节;*p是一个指针指向的int数据,int型数据占用4字节;a是数组,除了sizeof(a)和&a之外,当a出现在表达式中时,编译器会将a生成一个指向a[0]的指针,而这里数组的大小。答案:TOC\o"1-5"\h\zsizeof(p) = 4sizeof(*p) = 4sizeof(a) = 400
sizeof(a[100])=sizeof(&a) =4sizeof(&a[0]) =4请按任意键继续...广东省的省政府和广州市的市政府都在广州如程序清单6.2所示,如果ptr=Ox10000000,那么剩下三个输出是多少?程序清单6.2数组首地址与数组首元素地址intiArray[3]={1,2,3};int*ptr=iArray;printf(nptr =%#x\nM,ptr);printf(niArray =%#x\nn,iArray);printf(n&iArray =%#x\nn,&iArray);printf(n&iArray[0] =%#x\nn,&iArray[0]);iArray是数组名,由6.1节对a的说明,可iArray知同时也是数组首元素的地址,为Ox10000000;&iArray是娄这是毫无疑问的。&iArray[O]是数组首元素的地址,也是0x10000000。也就是说数组的首地址和数组首元素的此因为广东省的省政府和广东省的首号城市广州市的市政府都在广州,两者的地址是相等的。如图6.1所示。如程序清单6.3所示,ptr=0x10000000,那么这三个输出会是多少?程序清单9.3数组首地址加1、数组首元素地址加1intiArray[3]={1,2,3);int*ptr=iArray;printf(nprintf(n&iArray+l%#x\nH,&iArray+l);printf(niArray+l=%#x\nH,iArray+1);printf(n&iArray[0]+l=%#x\nM,&iArray[0]+1);答案是,第一个输出:0x1000000C;第二个、第三个输出:0x10000004o&iArray是数组的首地址,那么&iArray+1是偏移一个数组单元,因为站在全国的角度报全国各省政府的天气预:省省政府之后就为湖南省省政府;如图6.1所示。&iArray[O]是数组首元素的地址,&iArray[0]+1是偏移一个数学好比站在广东的角度报广东各城市的天气预报,报完广东省首号城市广州的天气预报之后就是为广东省第二号:报。1.3 数组作为函数参数,是传什么进去了如程序清单6.4所示,程序输出什么?程序清单6.4数组作为函数参数voidtext(charcArray[])(printf(nsizeof(cArray)=%d\nn,sizeof(cArray));)intmain(intargc,char*argv[])(charcArray[]=naBcDeFn;printf(nsizeof(cArray)=%d\nn,sizeof(cArray));text(cArray);return0;}这里考查两个知识点,其一,sizeoffnstrlen();其二text(charcArray⑴形参到底是什么?答案是7,4。看到答案我想大家就应该明白上面两个问题了吧。到底是传值还是传址一定要搞明白哦。如程序清单6.5程序,输出会是什么?程序清单6.5指针相减#include<stdio.h>intmain(intargc,char*argv[])(inta[2]={3,6};int*p=a;int*q=p+1;printf(nq-p=%d\nn,q-p);printf(n(int)q-(int)p=%d\nn,(int)q-(int)p);return0;)用数学方法到可以做出q-p=1这个答案,但是(int)q-(int)p的答案。指针,指针的强大。由于指针加1,内不sizeof(int),但是int(q)和int(p)就不再是指针,而是一个整形数据。所以(int)q-(int)p=4,1.5 指针加1到底是加什么如程序清单6.6所示,请问p1+5=_;p2+5=_;程序清单6.6指针加1#include<stdio.h>intmain(intargc,char*argv[])(unsignedchar*pl;unsignedlong*p2;pl=(unsignedchar*)0x801000;p2=(unsignedlong*)0x810000;printf(npl+5=%#x\nHzpl+5);printf(np2+5=%#x\nM,p2+5);return0;)由于p+n=p+n*sizeof(p的数据类型),所以答案为:pl+5=0x801005p2+5=0x810014请按任意键继续...1.6 数组与指针的概念如程序清单6.7所示,解释程序。程序清单6.7数组与指针的概念inta;int*a;int**a;inta[10];int*a[10];int(*a)[10];int(*a)(int);int(*a[10])(int);答案:一个整型数:一个指向整型数的指针;一个指向指针的指针,它指向的指针是指向一个整型数:一个有10个整型数的数组;一个有10个指针的数组,该指针是指向一个整型数的;一个指向有10个整型数数组的指针;一个指向函数的指针,该函数有一个整型参数并返回一个整型数;一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数。这个题目很经典,很多公司的笔试题目都会截取上面部分出来考试。特别是e和f,哪一个是数组指针,哪一个5g和h哪一个是函数指针,哪一个又是指针函数。1.7 数组与指针的糅合如程序清单6.8所示,输出会是什么?程序清单6.8数组与指针的糅合应用1intarr[]={6,7,8,9,10};int*ptr=arr;*(ptr++) +=123;printf(H%dz%d”,*ptr,*(++ptr));这个题目来源于华为的一道C语言笔试题,答案是:8,8Jptr=arr,这里ptr取得是数组arr{]的首元素地址,*(ptr-这里是ptr++,此时*(ptr)即为:6,那么*(prt++)+123=129,执行完*(ptr++)+=123之后,*(ptr)=7。跟*(.8这个值是没有半点关系,由于执行了*(++ptr),所以此刻的*(ptr)为8,所以输出为:8,8,如程序清单6.9所示,输出会是什么?程序清单6.9数组与指针的糅合应用2intmain(intargc,char*argv[])inta[5]={1,2,3,4,5};int*ptr=(int*)(&a+1);printf(n%d,%dn,*(a+1),*(ptr-1));}这个题目要求对指针的理解要比较透彻。由于*(a+1)和a[1]是等效的,则*(a+1)=a[1]=2。&a指的是指向与的指针,&a+1不是首地址+1,系统会认为加了一个a数组的偏移量,即偏移了一个数组的大小,因此ptr指市是*(ptr+5),既然如此,那么*(ptr-1)当然就是a[4]=5咯。所以这个题目的答案是:2,5。其实这个题目还有一个延伸,int*ptr=(int*)((int)a+1),*ptr是多少。答案是:2000000。假设&a[0]=0x10000000,由于存储方式是小端模式,那么a[0]=1和a[1]=2的存储方式如图6.2所示。因为a=0x10000000,而(int)a将这个地址强制转化为了int型数据,((int)a+1)=0x10000001,经过(int成了地址,ptr=(int*)((int)a+1),由于ptr是指向int型的指针,*ptr占4个字节,*ptr所占字节即为:0x00,0x0那么*ptr即为0x02000000。如程序清单6.10所示,如果ptrl为Ox10000000,那么三个输出分别为多少?程序清单9.10数组与指针的糅合应用3intiArray[7]={1,2,3,4,5,6,7};int*ptr1 =iArray;int*ptr2=&iArray[5];printf("ptr2 =%#x\nn,ptr2);printf(”ptrl =%#x\nH,ptrl);printf(nptr2-ptrl=%d\nH,ptr2-ptrl);很明显iArray是整型数据类型数组,那么ptr2=ptrl+5*sizeof(int)=0x10000014。很多同学立马就会脱口而t:=20嘛!真的是这样吗?其实答案是:5!解释之前,我们先来看这个程序:intiArray[7]={1,2,3,4,5,6,7};char*pl =(char*)iArray;char*p2 =(char*)&iArray[5];printf("p2-pl=%d\nM,p2-pl);这个程序的输出是:20。因为指针类型是char*,char是1个字节;而上面*ptr1和*ptr2是int*,所以答案是:如果是:short*pl =(short*)iArray;short*p2 =(short*)&iArray[5];贝ijp2-p1就是为:10。这里还有一个延仰:intiArray[7]={1,2,3,4,5,6,7};int*ptrl=iArray;int*ptr2=&iArray[5];printf(”ptr2 =%#x\nn,ptr2);printf(”ptrl =%#x\nn,ptrl);printf(nptr2-ptrl=%d\nn,(int)ptr2-(int)ptrl);这样输出答案是多少呢?20!阅读程序,输出什么?char*cArray[3]={HabodefH,”123456”,Hjxlgdxn};printf(nsizeof(cArray[0])=%d\nH,sizeof(cArray[0]));我相信有较多的人的答案是:6或者7。原因是忽略了*cArray[3]这是一个指针数组,也就是说cArray[3]数组中不而不是字符串常量。在C语言笔试陷阱与难点第一阶段讲过,只要是指针变量,其大小就是:4。所以这里毋克应该是:4o你要是存在怀疑,可以输出cArray[3]数组的各个元素看看是不是指针。printf( ncArray[0] = %#x \nn , cArray[0]);printf( ncArray[1] = %#x \n" , cArray[1]);printf( HcArray[2] = %#x \n" , cArray[2]);运行程序输出为:sizeof(cArray[0])=4cArray[0]=0x415840cArray[1]=0x415770cArray[2]=0x415768请按任意键继续...读者亦可输出指针所指向的字符串:printf( ncArray[0] = %s \nn , cArray[0]);printf( HcArray[1] = %s \nn , cArray[1]);printf( HcArray[2] = %s \nn , cArray[2]);运行输出为:cArray[0]=abcdefcArray[1]=123456cArray[2]=jxlgdx请按任意键继续...2. 阅读下列程序,输出什么?typedefint(init_fnc_t)(void);externintarch_cpu_init(void);externintboard_early_init_f(void);init_fnc_t*init_sequence[]={arch_cpu__initzboard_early_init_ffNULL,);intarch__cpu__init(void)(printf(MThisisarch_cpu_init\nn);return0;)intboard_early_init_f(void)(printf(HThisisboard_early_init_f\nM;voidhang(void)printf(**Error!\n");while(1);)intmain(intargc,char*argv[])(init_fnc_t**init_fnc_ptr;for(init_fnc_ptr=init_sequence; nc_ptr; init_fnc_.ptr)(if((*init_fnc_ptr)()!=0)(hang();}}return0;}这个题目是我在阅读u-boot-2012.10源码的时候稍作修改从中提取出来的。这个题目将指针数组、函数指针等体。Thisisarch__cpu_initThisisboard_early_init_f请按任意键继续...1.9 数组指针如程序清单6.11所示,程序输出什么?程序清单6.11数组指针#include<stdio.h>intmain(intargc,char*argv[])(inta[][4]={1,3,5,7,9,11,13,15,17,19,21,23};int(*p)[4],i=2,j=l;p=a;printf("%d\n",*(*(p+i)+j));return0;}答案是:19。不能理解?好吧,如果我告诉你**(p+1)=9,*((*p)+1)=3!到这里能理解了吗?如果还是不能理解,Ok,p有4个整型数据的数组,那么p如果加1,地址是不是得偏移4个整形数据的地址,而p等于数组a的首元素地ill组,也就意味着p是双重指针了。1.10再论数组指针与数组首地址如程序清单6.12所示,已知&a[0][0]=0x22fe70,想想会是输出什么?程序清单6.12数组指针与数组首地址intmain(intargc,char*argv[])inta[8][8]={1,2,3,4};int(*ptrl)[8] =a;int(*ptr2)[8][8]=&a;int*ptr3 =&a[0][0];printf(Mptrl =%#x\nn,ptrl);printf(H&a[0] =%#x\nn,&a[0]);printf(nptrl+1 =%#x\nn,ptr1+1);printf(M&a[0]+l=%#x\n\nH,&a[0]+l);printf(Hptr2 =%#x\nn,ptr2);printf(H&a=%#x\nn,&a);printf(Hptr2+l=%#x\nn,ptr2+l);printf(H&a+l =%#x\n\nH,&a+l);printf(nptr3 =%#x\n",ptr3);printf(M&a[0][0] =%#x\nn,&a[0][0]);printf(nptr3+l =%#x\nM,ptr3+l);printf(H&a[0][0]+l=%#x\n\nn,&a[0][0]+1);
这个题目涉及到两个知识点,其一,讲烂了的数组首元素地址和数组的首地址;其二,数组指针和指针数组的先看第三个指针,int*ptr3=&a[0][0];这个毫无疑问,是将数组a的首元素地址赋给指针ptr3,由于是int型数组则是偏移一个int型大小,即偏移4个字节,那么ptr3这一组的输出即为:ptr3 =0x22fe70&a[0][0] =0x22fe70ptr3+l =0x22fe74&a[0][0]+l=0x22fe74我们再看第二个指针,int(*ptr2)[8][8]=&a;根据之前我们讲过的,这个是取数组a的首地址,ptr2的解释京向二维数组网[8]的指针,那么ptr2+1则是偏移了一个二维数组[8][8]的地址,即为4*8*8=256(0x100)个字节ptr2这一组的输出即为:ptr2 = 0x22fe70&a = 0x22fo70ptr2+l = 0x22ff70&a+l = 0x22ff70剩下第一个指针,这个和6,9节差不多,int(*ptr1)[8] =a淇实它是等价于int(*ptr1)网=&a[8];那1个指向一维数组[8]的指针,如果我们这么理解a[8H8]={a1[8],a2[8],…a8网}(当然这个理解是错误的),那Z向a1[8],那么当ptr1+1就是指向a2[8],也就是偏移了一个含有8个int型数据的数组,即4*8=32(0x20)个字这一组的输出即为:ptrl = 0x22fe70&a[0] = 0x22fe70ptrl+1 = 0x22fe90&a[0]+l = 0x22fe90这里再一次重复讲一下数组指针和指针数组。int(*p)[8] p是指向一个含有8个整型数据的数组的指针(数组指针)int*p[8]p是一个含有8个指针型变量的数组(指针数组)&iArray &iArray+1&iArray &iArray+1【广东省政府】 【湖南省政府】5.iDa(268.75KB.F载次数:10)图6.1省政府和市政府iArray和 iArray+1和&iArray[0]&iArray[0]+1所指向的地址所指向的地址【广州市政府】【深圳市政府】gj£q(374.54KB,下载次数:11)6.2地址加数据10x10000007—AwlAAACCCG—►0x00UX1UUUUUUbnonnone0x00UX1UUUUUUDAv1nonnoo/i0x02UXIUUUUUU4Hw-1AAAAAAO0x00UX1UUUUUUJC"non0x00UX1UUUuuuznonnoni0x00UXIUUUUUUI0x10000000—QXQ1 Hvionnnon70x00UXIUUUUUU/j0x00UXlUUUUUUbAv1HAHCCCU0x00UX1UUUUUUuAv1onnnnnAj 1 •一1 0x02uxiuuuuuuqAwAAHAAAAQ0x00UX1UUUUUUonv-4nonnnno0x00UX1UUUUUUZAvinonnnn*i.j■0x00UX1uuuUUU10x10000000——►0x01第七节结构体与联合体1.1结构体内存对齐问题I. 这个程序本是我写来验证结构体内存对齐问题,但是我在linux系统和windows系统下的答案让我:便将其加进本书。如程序清单7.1所示,程序输出会是什么?程序清单7.1结构体的内存对齐问题#include<stdio.h>structDate{TOC\o"1-5"\h\zint year ;int month ;int day ;structDateType{TOC\o"1-5"\h\zint year ;int month ;int day ;}birthday;structstudent{int iNum ;charcName[30];float fScore ;char cSex ;doublemenber;}people;intmain(intargc,char*argv[])(printf(Hsizeof(structDate)=%d\n\n”,sizeof(structDate));printf(nsizeof(structDateType)=%d\nnsizeof(structDateType));printf("sizeof(birthday)=%d\n\n",sizeof(birthday)printf("sizeof(birthday)printf(n&birthday.year=%d\nn,&birthday.year);printf(n&sizeof.month=%d\nH,&birthday.month);printf(n&sizeof.day=%d\n\n",&birthday.day);TOC\o"1-5"\h\zprintf(nsizeof(structstudent)=%d\nn,sizeof(structstudent) );printf(nsizeof(people)=%d\n\nnrsizeof(people) )printf(M&people.iNum=%d\nHf&people.iNum )printf(n^people.cName=%d\nHr&people.cName )printf(n&people.fScore=%d\nM,&people.fScore )printf(n&people.cSex=%d\nn,TOC\o"1-5"\h\z&people.cSex );printf(n&people.menber =%d\n\n”,&people.menber );printf(Hsizeof(people.menber)=%d\n\n",sizeof(people.menber));return0;}传统在windows下,结果大家都应该知道,我现在就直接把window下和linux下结果直接贴出来,大家看看。结果有质疑,大可上机试试,毕竟眼见为实。)sizeof(structDate)=12sizeof(structDateType)=12&birthday.year=4210832&sizeof.month=4210836&sizeof.day=4210840sizeof(structstudent)=56sizeof(people) =56&people.iNum = 4210848&people.cName = 4210852&people.fScore = 4210884&people,cSex = 4210888&peop1e.menber = 4210896sizeof(people.menber)=8请按任意键继续..・上面是GFree中运行的结果,你可以试试VC等,答案依然如此。我们再来看看linux下结果:root@zhuzhaoqi-desktop:/home/zhuzhaoqi/C/prog1.34#./progsizeof(structDate)=12sizeof(birthday) =12&birthday.year =134520948&sizeof.month =134520952&sizeof.day =134520956sizeof(structstudent)=52sizeof(people) =52&people.iNum = 134520896&people.cName = 134520900&people.fScore = 134520932&people.cSex = 134520936&people.menber = 134520940sizeof(people.menber)=8这是linux下编译的结果。加粗标注区域够让你吃惊吧!说实话,看到第一眼,我也傻了。为什么,我们再看下划线标注区域,people.cSex在windows下联系上下确个字节,可是,可是为什么在linuxF只占用4个字节!!原来,在linux中以4个字节为开辟单元,即不足4个开辟4个,多于4个的继续开辟4个,多出的部分放进另一个4structstudent{intiNum; /*开辟4个字节★/charcName[30];/*开辟32个字节★/floatfScore ; /*开辟4个字节*///开辟4个字节,自己用1个字节,剩下3个,不足以存储menber*/charcSex;doublemenber;/*所以这里重新开辟4+4个字节*/}people;所以我们得出的答案是:4+32+4+4+8=52。但是,我们一直使用的windows下,以最大单元为开辟单位,即系统先检查结构中最大单位为double8个字节节为单位。student在Linux和windows下内存开辟如图7.1和图7.2所示。结构体在STM32的应用如程序清单7.2所示程序是截取STM32固件库中的一段代码,请问输出是什么?程序清单7.2结构体在STM32的应用#include<stdio.h>typedefvolatileunsignedintvui32;typedefstruct{vui32CRL;vui32CRH;vui32IDR;vui32ODR;vui32BSRR;vui32BRR;vui32LCKR;}GPIO_TypeDef;#defineGPIOA (GPIO_TypeDef*)0x10000000#defineGPIOLED (GPIO_TypeDef*)GPIOAvoidfunc(GPIO_TypeDef*GPIO)(printf(HGPIO->CRL=%#x\nn,&(GPIO->CRL));printf(MGPIO->CRH=%#x\nn,&(GPIO->CRH));printf(nGPIO->LCKR=%#x\nn,&(GPIO->LCKR));)intmain(intargc,char*argv[])(printf(Hsizeof(GPIO_TypeDef) =%d\nM,sizeof(GPIO_TypeDef));printf(HGPIOLED=%#x\nH,GPIOLED);func(GPIOLED);return0;)如果使用过STM32的固件函数库的话,应该对这个结构体不会陌生,STM32固件函数库就是这样,通过“大行体和指针实现对一大堆寄存器的配置,在—map.h这个头文件中,定义很多这样的结构体。这样做有什么好处呢抽象出来了,上面7个寄存器就是每个GPI。口寄存器的共有特性,那么只要给定某一个GPIOU的映射地址,过这个结构体得到每个寄存器的地址。能这么做很巧的是ARM的MCU每个寄存器的偏移量都是4个字节或者2能使用结构体完成,如果有一天出现了3个字节的偏移量,我想此时结构体也就没辙了。答案是:sizeof(GPIO_TypeDef) =28GPIOLED=0x10000000GPIO->CRL=0x10000000GPIO->CRH=0x10000004GPIO->LCKR=0x10000018请按任意键继续..・确实很巧妙,方便!结构体与指针已知如下所示条件。structstudent{TOC\o"1-5"\h\zlongint num ;char *name ;shortintdate ;char sex ;shortintda[5] ;)*P;p=(student*)0x1000000;那么请问,以下输出什么?,sizeof(*p) );printf(Msizeof,sizeof(*p) );printf(nsizeof(student)=%d\n”,sizeof(student));TOC\o"1-5"\h\zprintf("p =%#x\nH,p );printf(np+0x200 =%#x\nn,p+0x200 );printf(H(char*)p+0x200 =%#x\nM,(char*)p+0x200 );printf(n(int*)p+0x200 =%#x\nM,(int*)p+0x200 ).第一个输出不解释,内存对齐问题,结构体指针,答案为:24。第二个输出答案为:24。第三个输出,为已知,答案为:0x1000000。第四个输出,由于p此时是结构体类型指针,那么p+0x200=p+0x200*sizeof(student)=所以p+0x200=p+0x200*24=0x1000000+0x3000=0x1003000o第五个输出,由于p被强制转换成了字符型指针,那么p+0x200=0x1000200。第六个输出同理为:p+0x200=0x1000800.联合体的存储如程序清单7.3所示,程序输出什么?程序清单7.3联合体的存储union{inti;struct{charL;charH;}N;intmain(intargc,char*argv[])(N.i=0x1234;printf(HN.Bity.L=%#x\nnzN.Bity.L);printf(MN.Bity.H=%#x\nn,N.Bity.H);return0;)结构体的成员是共用一块内存,也就是说N.i和N.Bity是在同一个地址空间中。那么好办了,但是要注意,CP模式,所以低字节存储在低地址中,高字节存储在高地址中。那么N.Bity.L是取了低地址,也就是得到低字节,N.Bity.H是取了高字节,即为0x12。在电脑中,int是占4字节,所以存储方式如图10.3所示。其实这里有一个很巧妙的用法可以用于C51单片机中,为了与上面不重复,假设C51的存储模式是大端模式。在给定时器赋初值的时候,要将高八位和低八位分别赋给模式1定时器的高位预置值和低位预置值,有这么个式寸THx=(65536-10000)/256;TLx=(65536-10000)%256.那么我们就可以让这样写这个程序union{unsignedinti;struct{unsignedcharH;unsignedcharL;}N;intmain(intargc,char*argv[])N.i=65536-10000;THx=N.Bity.H;TLx=N.Bity.L;return0;}这样很方便并且高效地将高低位置好,其实单片机是一个很好学习C语言的载体,麻雀虽小,但是五脏俱全。65536-10000=55536=0xD8F0.由于在单片机中,int是占2字节,那么存储方式如图7.4所示。结构体在联合体中的声明如程序清单7.4所示,请问:printf("%d\n",sizeof(T.N));printf("%d\n",sizeof(T));输出什么?程序清单7.4结构体在联合体中的声明unionT
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 外墙彩色喷涂施工方案
- 施工现场的安全文化建设方案
- 建筑工程总体协调管理方案
- 储备粮仓库规划设计方案
- 水电工程施工现场沟通方案
- 农田无人机监测与管理技术方案
- 水电站设备故障应急处理方案
- 消防设备材料采购方案
- 给水管网漏水检测方案
- 土方施工过程记录管理方案
- 南昌地铁保护管理办法
- QC/T 476-2025客车防雨密封性要求及试验方法
- 活动售票策划方案
- DB11∕T 512-2024 建筑装饰工程石材应用技术规程
- 2025+CACA子宫内膜癌诊疗指南解读
- 2022变压器油枕技术培训
- 电力工程施工规范
- 配套课件-《中文版AutoCAD-2017基础教程》
- DL∕T 1522-2016 发电机定子绕组内冷水系统水流量 超声波测量方法及评定导则
- 意识障碍的判断及护理
- DZ∕T 0213-2020 矿产地质勘查规范 石灰岩、水泥配料类(正式版)
评论
0/150
提交评论