c语言函数调用问题,请问我哪里错了?调用子函数来排列顺序

C语言函数声明时自定义函数与主函数顺序先后有关问题会有影响吗 - C语言当前位置:& &&&C语言函数声明时自定义函数与主函数顺序先后有关问C语言函数声明时自定义函数与主函数顺序先后有关问题会有影响吗www.MyException.Cn&&网友分享于:&&浏览:36次C语言函数声明时自定义函数与主函数顺序先后问题会有影响吗?我要在主函数中调用一个自定义名为jolly函数,那么
#include&stdio.h&
void&jolly(void)
int&main(void)
#include&stdio.h&
int&main(void)
void&jolly(void)
如上两种情况,我把jolly函数的声明放在main之前和之后,有何不同?
------解决方案--------------------只要在调用之前,顺序无所谓------解决方案--------------------要看编译器。
第一种些方法在所有的编译器上应该都是可以的,第二种就未必了。------解决方案--------------------声明在main之前,肯定是没问题的
你的之后是一个什么状况?且C与C++是有区别的
int&main(void)
&&&jolly();&&//这个,对C++是有问题的,jolly没申明就调用.而C的可以
void&jolly(void);
以下这样可以的
int&main(void)
void&jolly(void);&//jolly没在main中调用
以下这样对C++也可以(但C不可以,除非void&jolly(void)前面只有定义或声明)
int&main(void)
&&void&jolly(void);
&&jolly();
#1楼说的是没错的,要在调用之前
但对C来说,也可以说没关系,因为C调用函数可以不声明,但这个只是语法上可以,实际可能会导致不正确的结果
(如类型不是int兼容的,对C来说,所有未声明的函数参数及返回都认为是int)
所以,合理的写法,要把函数的声明放在调用函数前面
------解决方案--------------------这个就是c和c++编译器的区别。。
12345678910
12345678910
12345678910 上一篇:下一篇:文章评论相关解决方案 12345678910 Copyright & &&版权所有An error occurred on the server when processing the URL. Please contact the system administrator.
If you are the system administrator please click
to find out more about this error.C语言编程——冒泡排序法。要求:由主函数调用排序子函数,对n个整数进行从小到大的排序,谢了_百度知道
C语言编程——冒泡排序法。要求:由主函数调用排序子函数,对n个整数进行从小到大的排序,谢了
我有更好的答案
8;n;,a[i]); } getchar(),4; sort(a;从小到达,前面的比后面的大; return 0;i++)
/&#47,3;i++) {
printf(&%d &quot,2。
int temp = a[j],int n){
for(i=1;i&
}}int main(){ int a[10]={1;a[j+1])
//j&n-i;j++)
a[j] = a[j+1];
a[j+1] = temp,6,则互换;i&10,10);
for(i=0,5;void sort(int a[],10},9;n个程序 排n-1次
for(j=0,7#include&stdio.h&gt
采纳率:59%
);n-1;n&;\n&quot,N); for(i=0;
arr[j]=arr[j+1];
arr[j+1]=t;
}}void main(){ int arr[N]; int i,int n){
for(i=0;i&请输入%d个要排序的数字!\j++) {
if(arr[j]&n-i-1; printf(&i&10;arr[j+1])
t=arr[j]; printf(&%d &,arr[i]),N); for(i=0;#define N 10void paixu(int *i++)
printf(&quot,&arr[i]); paixu(i++)
for(j=0;j&i&N;i++)
scanf(&%d&#include&stdio.h&gt
为您推荐:
其他类似问题
冒泡排序法的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。朱兆祺教你如何攻破C语言学习、笔试与机试的难点(连载) - 单片机/MCU论坛 -
中国电子技术论坛 -
最好最受欢迎电子论坛!
后使用快捷导航没有帐号?
朱兆祺教你如何攻破C语言学习、笔试与机试的难点(连载)
08:47:54  
167565&查看
本帖最后由 zzq宁静致远 于
19:42 编辑
& & 再过1个月又是一年应届毕业生应聘的高峰期了,为了方便应届毕业生应聘,笔者将大学四年C语言知识及去年本人C语言笔试难点进行梳理,希望能对今年应届毕业生的应聘有所帮助。
2013年10月18日更新--&& & 攻破C语言这个帖子更新到这里,我不仅仅是为了补充大学学生遗漏的知识,我更重要的是希望通过我的经验,你们实际项目中的C语言写得漂亮,写出属于你的风格。“朱兆祺STM32手记”()一帖中我就多次强调过编程的模块化,要考虑到可移植性和别人的可阅读性。我在某公司实习的时候,拿到一个程序,我当时就想吐槽,我想除了这个程序的当时写作者能够看懂之外,其他人有谁还能看懂,程序构架、可移植性性就更是一塌糊涂。结果我全部推到重写。因此明志电子科技工作室从承接第一个项目做开始,我便和搭档说,我们必须制定我们工作室的编程规范和编程风格,这样就算给谁,换成谁,拿到项目都能马上接下去做。
& & 朱兆祺在这个帖子将会不断更新,明志电子工作室的项目经验也将在电子发烧友论坛不断贴出,我希望能用我们仅有的力量,将我们的经验毫不保留传授给大家。我只希望在未来某一天,有那么几个人会竖着大拇指说:明志电子科技工作室的经验受益匪浅就够了。我相信,在深圳,明志电子科技工作室的影响力会日益增长,因为我们已经规划好了未来脚步。
& & C语言是一门技术,但更是一门艺术。写一个C语言代码不难,写一个高水平、漂亮的代码很难。朱兆祺将在此帖不断为大家攻破C语言。
&--朱兆祺于日
--&朱兆祺更新于日:
& & 深圳市馒头科技有限公司于2014年2月注册成立,当初命名为馒头科技,不管是吴坚鸿所说亲切还是谢贤斌所说草根。馒头科技永远以用户满意为我们的追求,馒头科技不追求锦上添花,但是我们很愿意为每一位客户雪中送炭。因为锦上添花每一个人都会做,但是雪中送炭却不是每一个人愿意去做。
& & 馒头科技往后将为每一位读者送上更为精美的学习资料。敬请期待。
第一节&&C语言编程中的几个基本概念
第二节&&数据存储与变量
第三节&&数学算法解决C语言问题
第四节&&关键字、运算符与语句
第五节& & C语言中的细节
第六节&&数组与指针
第七节&&结构体与联合体
第八节&&内存分配与内存释放
第九节& &笔试中的几个常考题
第十节&&数据结构之冒泡排序、选择排序
第十一节& &机试题之数据编码
第十二节&&机试题之十进制1~N的所有整数中出现“1”的个数
第十三节&&机试题之&&遍历单链表一次,找出链表中间元素
第十四节&&机试题之全排序
第十五节 机试题之大整数运算
第十六节&&机试题之大整数减法与乘法
第十七节&&算法之二分查找
第十八节&&数据结构之单向链表(颠覆你手中数据结构的三观)
第十九节&&数据结构之双向链表(接着颠覆你手中的数据结构三观)
第二十节 数据结构之栈
C语言技术公开课第一讲——编译环境给C语言带来的困扰,网络课程讲义
第二十一节&&通过加减法高效的求出两个无符号整数的商和余数
第二十二节&&表达式计算器(1)
第二十三节&&表达式计算器(2)
第二十四节&&表达式计算器(3)
第二十五节&&表达式计算器(4)
C语言技术公开课第三讲&&const问题
第二十六节&&序列差最小
第二十七节&&sizeof与strlen的深入
第二十八节&&C与C++中的const
第二十九节 do、while
第三十节 变量的生命周期
第一节&&C语言编程中的几个基本概念
1.1& && &#include& &与#include& &
<font color="#.& &#include& &和#include& &有什么区别?这个题目考查大家的基础能力,#include& &用来包含开发环境提供的库,#include& &用来包含.c/.cpp文件所在目录下的头文件。注意:有些开发环境可以在当前目录下面自动收索(包含子目录),有些开发环境需要指定明确的文件路径名。1.2& && &switch() <font color="#.& &switch(c) 语句中 c 可以是 int, long, char, float, unsigned int 类型?其实这个题目很基础,c应该是整型或者可以隐式转换为整型的数据,很明显不能是实型(float、double)。所以这个命题是错误的。1.3& && &const <font color="#.& &const有什么用途?虽然const很常用,但是我相信有很多人仍然答不上来。(1) 欲阻止一个变量被改变,可以使用const 关键字。在定义该 const 变量时,通常需要对它进行初 始化,因为以后就没有机会再去改变它了; (2) 对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二者同时指定为 const; (3) 在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值; (4) 对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成员变量; (5) 对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为“左值”。 1.4& && &#ifndef/#define/#endif <font color="#.& &头文件中的 #ifndef/#define/#endif 干什么用?其实#ifndef、#define、#endif这些在u-boot、linux内核文件中经常见到,在这么大型的程序中大量使用,可见它的作用不可小觑。这些条件预编译多用于对代码的编译控制,增加代码的可裁剪性,通过宏定义可以轻松的对代码进行裁剪。#ifndef/#define/#endif最主要的作用是防止头文件被重复定义。1.5& && &全局变量和局部变量 <font color="#.& && && &全局变量和局部变量在内存中是否有区别?如果有,是什么区别?全局变量储存在静态数据库,局部变量在堆栈。 其实,由于计算机没有通用数据寄存器,则函数的参数、局部变量和返回值只能保存在堆栈中。提示:局部变量太大可能导致栈溢出,所以建议把较大数组放在main函数外,防止产生栈溢出。思考:如程序清单1. 1所示。会出现怎样的情况?程序清单1. 1&&大数组放在main函数中导致堆栈溢出int main(int argc, char *argv[]){& & int iArray[1024 * 1024];& & return 0;}
鸿哥都推荐,必是人才&
朱兆祺,人才也。强烈建议版主看住他,别让他跑了。他是一个非常乐于分享单片机技术的人才。我代表鸿哥欢迎朱兆祺来电子发烧友论坛安家。&
很精彩,期待您更多的分享
您的付出是论坛的动力,感谢您一直支持!.
08:52:54  
本帖最后由 zzq宁静致远 于
08:58 编辑
第二节&&数据存储与变量
2.1& && &变量的声明与定义 <font color="#.& && && &如程序清单2. 1所示会不会报错?为什么?如果不会报错,又是输出什么结果?程序清单2. 1&&变量的声明与定义#include&stdio.h&static int&&a& &;static int&&b[] ;int main( int argc , char *argv[] ){& & printf( &%d&&%d \n& , a , b[0] ) ;& & return 0 ;}static int& &a = 8 ;static int& &b[4]&&;这个程序是不会报错的,并且连警告都不会出现。输出的结果是:8&&0static int a ,这句程序是声明全局变量a;static int b[],这句程序是声明全局数组变量b,并且是不完全声明,也就是可以省略数组下标。static int a = 8,这里才是定义全局变量a,static int b[4],这里是定义全局变量b。2.2& && &局部变量与全局变量的较量 <font color="#.& && && &请问如程序清单2. 2所示输出什么?程序清单2. 2&&局部变量与全局变量#include&stdio.h&static int a = 8 ;int main( int argc , char *argv[] ){& & int a = 4 ;& & printf( &%d \n& , a ) ;& & return 0 ;}C语言规定,局部变量在自己的可见范围内会“挡住”同名的全局变量,让同名的全局变量临时不可见。即在局部变量的可见范围内不能访问同名的全局变量。因此本程序输出为:4。2.3& && &char、int、float、double的数据存储 <font color="#.& && && &请问如程序清单2. 3所示,i和j输出什么?程序清单2. 3&&数据存储float i = 3 ;int& &j = *(int*)(&i) ;printf( &i = %f \n& , i ) ;printf( &j = %#x \n& , j ) ;i是毋庸置疑是:3.000000。但是j呢?3.000000?答案是否定的,j是输出:0x。有人会问了,难道j是随机输出?瞎说,j输出0x是有依据,是一个定值!由于i是float数据类型,而j是int数据类型。理论上说,j是取了i的地址然后再去地址,应该得到的就是i的值:3。但是问题的关键就是float数据类型的存储方式和int数据类型不一样,float是占用4个字节(32位),但是float存储是使用科学计数法存储,最高位是存储数符(负数的数符是0,正数的数符是1);接下来8位是存储阶码;剩下的23位是存储尾数。上面i=3.000000,那么3.进制) = 11(2进制) = &v:shape id=_x style=&WIDTH: 40.5 HEIGHT: 21.75pt& equationxml=' 121.1 脳&21' type=&#_x0000_t75&& (二进制)。数据在电脑中存储都是二进制,这个应该都没有疑问。那么这里的数符为:0 ,阶码为:E – 127 = 1 ,那么阶码为:E = 128 即为: (2进制) ,尾数为:100 00
。那么存储形式就是:&&00 00。这个数据转换成16进制就是0x。图2. 1&&数据存储方式
char、int、float、double的存储方式如图2. 1所示。提问:如果i = -3.5 的话,请问j输出多少?i = -3.500000j = 0xc0600000这个希望读者自行分析。再问:如果如程序清单2. 4所示。程序清单2. 4&&数据存储double i = 3 ;int& &&&j = *(int*)(&i) ;printf( &i = %lf \n& , i ) ;printf( &j = %#x \n& , j ) ;这样的话,j又输出多少呢?提示:double(&&8个字节(64位)&&)的存储方式是:最高位存储数符,接下来11位存储阶码,剩下52位存储尾数。是不是得不到你想要的结果?double是8个字节,int是4个字节。一定别忘记了这个。用这个方法也同时可以验证大小端模式!2.4& && &容易忽略char的范围 <font color="#.& && && &如程序清单2. 5所示,假设&b=0x12ff54,请问三个输出分别为多少?程序清单2. 5&&char的范围unsigned int b = 0x12ff60 ;printf(&( (int)(&b)+1 )& && && &= %#x \n& , ( (int)(&b)+1 )& && && &&&) ;printf(&*( (int*)( (int)(&b)+1 ) )&&= %#x \n& , *( (int*)( (int)(&b)+1 ) )& &) ;printf(&*( (char*)( (int)(&b)+1 ) ) = %#x \n& , *( (char*)( (int)(&b)+1 ) )&&) ;很显然,&b是取无符号整型b变量的地址,那么(int)(&b)是强制转换为整型变量,那么加1即为0x12ff54+1=0x12ff55。所以( (int)(&b)+1 )是0x12ff55。图2. 3&&指针加1取字符型数据
由于( (int)(&b)+1 )是整型数据类型,通过(int *)( (int)(&b)+1 )转化为了整型指针类型,说明要占4个字节,即为:0x12ff55、0x12ff56、0x12ff57、0x12ff58,再去地址*( (int *)( (int) (&b)+1 ) )得到存储在这4个字节中的数据。但是很遗憾,0x12ff58我们并不知道存储的是什么,所以我们只能写出0x**0012ff。**表示存储在0x12ff58中的数据。如图2. 2所示。图2. 2&&指针加1取整型数据
以此类推,*( (char *)( (int) (&b)+1 ) ) = 0xff。如图2. 3所示。但是,*( (char *)( (int) (&b)+1 ) )输出的却是:0xff ff ff ff !问题出现了,为什么*( (char *)( (int) (&b)+1 ) )不是0xff,而是0xff ff ff ff?char型数据应该占用1个字节,为什么会输出0xff ff ff ff?使用%d输出,printf(&*( (char*)( (int)(&b)+1 ) ) = %d \n& , *( (char*)( (int)(&b)+1 ) ) ) ;结果为-1???问题出在signed char 的范围是:-128~127,这样肯定无法储存0xff,出现溢出。所以将printf(&*( (char*)( (int)(&b)+1 ) ) = %#x \n& , *( (char*)( (int)(&b)+1 ) ) ) ;改成printf(&*( (unsigned char*)( (int)(&b)+1 ) ) = %#x \n& , *( (unsigned char*)( (int)(&b)+1 ) ) ) ;就可以输出0xff,因为unsigned char 的范围是:0~255(0xff)。
本帖子中包含更多资源
才可以下载或查看,没有帐号?
http://www.xiaoshuwo.net/3224/ 兽血沸腾2&
static int
这句运行的时候报错:error C2133: 'b' : unknown size
执行 cl.exe 时出错.
把static换成 extern,编译可以通过。
这是为什么???&
上面i=3.000000,那么3.进制) = 11(2进制) = &v:shape id=_x style=&WIDTH: 40.5 HEIGHT: 21.75pt& equationxml=' 121.1 脳&21' type=&#_x0000_t75&& (二进制)。(看不... &
为什么我在运行的时候报错了???错误原因D:\Cpp1.cpp(7) : error C2133: 'b' : unknown size
D:\VC98\Cpp1.cpp(23) : error C2086: 'a' : redefinition
D:\VC98\Cpp1.cpp(25) : error C208... &
不错,很详细也很具体&
09:02:15  
本帖最后由 zzq宁静致远 于
09:07 编辑
第三节&&数学算法解决C语言问题
3.1& && &N!结果中0的个数 <font color="#.& && &&&<font color="#!结果中后面有多少个0?谁跟你说过高数没用?数学是C语言的支撑,没有数学建模的支撑就没有那么经典的C语言算法!如果你能一个一个乘出来我算你狠!我也佩服你!<font color="#也就意味着乘积是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&& &int fun(int iValue)& &{& && & int iSum = 0;& && & while(iValue / 5 != 0)& && & {& && && &&&iSum& &&&+= (iValue / 5 );& && && &&&iValue&&/= 5;& && & }& && & return iS& &}& &&&int main( int argc , char *argv[] )& &{& &int iNumber, iZoreN&&& & scanf( &%d&, &iNumber);& && & iZoreNumber = fun(iNumber);& &printf( &%d\n&, iZoreNumber); & & return 0;& &} 所以99!后面有多少个0的答案是:99 / 5 = 19 , 19 / 5 = 3 ; 3 / 5 = 0 .也就是:19 + 3 + 0 = 22.这里延伸为N!呢,一样的,万变不离其宗!3.2& && &N!结果中的位数 1.& &请问N!结果中有几位数?数学!还是数学,数学真的博大精深,如果大学没有好好学数学,会成为你一辈子的遗憾。我们先假设:N! = 10 ^A,我们知道 10^1~10^2(不含10^2)之间是2位数,10^2~10^3(不含10^3)之间是3位数,以此类推,10^A~10^(A+1)(不含10^(A+1))则是(A+1)位数,那就是说,我们只要求出A,即可知道N!有几位数。A=log10(N!),N!= 1*2*3……*N,那么A= log10(1)+log10(2)+……+log10(N).这样好计算了吧!程序如程序清单6. 2所示。程序清单6. 2&&求N!结果中的位数#include &stdio.h&#include &math.h&int main(int argc, char *argv[]){& & int iNumber , i = 0 ;& & double sum = 0 ;& & printf(&请输入iNumber :&);& & scanf( &%d& , &iNumber );& & for( i = 1 ; i & ( iNumber + 1 ) ; i++) {& && & sum += log10(i) ;& & }& & printf(& N!有%d位 \n& , (int)sum&&+&&1&&) ;& & return 0;}我们看下调试结果:请输入iNumber :10N!有7位请按任意键继续. . .& & 网友可以自行验证。
比老师讲好多了&
需要一些基本的验证,这样便于证实行&
09:14:57  
第四节&&关键字、运算符与语句
1.1& && &static<font color="#.& && && &如程序清单4. 1所示,请问输出i、j的结果?程序清单4. 1&&static#include &stdio.h& static int&& void fun1(void){& & static int i = 0 ;& & i++ ;& & printf(&i = %d& && , i );} void fun2(void){& & j = 0 ;& & j++ ;& & printf(&j = %d \n& , j );} int main(int argc, char *argv[]){& & & & int k = 0 ;& & for( k = 0 ; k & 10 ; k++ ){& && & fun1() ;& && & fun2() ;& && & printf(&\n&);& & }& & & & return 0;}答案:i = 1& &j = 1 i = 2& &j = 1 i = 3& &j = 1 i = 4& &j = 1 i = 5& &j = 1 i = 6& &j = 1 i = 7& &j = 1 i = 8& &j = 1 i = 9& &j = 1 i = 10& &j = 1 请按任意键继续. . . 很多人傻了,为什么呢?是啊,为什么呢?!由于被static修饰的变量总存在内存静态区,所以运行这个函数结束,这个静态变量的值也不会被销毁,函数下次使用的时候仍然能使用这个值。有人就问啊,为什么j一直是1啊。因为每次调用fun2()这个函数,j都被强行置0了。static的作用:(1) 函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次, 因此其值在下次调用时仍维持上次的值; (2) 在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问; (3) 在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明 它的模块内; (4) 在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝; (5) 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
1.2& && &for循环的深究<font color="#.& && && &如程序清单4. 2所示,输出结果是什么?程序清单4. 2&&for循环#include &stdio.h& int main(int argc, char *argv[]){& & int&&i = 0 ;& & & & for( i = 0 ,printf(&First = %d&&& , i ) ;& && && &printf(&Second = %d&&& , i ) , i & 10 ;& && &&&i++ , printf(&Third = %d&&& , i )) {& && && &&&printf(&Fourth = %d&&\n& , i) ;}& & & & return 0;}这个题目主要考察对for循环的理解。我们先来看看运行程序会输出什么?First = 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 = 10&&Second = 10&&请按任意键继续. . .从输出我们就可以知道程序到底是什么运行:首先i = 0 , 所以输出:First = 0 ; 接着输出:Second = 0&&;&&i & 10 成立,则输出:Fourth = 0 。就此完成第一个循环。接着 i ++ , 此时i = 1 ,输出:Third = 1 ;接着输出:Second = 1 ;i & 10 成立,则输出:Fourth = 1 ······以此类推。
1.3& && &尺子——sizeof<font color="#.& &如程序清单4. 3所示,sizeof(a),sizeof(b)分别是多少?程序清单4. 3&&sizeof#include &stdio.h&int main(int argc, char *argv[]){& & char&&a[2][3] ; & & short b[2][3] ;& & printf( &sizeof(a) = %d \n& , sizeof( a ) ) ;& & printf( &sizeof(b) = %d \n& , sizeof( b ) ) ;& & return 0;}这个题目比较简单,由于char 是1个字节、short是2个字节,所以本题答案是:sizeof(a) = 6sizeof(b) = 12请按任意键继续. . . 好的,再来看看如程序清单4. 4所示,sizeof(a),sizeof(b)分别是多少?程序清单4. 4&&sizeof#include &stdio.h&int main(int argc, char *argv[]){& & char&&*a[2][3] ; & & short *b[2][3] ;& & printf( &sizeof(a) = %d \n& , sizeof( a ) ) ;& & printf( &sizeof(b) = %d \n& , sizeof( b ) ) ;& & return 0;}是数组指针呢,还是指针数组呢?这里涉及*和[]和优先级的问题。我告诉大家的是这两个数组存放的都是指针变量,至于为什么,在后续章节会详细解释,然而指针变量所占的字节数为4字节,所以答案:sizeof(a) = 24sizeof(b) = 24请按任意键继续. . . 1.4& && &++i和i++<font color="#.& &或许大家都知道,++i是先执行i自加再赋值,但是i++是先赋值再自加,但是还有隐藏在后面的东西呢? int i = 0 ;int iNumber = 0 ;iNumber = (++i) + (++i) + (++i) ;C-Free编译输出是:7,有的编译器输出是:9。这两个答案都是对的,编译器不同所不同。7 = 2+2+3;9=3+3+3。区别在于答案是7的先执行(++i)+(++i)再执行+(++i),但是答案是9的是一起执行。 这只是前奏,先看几个让你目瞪口呆的例子。编译环境是VS2010。int i = 0 ;int iNumber = 0 ;iNumber = (i++) + (++i) + (++i) ;printf( &iNumber = %d \n& , iNumber ) ;这里输出是:4!!!4 = 1+1+2。 int i = 0 ;int iNumber = 0 ;iNumber = (++i) + (i++) + (++i) ;printf( &iNumber = %d \n& , iNumber ) ;这里输出是:4!!!4=1+1+2。 int i = 0 ;int iNumber = 0 ;iNumber = (++i)&&+ (++i) + (i++) ;printf( &iNumber = %d \n& , iNumber ) ;这里输出是:6!!!6=2+2+2。 这里至少能说明两个问题,其一,先执行前面两个,再执行第三个;其二,(i++)这个i的自加是最后执行!int i = 0 ;int iNumber = 0 ;iNumber = (++i)&&+ (i++) + (++i) + (++i) + (i++) ;printf( &iNumber = %d \n& , iNumber ) ;这个会是多少?!答案是:10!!!10=1+1+2+3+3!不同的编译器或许会存在不同的答案,希望读者自行进行验证。 1.5& && &scanf()函数的输入<font color="#.& &如程序清单4. 5所示,运行程序,当显示Enter Dividend: , 输入的是a,按下Enter之后程序会怎么运行?程序清单4. 5&&scanf()函数的输入#include&stdio.h& int main(void){& && &&&float fDividend,fDivisor,fR & && &&&printf(&Enter Dividend:&);& && &&&scanf(&%f&,&fDividend); & && &&&printf(&Enter Divisor:&);& && &&&scanf(&%f&,&fDivisor); & && &&&fResult=fDividend/fD& && &&&printf(&Result is: %f\n&,fResult); & && &&&return 0;}这个问题有人会说,肯定是显示Enter Divisor:要我输入除数咯。是这样吗?答案是:如果你在Enter Dividend:之后输入非数字,按下Enter之后显示的不是Enter Divisor: 要你输入除数,而是程序到此就运行结束,显示一个不确定答案,这个答案每一次都会变。如果你在Enter Divisor:之后输入非数字,按下Enter之后显示的不是Reslut的结果, 而是程序到此就运行结束,显示一个不确定答案,这个答案每一次都会变。由于scanf()使用了%f,当输入数字的时候,scanf()将缓冲区中的数字读入fDividend,并清空缓冲区。由于我们输入的并非数字,因此scanf()在读入失败的同时并不会清空缓冲区。最后的的结果是,我们不需要再输入其他字符,scanf()每次都会去读取缓冲区,每次都失败,每次都不会清空缓冲区。当执行下一条scanf()函数读取除数时,由于缓冲区中有数据,因此它不等待用户输入,而是直接从缓冲区中取走数据。那么防止输入非数字的程序应该怎样呢?#include&stdio.h& int main( int argc , char *argv[] ){& & float& &fDividend , fDivisor , fR& & int& &&&iR& & char& & cTmp1[ 256 ] ; & & printf( &Enter Dividend \n& ) ;& & iRet&&=&&scanf( &%f& , &fDividend ) ; if ( 1 == iRet ) {& && &&&printf( &Enter Divisor \n& ) ;& && &&&iRet = scanf( &%f& , &fDivisor ) ;& && &&&if ( 1== iRet ){& && && && &fResult = fDividend / fD& && && && &printf( &Result is %f \n& , fResult ) ;& && &&&}& && &&&else{& && && && &printf( &Input error ,not a number! \n& ) ;& && && && &gets(cTmp1) ;& && && && &return 1 ;& && &&&}& & }else{& && &&&printf( &Input error , not a number! \n& ) ;& && &&&gets(cTmp1) ;& && &&&return 1 ;& & } & & return 0 ;} 1.6& && &scanf()函数的返回值<font color="#.& && && &如程序清单4. 6所示,请问输出会是什么?程序清单4. 6&&scanf()函数的返回值int a ,printf( &%d \n& , scanf(&%d%d& , &a , &b) ) ;输出输入这个函数的返回值?!答案是:2。只要你合法输入,不管你输入什么,输出都是2。那么我们就要深入解析scanf这个函数。scanf()的返回值是成功赋值的变量数量。 1.7& && &const作用下的变量<font color="#.& && && &阅读如程序清单4. 7所示,想想会输出什么?为什么?程序清单4. 7&&const作用下的变量const int& &iNumber = 10 ;printf(& iNumber = %d&&\n& ,&&iNumber) ;int *ptr = (int *)(&iNumber) ;*ptr&&= 100 ;printf(& iNumber = %d&&\n& ,&&iNumber) ;const的作用在第四章已经详细讲了,这里就不再累赘,答案是:10,10。这里补充一个知识点:const int *p& && && && && && & 指针变量p可变,而p指向的数据元素不能变int* const p& && && && && && & 指针变量p不可变,而p指向的数据元素可变const int* const p& && && && &指针变量p不可变,而p指向的数据元素亦不能变 1.8& && &*ptr++、*(ptr++),*++ptr、*(++ptr),++(*ptr)、(*ptr)++的纠缠不清<font color="#.& && && &如程序清单4. 8所示程序,输出什么?程序清单4. 8&&*ptr++int&&iArray[3] = { 1 , 11 , 22} ;int&&*ptr& &&&= iA printf( &*ptr++ = %d \n& , *ptr++ ) ; printf( &*ptr& &= %d \n& , *ptr& &) ; 纠结啊,是先算*ptr还是ptr++;还是纠结啊,ptr是地址加1还是偏移一个数组元素!这里考查了两个知识点,其一:*与++的优先级问题;其二,数组i++和++i的问题。*和++都是优先级为2,且都是单目运算符,自右向左结合。所以这里的*ptr++和*(ptr++)是等效的。首先ptr是数组首元素的地址,所以ptr++是偏移一个数组元素的地址。那么ptr++运算完成之后,此时的ptr是指向iArray[1],所以第二个输出*ptr = 11 。如图4. 1所示。那么倒回来看第一个输出,ptr++是在执行完成*ptr++之后再执行的,所以,*ptr++ = 1 。图4. 1&&ptr++
如程序清单4. 9所示程序,输出会是什么?程序清单4. 9&&*++ptrint&&iArray[3] = { 1 , 11 , 22} ;int&&*ptr& && & = iA printf( &*++ptr = %d \n& , *++ptr ) ;printf( &*ptr& &= %d \n& , *ptr& &) ; 这个解释和上面解释差不多,就是++ptr和ptr++的差别,所以这里的两个输出都是:11。同样的道理,*++ptr和*(++ptr)是等效。 再如程序清单4. 10所示,输出又会是什么?程序清单4. 10&&(*ptr)++int&&iArray[3] = { 1 , 11 , 22} ;int&&*ptr& && & = iA printf( &(*ptr)++ = %d \n& , (*ptr)++ ) ;printf( &*ptr& &= %d \n& , *ptr& &) ; 这个的输出是:1,2。原因请读者分析。
本帖子中包含更多资源
才可以下载或查看,没有帐号?
09:18:45  
第五节& & C语言中的细节
1.1& && &“零值”比较<font color="#.& &写出float x 与“零值”比较的if语句。首先要知道float是有精度的,不能直接与0相比较或者两数相减与0相比较。float能保留几位小数?答案是6位。既然如此,那么就应该这么写: if((x & 0.000001) && (x & -0.000001)) 。 1.2& && &宏定义<font color="#.& &定义一个宏,返回X、Y中的较大值。这个题目其实很简单,但是在很多笔试中都会拿出来考试,并且出错率很高,原因只有一个,忽略细节(优先级的问题,实在搞不明白就加括号吧,你好理解,别人一看也懂)。终究还是细节决定成败。#define& &MAX( (X) , (Y) )& & ((X) &= (Y) ? (X) : (Y)) <font color="#.& &宏定义两个数相加请问如程序清单5. 1输出什么?程序清单5. 1&&宏定义两数相加#define& &DOUBLE(x) x+xint main(int argc, char* argv[]){& & int iNumber = 0 ;& & printf(&%d\n& , 10*DOUBLE(10));& & return 0;}其实这个程序非常简单,学习C语言一周就应该理解是什么意思,但是一般会出错的的地方都在细节。其实这个程序输出是110。可能有人会问,不是10先DOUBLE嘛,然后乘以10,不是200嘛。是啊,想法是好的,我想这个程序的“原意”也应该是这样,但是就是由于优先级的问题,打破了我们的愿望。如果要得到200,那么就应该是这样宏定义:#define& &DOUBLE(x) ((x)+(x))。我想,无论我加多少层括号都不算违法吧。 1.3& && &递归运算<font color="#.& && && &如程序清单5. 2所示,输出什么?程序清单5. 2&&递归运算#include &stdio.h&int func(int a){& & if (a==0)& & {& && && & }& & printf(&%d\n&,func(a++/2));& &} int main(int argc, char *argv[]){& & printf(&%d&,func(7));& & return 0;}答案: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送进去,此时直接返回0,递归结束。递归最容易忽略的细节是,由于递归次数过多,容易导致堆栈溢出。 1.4& && &让人忽略的贪心法<font color="#.& && && &如程序清单5. 3所示,程序输出什么?程序清单5. 3&&贪心法int k = 8 ;int i = 10 ;int j = 10 ;k *= i+++printf(&%d \n& , k) ;贪心法,就是一次性能尽可能多得吃运算符,那么这里k *= i+++j ,加上括号之后就是这样:k = k * ((i++) + j) ;这样的话就很简单可以得出答案为:160。 1.5& && &性能优化<font color="#.& &对如程序清单5. 4所示进行性能优化,使得效率提高。程序清单5. 4&&性能优化int iValue1;int iValue2; iValue1 = 1234/16;iValue2 = 1234%32;对于嵌入式进行除法是很消耗效率的,能使用移位完成最好使用移位完成。iValue1 = 1234 && 4;iValue2 = 1234 – ((1234 && 5) && 5);<font color="#34 / 16 = 77; 1234 % 32 = 18。而十进制:1234转化成二进制:10。1234 && 4 = 01,转化为十进制即为:77;1234 && 5 = 10,((1234 && 5) && 5)即为00,转化为十进制即为: – 1216 = 18。
www.xiaoshuwo.net/13/ 独医无二&
09:27:52  
本帖最后由 zzq宁静致远 于
09:37 编辑
第六节&&数组与指针
1.1& && &数组、数组元素、指针的大小 <font color="#.& && && &如程序清单6. 1所示,程序输出什么?程序清单6. 1&&数组、数组元素、指针的大小#include &stdio.h&int main(int argc, char *argv[]){& & int *& & printf( &sizeof(p)& && & = %d \n&&&,&&sizeof(p)&&) ;& & printf( &sizeof(*p)& && &= %d \n&&&,&&sizeof(*p) ) ;& & int a[100];& & printf( &sizeof(a)& && & = %d \n&&&,&&sizeof(a)& && &) ;& & printf( &sizeof(a[100]) = %d \n&&&,&&sizeof(a[100]) ) ;& & printf( &sizeof(&a)& && &= %d \n&&&,&&sizeof(&a)& &&&) ;& & printf( &sizeof(&a[0])&&= %d \n&&&,&&sizeof(&a[0])&&) ;& & return 0;}p是指针,任何数据类型的指针都是占4字节;*p是一个指针指向的int数据,int型数据占用4字节;a是数组,除了sizeof(a)和&a之外,当a出现在表达式中时,编译器会将a生成一个指向a[0]的指针,而这里测量的是整个数组的大小。答案:sizeof(p)& && & = 4sizeof(*p)& && &= 4sizeof(a)& && & = 400sizeof(a[100]) = 4sizeof(&a)& && &= 4sizeof(&a[0])& &= 4请按任意键继续. . .1.2& && &广东省的省政府和广州市的市政府都在广州 <font color="#.& && && &如程序清单6. 2所示,如果ptr = 0x ,那么剩下三个输出是多少?程序清单6. 2&&数组首地址与数组首元素地址int iArray[3] = { 1 , 2 , 3 } ;int *ptr& && &= iAprintf(&ptr& && && & = %#x\n& , ptr ) ;printf(&iArray& && & = %#x\n& , iArray ) ;printf(&&iArray& && &= %#x\n& , &iArray ) ;printf(&&iArray[0]& &= %#x\n& , &iArray[0] ) ;
iArray是数组名,由6.1节对a的说明,可iArray知同时也是数组首元素的地址,为0x;&iArray是数组的首地址,这是毫无疑问的。&iArray[0]是数组首元素的地址,也是0x。也就是说数组的首地址和数组首元素的地址是相等的。因为广东省的省政府和广东省的首号城市广州市的市政府都在广州,两者的地址是相等的。如图6. 1所示。如程序清单6. 3所示,ptr = 0x ,那么这三个输出会是多少?程序清单9. 3&&数组首地址加1、数组首元素地址加1int iArray[3] = { 1 , 2 , 3 } ;int *ptr& && &= iAprintf(&&iArray+1& &&&= %#x\n& , &iArray+1 ) ;printf(&iArray+1& && &= %#x\n& , iArray+1 ) ;printf(&&iArray[0]+1 = %#x\n& , &iArray[0]+1 ) ;答案是,第一个输出:0xC;第二个、第三个输出:0x。&iArray是数组的首地址,那么&iArray+1是偏移一个数组单元,因为站在全国的角度报全国各省政府的天气预报,报完广东省省政府之后就为湖南省省政府;如图6. 1所示。&iArray[0]是数组首元素的地址,&iArray[0]+1是偏移一个数组元素单元,好比站在广东的角度报广东各城市的天气预报,报完广东省首号城市广州的天气预报之后就是为广东省第二号城市的天气预报。1.3& && &数组作为函数参数,是传什么进去了 <font color="#.& && && &如程序清单6. 4所示,程序输出什么?程序清单6. 4&&数组作为函数参数void text(char cArray[]){& & printf( &sizeof(cArray) = %d \n& , sizeof(cArray) ) ;}int main(int argc, char* argv[]){& & char cArray[] = &aBcDeF& ;& & printf( &sizeof(cArray) = %d \n& , sizeof(cArray) ) ;& & text(cArray) ;& & return 0;}这里考查两个知识点,其一,sizeof和strlen();其二text(char cArray[])形参到底是什么?答案是7,4。看到答案我想大家就应该明白上面两个问题了吧。到底是传值还是传址一定要搞明白哦。1.4& && &指针相减 <font color="#.& && && &如程序清单6. 5程序,输出会是什么?程序清单6. 5&&指针相减#include &stdio.h&int main(int argc, char *argv[]){& & int&&a[2] = { 3 , 6 } ; & & int&&*p& &=& & int&&*q& &= p + 1 ;& & printf( &q - p = %d \n& , q-p ) ;& & printf( &(int)q - (int)p = %d \n& , (int)q-(int)p ) ;& & & & return 0;}用数学方法到可以做出q-p = 1这个答案,但是(int)q - (int)p 的答案。指针,指针的强大。由于指针加1,内存地址是加sizeof(int),但是int(q)和int(p)就不再是指针,而是一个整形数据。所以(int)q - (int)p&&= 4 。1.5& && &指针加1到底是加什么 <font color="#.& &如程序清单6. 6所示,请问p1+5=__;p2+5=__;程序清单6. 6&&指针加1#include &stdio.h&int main(int argc, char *argv[]){& & unsigned char&&*p1 ;& & unsigned long& &*p2 ;& & & & p1 = (unsigned char *)0x801000 ;& & p2 = (unsigned long *)0x810000 ;& & & & printf( &p1+5 = %#x \n& , p1 + 5 ) ;& & printf( &p2+5 = %#x \n& , p2 + 5 ) ;& & & & return 0;}由于p + n = p + n * sizeof(p的数据类型) ,所以答案为:p1+5 = 0x801005p2+5 = 0x810014请按任意键继续. . .1.6& && &数组与指针的概念 <font color="#.& && && &如程序清单6. 7所示,解释程序。程序清单6. 7&&数组与指针的概念a)
b) int *a;
c) int **a;
d) int a[10];
e) int *a[10];&&
f) int (*a)[10];&&
g) int (*a)(int);&&
h) int (*a[10])(int);答案:a)& &一个整型数 ;b)& &一个指向整型数的指针;c)& &一个指向指针的指针,它指向的指针是指向一个整型数;d)& &一个有10个整型数的数组;e)& &一个有10个指针的数组,该指针是指向一个整型数的;f)& &一个指向有10个整型数数组的指针;g)& &一个指向函数的指针,该函数有一个整型参数并返回一个整型数;h)& &一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数。这个题目很经典,很多公司的笔试题目都会截取上面部分出来考试。特别是e和f,哪一个是数组指针,哪一个又是指针数组;g和h哪一个是函数指针,哪一个又是指针函数。1.7& && &数组与指针的糅合 <font color="#.& && && &如程序清单6. 8所示,输出会是什么?程序清单6. 8&&数组与指针的糅合应用1int arr[] ={6,7,8,9,10};& &int *ptr& &=&&*(ptr++)& &+=123;&&printf("%d,%d",*ptr,*(++ptr));这个题目来源于华为的一道C语言笔试题,答案是:8,8。*ptr = arr ,这里ptr取得是数组arr[]的首元素地址,*(ptr++) +=123 ,这里是ptr++,此时*(ptr)即为:6,那么*(prt++)+123=129,执行完*(ptr++)+=123之后,*(ptr) = 7。跟*(++ptr)之后为8这个值是没有半点关系,由于执行了*(++ptr),所以此刻的*(ptr)为8,所以输出为:8,8。<font color="#.& && && &如程序清单6. 9所示,输出会是什么?程序清单6. 9&&数组与指针的糅合应用2int main( int argc , char *argv[] ) {&&& &int a[5] = { 1 , 2 , 3 , 4 , 5 };&&& &int *ptr = (int *)( &a + 1 );&&& &printf( &%d,%d& , *(a+1) , *(ptr-1) ); } 这个题目要求对指针的理解要比较透彻。由于*(a+1)和a[1]是等效的,则*(a+1)=a[1] = 2 。&a指的是指向整个数组变量的指针,&a+1不是首地址+1,系统会认为加了一个a数组的偏移量,即偏移了一个数组的大小,因此ptr指向的是a[5],即是*(ptr+5),既然如此,那么*(ptr-1)当然就是a[4] = 5咯。所以这个题目的答案是: 2 , 5 。其实这个题目还有一个延伸,int *ptr = (int *)( (int) a + 1 ), *ptr是多少。答案是:2 00 00 00。假设 &a[0] = 0x,由于存储方式是小端模式,那么a[0] = 1和a[1] = 2的存储方式如图6. 2所示。因为a = 0x,而(int)a将这个地址强制转化为了int型数据,((int)a + 1) = 0x,经过(int *)((int)a + 1)成了地址,ptr = (int *)((int)a + 1),由于ptr是指向int型的指针,*ptr占4个字节,*ptr所占字节即为:0x00,0x00,0x00,0x02,那么*ptr即为0x。
<font color="#.& && && &如程序清单6. 10所示,如果ptr1为0x,那么三个输出分别为多少?程序清单9. 10&&数组与指针的糅合应用3int iArray[7] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 } ;int *ptr1& &&&= iAint *ptr2& &&&= &iArray[5] ;printf( & ptr2& && & = %#x \n& , ptr2 ) ;printf( & ptr1& && & = %#x \n& , ptr1 ) ;printf( &ptr2 - ptr1 = %d \n& , ptr2 - ptr1 ) ;很明显iArray是整型数据类型数组,那么ptr2 = ptr1 + 5*sizeof(int) = 0x。很多同学立马就会脱口而出ptr2 – ptr1 = 20嘛!真的是这样吗?其实答案是:5!解释之前,我们先来看这个程序:int iArray[7] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 } ;char *p1& && &= (char *) iAchar *p2& && &= (char *) &iArray[5] ;printf( &p2 - p1& &&&= %d \n& , p2 - p1 ) ;这个程序的输出是:20。因为指针类型是char*,char是1个字节;而上面*ptr1和*ptr2是int*,所以答案是:5。如果是:short *p1& && &= (short *) iAshort *p2& && &= (short *) &iArray[5] ;则p2 – p1 就是为:10。这里还有一个延伸:& & int iArray[7] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 } ;& & int *ptr1& &&&= iA& & int *ptr2& &&&= &iArray[5] ;& & printf( & ptr2& && & = %#x \n& , ptr2 ) ;& & printf( & ptr1& && & = %#x \n& , ptr1 ) ;& & printf( &ptr2 - ptr1 = %d \n& , (int)ptr2 – (int)ptr1 ) ;这样输出答案是多少呢?20!1.8& && &指针数组 <font color="#.& && && &阅读程序,输出什么?char *cArray[3] = { &abcdef& , &123456& , &jxlgdx& } ;printf( &sizeof(cArray[0]) = %d \n& , sizeof(cArray[0]) );我相信有较多的人的答案是:6或者7。原因是忽略了*cArray[3]这是一个指针数组,也就是说cArray[3]数组中存放的是指针,而不是字符串常量。在C语言笔试陷阱与难点第一阶段讲过,只要是指针变量,其大小就是:4。所以这里毋庸置疑,输出应该是:4。你要是存在怀疑,可以输出cArray[3]数组的各个元素看看是不是指针。printf( &cArray[0]&&= %#x \n& , cArray[0] ) ;printf( &cArray[1]&&= %#x \n& , cArray[1] ) ;printf( &cArray[2]&&= %#x \n& , cArray[2] ) ;运行程序输出为:sizeof(cArray[0]) = 4cArray[0]&&= 0x415840cArray[1]&&= 0x415770cArray[2]&&= 0x415768请按任意键继续. . .读者亦可输出指针所指向的字符串:printf( &cArray[0]&&= %s \n& , cArray[0] ) ;printf( &cArray[1]&&= %s \n& , cArray[1] ) ;printf( &cArray[2]&&= %s \n& , cArray[2] ) ;运行输出为:cArray[0]&&= abcdefcArray[1]&&= 123456cArray[2]&&= jxlgdx请按任意键继续. . .<font color="#.& && && &阅读下列程序,输出什么?typedef int (init_fnc_t) (void);extern int arch_cpu_init(void);extern int board_early_init_f(void);init_fnc_t *init_sequence[] = {& & arch_cpu_init,& && & & & board_early_init_f,& & NULL,};int arch_cpu_init(void){& & printf(&This is arch_cpu_init \n&);& & return 0;}int board_early_init_f(void){& & printf(&This is board_early_init_f \n&);& & return 0;}void hang (void){& & printf(&Error! \n&);& & while (1) ;}int main(int argc, char* argv[]){& & init_fnc_t **init_fnc_& & for (init_fnc_ptr = init_ *init_fnc_ ++init_fnc_ptr) & & {& && & if ( (*init_fnc_ptr)() != 0 ) & && & {& && && &&&hang ();& && & }& & }& & return 0;}这个题目是我在阅读u-boot-2012.10源码的时候稍作修改从中提取出来的。这个题目将指针数组、函数指针等知识点融为一体。This is arch_cpu_initThis is board_early_init_f请按任意键继续. . .1.9& && &数组指针 <font color="#.& && && &如程序清单6. 11所示,程序输出什么?程序清单6. 11&&数组指针#include &stdio.h&int main(int argc, char *argv[]){& & int a[][4]={ 1,3,5,7,9,11,13,15,17,19,21,23};& & int (*p)[4] , i=2 , j=1 ;& & p=a;& & printf( &%d\n&, *(*(p+i)+j));& & return 0;}答案是:19。不能理解?好吧,如果我告诉你**(p+1) = 9, *((*p)+1) = 3!到这里能理解了吗?如果还是不能理解,ok,p是指向一个含有4个整型数据的数组,那么p如果加1,地址是不是得偏移4个整形数据的地址,而p等于数组a的首元素地址,a是二维数组,也就意味着p是双重指针了。1.10& &再论数组指针与数组首地址 <font color="#.& && && &如程序清单6. 12所示,已知&a[0][0] = 0x22fe70,想想会是输出什么?程序清单6. 12&&数组指针与数组首地址int main(int argc, char *argv[]){& & int a[8][8] = {1,2,3,4};& & int (*ptr1)[8]& & =& & int (*ptr2)[8][8]&&= &a;& & int *ptr3& && &&&= &a[0][0];&&& & printf(& ptr1& &&&= %#x\n& , ptr1);& & printf(& &a[0]& & = %#x\n& , &a[0]);& & printf(& ptr1+1& &= %#x\n& , ptr1+1);& & printf(& &a[0]+1&&= %#x\n\n& , &a[0]+1);& & printf(& ptr2& &&&= %#x\n& , ptr2);& & printf(& &a& && & = %#x\n& , &a);& & printf(& ptr2+1& &= %#x\n& , ptr2+1);& & printf(& &a+1& &&&= %#x\n\n& , &a+1);& & printf(& ptr3& && &&&= %#x\n& , ptr3);& & printf(& &a[0][0]& & = %#x\n& , &a[0][0]);& & printf(& ptr3+1& && &= %#x\n& , ptr3+1);& & printf(& &a[0][0]+1&&= %#x\n\n& , &a[0][0]+1);& & return 0;}这个题目涉及到两个知识点,其一,讲烂了的数组首元素地址和数组的首地址;其二,数组指针和指针数组的区别。先看第三个指针,int *ptr3 = &a[0][0];这个毫无疑问,是将数组a的首元素地址赋给指针ptr3,由于是int型数组,那么ptr3+1则是偏移一个int型大小,即偏移4个字节,那么ptr3这一组的输出即为:ptr3& && &&&= 0x22fe70&a[0][0]& & = 0x22fe70ptr3+1& && &= 0x22fe74&a[0][0]+1&&= 0x22fe74我们再看第二个指针,int (*ptr2)[8][8]&&= &a;根据之前我们讲过的,这个是取数组a的首地址,ptr2的解释就是:一个指向二维数组[8][8]的指针,那么ptr2+1则是偏移了一个二维数组[8][8]的地址,即为4*8*8=256(0x100)个字节的偏移。那么ptr2这一组的输出即为:ptr2& &&&= 0x22fe70&a& && & = 0x22fe70ptr2+1& &= 0x22ff70&a+1& &&&= 0x22ff70剩下第一个指针,这个和6.9节差不多,int (*ptr1)[8]& & =其实它是等价于int (*ptr1)[8]& & = &a[8] ;那么ptr1则是一个指向一维数组[8]的指针,如果我们这么理解a[8][8] = {a1[8],a2[8],…a8[8]}(当然这个理解是错误的),那么ptr1就是指向a1[8],那么当ptr1+1就是指向a2[8],也就是偏移了一个含有8个int型数据的数组,即4*8=32(0x20)个字节。那么ptr1这一组的输出即为:ptr1& &&&= 0x22fe70&a[0]& & = 0x22fe70ptr1+1& &= 0x22fe90&a[0]+1&&= 0x22fe90这里再一次重复讲一下数组指针和指针数组。int (*p)[8]& && &&&p是指向一个含有8个整型数据的数组的指针(数组指针)int&&*p[8]& && && &p是一个含有8个指针型变量的数组(指针数组)
本帖子中包含更多资源
才可以下载或查看,没有帐号?
http://www.xiaoshuwo.net/255/ 官路红颜&
10:01:27  
第七节&&结构体与联合体
1.1& && &结构体内存对齐问题<font color="#.& && && &这个程序本是我写来验证结构体内存对齐问题,但是我在linux系统和windows系统下的答案让我有点意外,我便将其加进本书。如程序清单7. 1所示,程序输出会是什么?程序清单7. 1&&结构体的内存对齐问题#include&stdio.h& struct Date{& & int& & year& && &;& & int& & month& &&&;& & int& & day& && & ;} ; struct DateType{& & int& & year& && &;& & int& & month& &&&;& & int& & day& && & ;} struct student{& & int& & iNum& && &;& & char& &cName[30] ;& & float&&fScore& & ;& & char& &cSex& && &;& & double menber& & ;} int main( int argc , char *argv[] ){printf( &sizeof(struct Date)& &&&= %d \n\n&, sizeof( struct Date)& &&&) ;& & printf( &sizeof(struct DateType) = %d \n&&&, sizeof( struct DateType) ) ;& & printf( &sizeof(birthday)& && &&&= %d \n\n&, sizeof( birthday )& && & ) ;& & printf( &&birthday.year& && && & = %d \n&&&, &birthday.year& && && &&&) ;& & printf( &&sizeof.month& && && &&&= %d \n&&&, &birthday.month& && && & ) ;& & printf( &&sizeof.day& && && && & = %d \n\n&, &birthday.day& && && && &) ;& & printf( &sizeof(struct student)&&= %d \n&&&, sizeof( struct student)&&) ;& & printf( &sizeof(people)& && && & = %d \n\n&, sizeof( people&&)& && &&&) ;& & printf( &&people.iNum& && && && &= %d \n&&&, &people.iNum& && && && & ) ;& & printf( &&people.cName& && && &&&= %d \n&&&, &people.cName& && && && &) ;& & printf( &&people.fScore& && && & = %d \n&&&, &people.fScore& && && &&&) ;& & printf( &&people.cSex& && && && &= %d \n&&&, &people.cSex& && && && & ) ;& & printf( &&people.menber& && && & = %d \n\n&, &people.menber& && && &&&) ;& & printf( &sizeof(people.menber)& &= %d \n\n&, sizeof( people.menber&&) ) ; & & return 0 ;}传统在windows下,结果大家都应该知道,我现在就直接把window下和linux下结果直接贴出来,大家看看。(如果大家对结果有质疑,大可上机试试,毕竟眼见为实。)sizeof(struct Date)& && &= 12 sizeof(struct DateType) = 12sizeof(birthday)& && && & = 12 &birthday.year& && && && &= 4210832&sizeof.month& && && && & = 4210836&sizeof.day& && && && && &= 4210840 sizeof(struct student)&&= 56sizeof(people)& && && &&&= 56 &people.iNum& && && && &= 4210848&people.cName& && && &&&= 4210852&people.fScore& && && & = 4210884&people.cSex& && && && &= 4210888&people.menber& && && & = 4210896 sizeof(people.menber)& &= 8 请按任意键继续. . .上面是C-Free中运行的结果,你可以试试VC等,答案依然如此。 我们再来看看linux下结果:root@zhuzhaoqi-desktop:/home/zhuzhaoqi/C/prog1.34# ./prog sizeof(struct Date)& &&&= 12
sizeof(struct DateType) = 12 sizeof(birthday)& && &&&= 12
&birthday.year& && && & =
&sizeof.month& && && &&&=
&sizeof.day& && && && & =
sizeof(struct student)&&= 52 sizeof(people)& && && &&&= 52
&people.iNum& && && && &=
&people.cName& && && &&&=
&people.fScore& && && & =
&people.cSex& && && && &=
&people.menber& && && & =
sizeof(people.menber)& &= 8 这是linux下编译的结果。加粗标注区域够让你吃惊吧!说实话,看到第一眼,我也傻了。为什么,我们再看下划线标注区域,people.cSex 在windows下联系上下确实应该占用8个字节,可是,可是为什么在linux下只占用4个字节!!原来,在linux中以4个字节为开辟单元,即不足4个开辟4个,多于4个的继续开辟4个,多出的部分 放进另一个4个字节中。struct student{& & int& & iNum& && &;& & /* 开辟4个字节 */& & char& &cName[30] ;&&/* 开辟32个字节 */& & float&&fScore& & ;& &/* 开辟4个字节 */& & /*开辟4个字节,自己用1个字节,剩下3个,不足以存储menber */char& &cSex& && &;& && & double menber& & ;&&/* 所以这里重新开辟4+4个字节 */} 所以我们得出的答案是:4+32+4+4+8=52。但是,我们一直使用的windows下,以最大单元为开辟单位,即系统先检查结构中最大单位 为double 8个字节,所以以8个字节为单位。student 在Linux和windows下内存开辟如图7. 1和图7. 2所示。
1.1& && &结构体在STM32的应用<font color="#.& && && &如程序清单7. 2所示程序是截取STM32固件库中的一段代码,请问输出是什么?程序清单7. 2&&结构体在STM32的应用#include &stdio.h&typedef& &volatile unsigned int&&vui32;typedef struct {&&vui32&&CRL;&&vui32&&CRH;&&vui32&&IDR;&&vui32&&ODR;&&vui32&&BSRR;&&vui32&&BRR;&&vui32&&LCKR;} GPIO_TypeD #define&&GPIOA& && & (GPIO_TypeDef *)0x#define&&GPIOLED& &&&(GPIO_TypeDef *)GPIOA void func (GPIO_TypeDef *GPIO){& &printf(&GPIO-&CRL = %#x\n& , &(GPIO-&CRL)); & &printf(&GPIO-&CRH = %#x\n& , &(GPIO-&CRH)); & &printf(&GPIO-&LCKR = %#x\n& , &(GPIO-&LCKR));} int main(int argc, char *argv[]){& & printf(&sizeof(GPIO_TypeDef)&&= %d\n& , sizeof(GPIO_TypeDef)) ;& & printf(&GPIOLED=%#x\n& , GPIOLED);& & func(GPIOLED);& & return 0;}如果使用过STM32的固件函数库的话,应该对这个结构体不会陌生,STM32固件函数库就是这样,通过“大行其肆”的结构体和指针实现对一大堆寄存器的配置,在_map.h这个头文件中,定义很多这样的结构体。这样做有什么好处呢,将共同点给抽象出来了,上面7个寄存器就是每个GPIO口寄存器的共有特性,那么只要给定某一个GPIO口的映射地址,很快就可以通过这个结构体得到每个寄存器的地址。能这么做很巧的是ARM的MCU每个寄存器的偏移量都是4个字节或者2个字节,所以能使用结构体完成,如果有一天出现了3个字节的偏移量,我想此时结构体也就没辙了。答案是:sizeof(GPIO_TypeDef)&&= 28GPIOLED& &=0xGPIO-&CRL&&= 0xGPIO-&CRH&&= 0xGPIO-&LCKR = 0x请按任意键继续. . .确实很巧妙,方便! 1.2& && &结构体与指针1.& && && &已知如下所示条件。struct student{ & & long int& & num& & ; & & char& && &&&*name&& ;& & short int&&date& &&&;& & char& && &&&sex& && &;& & short int&&da[5]& & ; }*p;p = (student*)0x1000000 ;那么请问,以下输出什么?printf( &sizeof(*p)& && &&&= %d\n&&&, sizeof(*p)& && &&&) ;printf( &sizeof(student)&&= %d\n&&&, sizeof(student)&&& &) ;printf( &p& && && && && && & = %#x\n& , p& && && && &&&) ;printf( &p + 0x200& && && &= %#x\n& , p + 0x200& && &&&) ;printf( &(char*)p + 0x200& && & = %#x\n& , (char*)p + 0x200& && &) ;printf( &(int*)p + 0x200& && &&&= %#x\n& , (int*)p + 0x200& && &&&) ;第一个输出不解释,内存对齐问题,结构体指针,答案为:24。第二个输出答案为:24。第三个输出,为已知,答案为:0x1000000。第四个输出,由于p此时是结构体类型指针,那么p+0x200 = p + 0x200*sizeof(student)。所以 p + 0x200 = p + 0x200 * 24 = 0x1000000 + 0x3000 = 0x1003000。第五个输出,由于p被强制转换成了字符型指针,那么p + 0x200 = 0x1000200。第六个输出同理为:p + 0x200 = 0x1000800。 1.3& && &联合体的存储<font color="#.& && && &如程序清单7. 3所示,程序输出什么?程序清单7. 3&&联合体的存储union {& && & struct {& && & char L;& && & char H;& & }B}N; int main(int argc, char* argv[]){& & N.i = 0x1234;& & printf(&N.Bity.L = %#x\n&,N.Bity.L);& & printf(&N.Bity.H = %#x\n&,N.Bity.H);& & return 0;}
结构体的成员是共用一块内存,也就是说N.i和N.Bity是在同一个地址空间中。那么好办了,但是要注意,CPU是小段存储模式,所以低字节存储在低地址中,高字节存储在高地址中。那么N.Bity.L是取了低地址,也就是得到低字节,即为0x34,N.Bity.H是取了高字节,即为0x12。在电脑中,int是占4字节,所以存储方式如图10. 3所示。 其实这里有一个很巧妙的用法可以用于C51单片机中,为了与上面不重复,假设C51的存储模式是大端模式。在C51的定时器,给定时器赋初值的时候,要将高八位和低八位分别赋给模式1定时器的高位预置值和低位预置值,有这么个式子:THx = ()/256;TLx = ()%256. 那么我们就可以让这样写这个程序union {& && & struct {& && & unsigned char H;& && & unsigned char L;& & }B}N; int main(int argc, char* argv[]){& & ……& & N.i = 65536 - 10000;THx = N.Bity.H ;TLx = N.Bity.L ;……& & return 0;}这样很方便并且高效地将高低位置好,其实单片机是一个很好学习C语言的载体,麻雀虽小,但是五脏俱全。65536 – 10000 = 55536 = 0xD8F0。由于在单片机中,int是占2字节,那么存储方式如图7. 4所示。
1.1& && &结构体在联合体中的声明1.& &如程序清单7. 4所示,请问:printf( &%d\n& , sizeof(T.N) ) ;printf( &%d\n& , sizeof(T)& & ) ;输出什么?程序清单7. 4&&结构体在联合体中的声明union T {& && & struct N {& && & int& && && & float&&& && && & };};第一个结构体嘛,4+4+8=16;第二个嘛,最大元素所占内存开辟,则为:16!真的是这样吗?正确答案是:16,4!union T {& && & struct N {& && & int& && && & float&&& && && & }A;};如果这个程序是这样,输出又是多少呢?正确答案是:16,16!不知道你是否知道其中的原因了呢? 1.2& && &结构体与联合体的内存1.& &如程序清单7. 5所示,语句printf(&%d&,sizeof(struct date)+sizeof(max));的执行结果是?程序清单7. 5&&结构体与联合的内存typedef union { int k[5]; } DATE; struct data { int& &&& DATE& &} DATE很明显,这里考查的是联合体和结构体的内存对齐问题。联合体的内存大小是以最大类型存储内存作为依据,而结构体则是内存对齐相加。上面例子的联合体最大是:20,所以sizeof(max) = 20 ;在结构体中,sizeof(cat) = 4 ,sizeof(cow) =&&20 ,sizeof(dog) = 8 ,由于内存都是对齐的,所以siezof(struct date) = 32 .所以最终答案是:52.#include &stdio.h& typedef&&union { & & long int&&i& & ;& & int& && & k[5] ;& & char& && &c& & ; }DATE ; struct&&data { & & int& &&&& & DATE& && & double&& }
int main(int argc, char *argv[]){ & & DATE & & printf(&sizeof(cat)=%d\n&,& &sizeof(too.cat));& & printf(&sizeof(cow)=%d\n&,& &sizeof(too.cow));& & printf(&sizeof(dog)=%d\n\n&, sizeof(too.dog));
& & printf(&sizeof(struct data)=%d\n&,sizeof(struct data));& & printf(&sizeof(max)=%d\n&, sizeof(max));& & printf(&sizeof(struct data)+ sizeof(max)=%d\n&,sizeof(struct data)+ sizeof(max)); & & return 0;} 运行结果是:sizeof(cat)=4sizeof(cow)=20sizeof(dog)=8 sizeof(struct data)=32sizeof(max)=20sizeof(struct data)+ sizeof(max)=52请按任意键继续. . .内存对齐问题是一个比较难以理解的内存问题,因为摸不着、难以猜透。 1.3& && &再论联合体与结构体1.& && && &如程序清单7. 6所示,程序输出什么?程序清单7. 6&&再论联合体与结构体union {& & struct {& && && &unsigned char c1:3;& && &&&unsigned char c2:3;& && &&&unsigned char c3:2;& & }s;& &}u;int main(int argc, char *argv[]){& & u.c = 100;& & printf(&u.s.c1 = %d\n&, u.s.c1);& & printf(&u.s.c2 = %d\n&, u.s.c2);& & printf(&u.s.c3 = %d\n&, u.s.c3);& & return 0;}这个程序考查对结构体和联合体的理解。首先我们应该知道,u.c和u.s是在同一个地址空间中,那么u.s中存储的数据即为100。100转化为二进制为:。由于是小端模式存储方式,那么u.s.c1取最低三位100,即为十进制的4;u.s.c2取中间三位100,即为十进制的4;而u.s.c3取最高两位01,即为十进制的1。所以输出即为:4,4,1。
u.c和u.s的存储方式如图7. 5所示。
本帖子中包含更多资源
才可以下载或查看,没有帐号?
10:04:12  
本帖最后由 zzq宁静致远 于
08:22 编辑
第八节&&内存分配与内存释放
1.1& && &malloc <font color="#.& && & 某32 位系统下, C++程序,请计算 sizeof&&的值 。char str[] = “http://www.ibegroup.com/” ;char *p = int n = 10; 请计算 sizeof (str ) =&&?(1) sizeof ( p ) =&&?(2) sizeof ( n ) =&&?(3) void Foo ( char str[100]){} 请计算 sizeof( str ) =&&?(4) void *p = malloc( 100 ); 请计算 sizeof ( p ) =&&?(5) 答案是:(1).25& &(2).4& &(3).4& &(4).4& &(5).4不管是int *还是char *指针,指针长度都是4.有了这点sizeof(p) = 4应该就没有任何问题了。sizeof(n) = 4 , 因为整型长度为4。剩下sizeof(str)了,我们把char str[100]变下形你可能就知道了,其实char str[100]和*(str+100)是等效的,也就是说传进去的是指针,而不是数组,那么sizeof(str) = 4就应该可以理解了。2.& &如程序清单8. 1所示,请问运行Test函数会有什么样的结果? 程序清单8. 1&&malloc()的应用1void GetMemory(char *p) { & & p = (char *)malloc(100); } void Test(void) { & & char *str = NULL; & & GetMemory(str); & & strcpy(str, &hello world&); & & printf(str); } 我敢说很多人看到这里会冒出来的答案是: hello world 。实际上答案是:NULL 。是不是傻眼了。程序意图很简单,想通过GetMenory这个函数改变str的值。事实上,GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入实参的值,执行完 char *str = NULL; GetMemory( str );这2条程序后的str仍然为NULL 。
<font color="#.& &如程序清单8. 2所示,请问运行Test函数会有什么样的结果? 程序清单8. 2&&malloc()的应用2char *GetMemory(void) { & & char p[] = &hello world&; & & } void Test(void) { & & char *str = NULL; & & str = GetMemory(); & & printf(str); } 这个是hello world 了吧 !这个还真不是输出hello world 。有同学就要问了,str = GetMemory(),而Getmemory()函数返回的是p , 而p[] = &hello world & ,怎么可能不是hello world ! 实际上,p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。所以输出什么我也不知道,很可能是乱码。所以要理解变量的生存周期,否则就死翘翘了。<font color="#.& &如程序清单8. 3所示,请问运行Test函数会有什么样的结果?程序清单8. 3&&malloc()的应用3 void GetMemory(char **p, int num) { & & *p = (char *)malloc(num); } void Test(void) { & & char *str = NULL; & & GetMemory(&str, 100); & & strcpy(str, &hello&); & & printf(str); } 有些人到这里不敢吭声了,这个会是输出什么?答案是:hello。这个题目我不分析,结合上面2题请读者自己分析下。<font color="#.& && && &如程序清单8. 4所示,请问这个会是输出什么?程序清单8. 4&&malloc()的应用4#include &stdio.h&char *str(){& & char *p = &abcdef&;& & return}int main(int argc, char *argv[]){& & printf(&%s&, str());& & return 0;}乍眼一看,在哪里见过?是的,确实似曾相识。有记忆了吧,会不会有人立马说出答案:输出乱码? char *GetMemory(void){char p[] = &hello world&;}void Test(void){char *str = NULL;str = GetMemory();printf(str);}上面这个题目的答案确实是:乱码!但是char *p = &abcdef&;和char p[] = “abcdef”是有区别的。char *p = &abcdef&这个程序的正确答案是输出:abcdef。为什么?是的,有人会说,你不是说数组是可以退化成指针吗?但是,你要知道,数组是存储在栈中,p[] = “abcdef”实在执行这条语句时,abcdef才会赋给p[],并且栈在执行完成之后是会自动销毁;但是指针是保存在堆中,当开辟了*p这个地址的时候,abcdef就已经存储在p中,然而堆只有在程序结束之后才会销毁,所以是可以输出abcdef这个字符串。1.2& && &malloc(0) <font color="#.& && && &把这个独立开来是因为很少这样使用,但是又会使用。如程序清单8. 5所示,程序会输出什么?程序清单8. 5&&malloc(0)int main(int argc, char *argv[]){& & char *ptr = NULL;& & if ((ptr = (char *)malloc(0)) == NULL) {& && & printf(&Null pointer\n&);& && & printf(&ptr = %#x\n&, ptr);& & }&&else{& && & printf(&Valid pointer\n&);& && & printf(&ptr = %#x\n&, ptr);& & }& & return 0;}我想很多人的第一个感知是输出:Null pointer!但是很遗憾,是输出Valid pointer!虽然ptr所开辟的内存空间为0,但是ptr是不会等于NULL的。
有些字母叫模糊,待核实&
10:06:18  
第九节& &笔试中的几个常考题
1.1& && &strcpy<font color="#.& &已知strcpy函数的原型是char *strcpy(char *strDest, const char *strSrc); 其中strDest是目的字符串,strSrc是源字符串。不调用C++/C的字符串库函数,请编写函数 strcpy。char *strcpy(char *strDest, const char *strSrc) { & & assert((strDest!=NULL) && (strSrc !=NULL)); & & char *address = strD & & while( (*strDest++ = * strSrc++) != ‘\0’ ) & & NULL ; & & } 这个题目创维和华为都曾用来做为考题。在程序开头我们肯定要断言strDest和strSrc不等于NULL,assert()的作用是:断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为 true。如果表达式计算为 false,那么系统会报告一个 Assertionerror。我们注意到返回值是char *类型!这里是为了实现链式表达式。 将这个题目再引申下,已知strncpy的函数原型是char *strncpy( char *to, const char *from, size_t count ); 其中to是目的字符串,from是源字符串。不调用C++/C的字符串库函数,请编写函数 strncpy。提示:将字符串from 中至多count个字符复制到字符串to中。如果字符串from 的长度小于count,其余部分用'\0'填补。返回处理完成的字符串。char *strncpy(char *to, const char *from , size_t count ){ & & assert((to!=NULL) && (from !=NULL)); & & char *address = & & while( count != 0 )& & {& && &&&*address++ = *from++ ;& && &&&if ( *from == '\0')& && &&&{& && && &&&*address++ = '\0' ;& && &&&}& && &&&count-- ;& & }& & } 1.2& && &CPU的使用率<font color="#.& &写一个程序,让你的电脑CPU使用率一直运行在50%。#include &iostream&#include &stdlib.h&/* 让CPU的使用率在50% */ int main(int argc, char *argv[]){& & for( ; ; ){ & && & for( int i = 0 ; i &
; i++ ) ; & && & _sleep(10) ; & & } & & return 0;}这里的是根据我自己电脑算出来的,因为我的电脑主频是2.0GHz。留一个问题给读者,怎样让自己的电脑CPU以正弦曲线运行?注意:对于新代处理器由于优化,可能做不到。 1.3& && &二进制数据中1的个数<font color="#.& &写一个程序,随意输入x,输出x的二进制数据中1的个数。这个程序的算法很多,可以一位一位右移进行测试,也有其他办法。右移法我就不再累赘,这个方法简单,但是时间复杂度会比较大。看看下面这个方法:int number( unsigned int x ){& & unsigned int countx = 0; & & while (x) { & && & countx ++ ;& && & x = x&(x-1) ; & & } & &}如果x大于0,那么x一定有一位为1,所以进入while之后countx先加1。如果x=100,那么经过x=x&(x-1),x为0,countx为1,此时结束程序。 1.4& && &二进制高位到低位实现逆变<font color="#.& &编写一个函数,实现将一个32位int型数据的二进制高位到低位实现逆变,例如:变成。这个题目的解决方法很多,代表性的有两种。int func(unsigned int uiData , int length){& & unsigned int uiValue = 0 ;& & int& && && && &i& && &&&= 0 ;& & for&&( i = 0 ; i & i++ )&&{& && & uiValue&&= (uiValue && 1) + (uiData & 0x01) ;& && & uiData& &= uiData && 1 ;& & }& & return uiV}这个方法比较常见,通过移位的方法将高位到低位实现逆序。但是这个方法存在唯一的不足之处是效率低,要进行32次移位和运算。int func (unsigned int uiData){& & unsigned int uiValue = 0 ; & & /* 分而治之的思想 */& & /* 高16位和低16互换 */& & uiValue = ((uiData && 16)&0x0000ffff) | ((uiData && 16)&0xffff0000); /*高低16位中的高低8位互换*/& & uiValue = ((uiValue && 8)&0x00ff00ff) | ((uiValue && 8)&0xff00ff00); /*8位中的高低4位互换*/& & uiValue = ((uiValue && 4)&0x0f0f0f0f) | ((uiValue && 4)&0xf0f0f0f0); /*4位中的高低2位互换*/& & uiValue = ((uiValue && 2)&0x) | ((uiValue && 2)&0xcccccccc); /*2位中的高低位互换*/& & uiValue = ((uiValue && 1)&0x) | ((uiValue && 1)&0xaaaaaaaa);
& & return uiV}这个程序只需要位操作5次,就能实现高位到低位的逆序。我们逐句程序分析一下。假设uiData = 01 01 。执行完成下面这句程序,/* 高16位和低16互换 */uiValue = ((uiData && 16)&0x0000ffff) | ((uiData && 16)&0xffff0000);得到01 01 ,也就是高16位和低16位互换。 执行完成:/*高低16位中的高低8位互换*/uiValue = ((uiValue && 8)&0x00ff00ff) | ((uiValue && 8)&0xff00ff00);得到00 00 ,也就是高低16位中高8位和低8位互换。 执行完成:/*8位中的高低4位互换*/uiValue = ((uiValue && 4)&0x0f0f0f0f) | ((uiValue && 4)&0xf0f0f0f0);得到01 01 ,也就是从高位起,每8位段的高4位和低4位完成互换。 执行完成:/*4位中的高低2位互换*/uiValue = ((uiValue && 2)&0x) | ((uiValue && 2)&0xcccccccc);得到01 01 ,也就是从高位起,每4位段的高2位和低2位完成互换。 执行完成:/*2位中的高低位互换*/uiValue = ((uiValue && 1)&0x) | ((uiValue && 1)&0xaaaaaaaa); 得到10 10 。也就是从高位起,每2位段的高1位和低1位完成互换。和原始数据01 01 进行对比,逆序。 1.5& && &大小端测试<font color="#.& &编写一个函数,测试MCU是大端模式存储还是小端模式存储。/******************************************************************&&函数名称:LBEndian**&&函数功能:大小端测试函数**&&入口参数:None**&&出口参数:1 or 0****************************************************************/int LBEndian (void){& & unsigned int& &uiNumber = 0x ;& & unsigned char&&*ucptr& &= (void *)0&&; & & /* 将最低位1一个字节赋给ucptr */& & ucptr = (unsigned char *)(&uiNumber) ; & & /* 如果是小段模式,则返回1*/& & if ( 0x78 == (*ucptr) ) {& && &&&return 1 ;& & }&&/* 如果是大端模式,则返回0 */else {& && &&&return 0 ;& & }}ucptr = (void *)0 ,这样做是为了防止野指针的危患。通过ucptr = (unsigned char *)(&uiNumber) (好好理解这句程序);截取低地址的存储字节数据,如果低地址存储的是低字节,那么就是小端模式,如果低字节存储的是高字节,那么就是大端模式。 1.6& && &二分查找<font color="#.& &写一个函数实现二分查找int BinarySeach(int *iArray, int key, int n){& & int iLow&&= 0 ;& & int iHigh = n - 1;& & int iM & & while (iLow &= iHigh) {& && & iMid = (iHigh + iLow)/2;& && & if (iArray[iMid] & key) {& && && &&&iHigh = iMid - 1 ;& && & }&&else if (iArray[iMid] & key) {& && && &&&iLow = iMid + 1 ;& && & }&&else{& && && &&&return iM& && & }& & }}数据结构中的各种查找算法在笔试中是无处不在,在工程应用中也是“无孔不入”。所以作为一个软件工程师,必须牢牢掌握各种查找算法。 1.7& && &int (*p[10])(int)<font color="#.& &int (*p[10])(int) 表示的是什么?函数指针数组,int(*p)(int),我们知道这是一个函数指针,一个指向int Fun(int)函数的指针,那么int (*p[10])(int)即为函数指针数组。 1.8& && &对绝对地址赋值的问题涉及到内存的问题,都让很多人望而却步,因为内存确实是地雷阵,稍不小心就会引爆。<font color="#.& &要对绝对地址0x10 0000赋值,我们该怎么做?& &*(unsigned&&int&&*)0x10 0000 = 1234 ;通过这个程序我们把常量1234存储在地址为0x10 0000。 <font color="#.& &如果想让程序跳转到绝对地址为0x10 0000去执行,应该怎么做?*(&&(void (*)( ))0x100000 ) ( );首先要将0x10 0000转换成函数指针:(void (*)( ))0x100000然后再调用他:*(&&(void (*)( ))0x100000&&) () ;因为内存是摸不着,猜不透的,所以很像地雷阵,随时都可能挂掉。
08:59:59  
第十节&&数据结构之冒泡排序、选择排序
我相信很多人曾经写冒泡排序和选择排序都是一个算法一个代码,并且还一个一个
写得不亦乐乎。zzq宁静致远今天就告诉你如何写出一手漂亮的C语言代码,当你看完
今天的帖子,你就会恍然顿悟曾经自己写的代码如此不堪。
1. 冒泡排序
1.1 底层冒泡排序的头文件
为了增强代码的可移植性,健壮性。我们将冒泡排序的算法封装在库中,我们只需要调用库函数即可。冒泡排序头文件程序如程序清单1. 1所示。
程序清单1. 1&&冒泡排序头文件
*&&声明比较函数,升序还是降序
typedef int (*COMPAREFUN)(const void *pvData1, const void *pvData2);
/*******************************************************************************
**函数名称:&&bubbleSort
**函数功能:&&冒泡排序
**入口参数:&&*pvData:& & 需要进行排序的数组
& && && && &&&stAmount:& &数组中包含的元素个数
& && && && &&&stSize:& &&&元素内存大小
& && && && &&&CompareFun: 需要排序的数据类型、升序还是降序
**出口参数:&&
*******************************************************************************/
extern void bubbleSort (void *pvData, size_t stAmount, size_t stSize , COMPAREFUN CompareFun);
为了各种数据的兼容性,所有不确定情况的数据类型都使用void *。
1.2 底层数据交换函数实现
通过函数指针类型数据,向swap函数传入需要交换的两个数据的地址和数组元素内存大小,实现数据的交换。swap函数代码如程序清单1. 2所示。
程序清单1. 2&&swap函数
/*******************************************************************************
**函数名称:&&__swap
**函数功能:&&数据交换
**入口参数:&&*pvData1:&&需要交换的数据一
& && && && &&&*pvData2:&&需要交换的数据二& &
& && && && &&&stDataSize:元素内存大小
**出口参数:&&
*******************************************************************************/
static void __swap (void *pvData1, void *pvData2, size_t stDataSize)& &
unsigned int *p1=(unsigned int)pvData1;
unsigned int *p2=(unsigned int)pvData2;
unsigned int uiT
while ( stDataSize &= sizeof(unsigned int) )&&//一次交换sizeof(unsigned int)个字节
&&(*p1)&&^=(*p2);
&&(*p2)&&^=(*p1);
&&(*p1++)^=(*p2++);
&&stDataSize -= sizeof(unsigned int);
if (stDataSize&0)
& &*&&void *memmove( void *to, const void *from, size_t count );
& &*&&函数从from中复制count 个字符到to中,并返回to指针。
&&memmove( &uiTemp,p1,stDataSize);
&&memmove( p1,p2,stDataSize);
&&memmove( p2,&uiTemp,stDataSize);
这里传进去的是三个参数分别是:pvData1,为需要交换的两个数据中的第一个数据的地址;pvData2,为需要交换的两个数据中的第二个数据的地址;stDataSize:数组中元素内存的大小。
传进去之后,先将两个数据的地址强制转化为(int*)型地址。数据的交换分成两个部分:如果元素内存大小大于一个sizeof(unsigned int)个字节大小,则一次性交换4位;当stDataSize大于0且小于一个sizeof(unsigned int)个字节大小时,再通过memmove交换剩下的部分。
1.3 冒泡排序算法实现
冒泡排序的基本思想是通过相邻元素之间的比较和交换使得排序码较小的元素上移或下移。冒泡排序代码如程序清单1. 3所示。
程序清单1. 3&&冒泡排序
/*******************************************************************************
**函数名称:&&bubbleSort
**函数功能:&&冒泡排序
**入口参数:&&*pvData:& & 需要进行排序的数组
& && && && &&&stAmount:& &数组中包含的元素个数
& && && && &&&stSize:& &&&元素内存大小
& && && && &&&CompareFun: 需要排序的数据类型、升序还是降序
**出口参数:&&
*******************************************************************************/
void bubbleSort (void *pvData, size_t stAmount, size_t stSize , COMPAREFUN CompareFun)
int iNoSwapFlg = 0;
void *pvThis = NULL;
void *pvNext = NULL;
&&*&&冒泡排序
i = stAmount - 1;
&&iNoSwapFlg = 0;
&&pvThis = pvD
&&pvNext = (char *)pvData + stS
& &if (CompareFun(pvThis, pvNext) & 0) {
& & __swap(pvThis, pvNext, stSize);
& & iNoSwapFlg = 1;
& &pvThis = pvN
& &pvNext = (char *)pvNext + stS
&&} while (--j != 0);
&&if (iNoSwapFlg == 0) {
} while ( --i != 0);
bubbleSort函数传入的有四个参数:pvData:需要进行排序的数组的首元素地址,但是这个地址也就是需要进行排序的数组的地址。这个区别就好像是广东的省政府在广州,而广东省首号城市广州市的市政府也在广州,虽然地址相同,但是意义不同。为了证明这个观点,我定义了两个数组进行测试。
static int iArray[]& &&&= {39, 33, 18, 64, 73, 30, 49, 51, 81};
& &&&static char *strArray[] ={&forARM&,&mzdzkj&,&c language&,&shenzhen&,&china&};
printf(&&iArray = %#x \n& , &iArray ) ;
printf(&&iArray[0] = %#x \n& , &iArray[0] ) ;
printf(&strArray = %#x \n& , strArray ) ;
printf(&&strArray = %#x \n& , &strArray ) ;
编译之后运行的结果为:
&iArray = 0x402000
&iArray[0] = 0x402000
strArray = 0x402024
&strArray = 0x402024
所以在这个函数中,无论传入的是数组的首元素地址,还是数组的地址,都是可以的,因为有这么一句程序:
pvNext = (char *)pvData + stS
所以无论如何,pvNext都是下一元素的地址。
测试程序:
printf(&(char*)&iArray[0] + sizeof(iArray[0]) = %#x \n& , (char*)&iArray[0] + sizeof(iArray[0]) ) ;
printf(&&iArray[1] = %#x \n\n& , &iArray[1] ) ;
printf(&(char*)&strArray[0] + sizeof(strArray[0]) = %#x \n& , (char*)&strArray[0] + sizeof(strArray[0]) ) ;
printf(&&strArray[1] = %#x \n& , &strArray[1] ) ;
(char*)&iArray[0] + sizeof(iArray[0]) = 0x402004
&iArray[1] = 0x402004
(char*)&strArray[0] + sizeof(strArray[0]) = 0x402028
&strArray[1] = 0x402028
stAmount:数组中包含的元素个数,我们通常使用:sizeof(strArray) / sizeof(strArray[0],即为数组总长度除以元素内存大小,这个结果就是数组元素的个数。
stSize:元素内存大小,sizeof(strArray[0],因为数组内每一个元素的类型相同,所以每个元素的内存大小也就相同。
CompareFun:需要排序的数据类型、升序还是降序。这个函数的原型是:
typedef int (*COMPAREFUN)(const void *pvData1, const void *pvData2);
如果是整型数组需要升序排列,则函数为如程序清单1. 4所示:
程序清单1. 4&&整型数组升序
/*******************************************************************************
**函数名称:&&int_Rise_cmp
**函数功能:&&对int进行升序排列
**入口参数:&&*x:
& && && && &&&*y:
**出口参数:&&( *(int *)x - *(int *)y )
& && && && &&&确定升序
*******************************************************************************/
int int_Rise_cmp(void *x , void *y)
return ( *(int *)x - *(int *)y );
我们就综合上述对其进行一个整体的分析。假设需排序的数组为:static int iArray[]& &&&= {39, 33, 18, 64, 73, 30, 49, 51, 81};pvData是需排序数组的首元素地址,由:
&&pvThis = pvD
&&pvNext = (char *)pvData + stS
那么pvThis即为数组首元素的地址,也就是&iArray[0],pvNext为下一个元素的地址,也就是&iArray[1]。接着通过CompareFun(pvThis, pvNext)比较两个元素的大小,进入CompareFun,也就是int_Rise_cmp函数,x即为pvThis,y即为pvNext。这样x即为数组首元素的地址,这里还是void*,我们通过强制转化,将x指向整型,即为(int*)x,再去地址,也就是( *(int *)x,数组首元素,y以此类推。如果( *(int *)x - *(int *)y ) &0,也就是CompareFun(pvThis, pvNext)&0,则交换这两个数据,从而达到从小到大排列的目的。交换完成之后,
pvThis = pvN
pvNext = (char *)pvNext + stS
这样以此类推。
static int iArray[]& &&&= {39, 33, 18, 64, 73, 30, 49, 51, 81};
& &&&static char *strArray[] ={&forARM&,&mzdzkj&,&c language&,&shenzhen&,&china&};
第二个数组值得一提,这是一个指针数组,即为数组中存储的是指针变量。不相信的话可以测试一下。
printf(&strArray[0] = %#x \n\n& , strArray[0] ) ;
strArray[0] = 0x403000
很显然是指针。上述两个数组经过排序之后的测试结果如程序清单1. 5所示。
程序清单1. 5&&测试结果
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
整型数组数据排序之前:
39 33 18 64 73 30 49 51 81
字符串数组排序之前:
'forARM' 'mzdzkj' 'c language' 'shenzhen' 'china'
整型数组数据升序之后:
18 30 33 39 49 51 64 73 81
整型数组数据降序之后:
81 73 64 51 49 39 33 30 18
字符串数组数据升序之后:
'c language' 'china' 'forARM' 'mzdzkj' 'shenzhen'
字符串数组数据降序之后:
'shenzhen' 'mzdzkj' 'forARM' 'china' 'c language'
2.选择排序
2.1 选择排序算法
一个好的迭代器,只需要修改排序算法,其他的程序都无需修改。其实这里只需要把冒泡排序算法修改为选择排序算法即可。
选择排序算法程序如程序清单2. 1所示。
程序清单2. 1&&选择排序函数
/*******************************************************************************
**函数名称:&&selectSort
**函数功能:&&选择排序
**入口参数:&&*pvData:& & 需要进行排序的数组
& && && && &&&stAmount:& &数组中包含的元素个数
& && && && &&&stSize:& &&&元素内存大小
& && }

我要回帖

更多关于 c语言函数调用 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信