发 帖  
原厂入驻New
发烧友10周年庆典,全网超值优惠来袭!千元现金券,下单抽奖赶紧参与》》

[经验] 朱兆祺教你如何攻破C语言学习、笔试与机试的难点(连载)

2013-7-31 08:47:54  183605 C语言 C语言 C语言 C语言 C语言
分享
341
本帖最后由 zzq宁静致远 于 2014-2-27 19:42 编辑

    再过1个月又是一年应届毕业生应聘的高峰期了,为了方便应届毕业生应聘,笔者将大学四年C语言知识及去年本人C语言笔试难点进行梳理,希望能对今年应届毕业生的应聘有所帮助。

2013年10月18日更新-->    攻破C语言这个帖子更新到这里,我不仅仅是为了补充大学学生遗漏的知识,我更重要的是希望通过我的经验,你们实际项目中的C语言写得漂亮,写出属于你的风格。“朱兆祺STM32手记”(http://bbs.elecfans.com/jishu_385613_1_1.html)一帖中我就多次强调过编程的模块化,要考虑到可移植性和别人的可阅读性。我在某公司实习的时候,拿到一个程序,我当时就想吐槽,我想除了这个程序的当时写作者能够看懂之外,其他人有谁还能看懂,程序构架、可移植性性就更是一塌糊涂。结果我全部推到重写。因此明志电子科技工作室从承接第一个项目做开始,我便和搭档说,我们必须制定我们工作室的编程规范和编程风格,这样就算给谁,换成谁,拿到项目都能马上接下去做。
    朱兆祺在这个帖子将会不断更新,明志电子工作室的项目经验也将在电子发烧友论坛不断贴出,我希望能用我们仅有的力量,将我们的经验毫不保留传授给大家。我只希望在未来某一天,有那么几个人会竖着大拇指说:明志电子科技工作室的经验受益匪浅就够了。我相信,在深圳,明志电子科技工作室的影响力会日益增长,因为我们已经规划好了未来脚步。
    C语言是一门技术,但更是一门艺术。写一个C语言代码不难,写一个高水平、漂亮的代码很难。朱兆祺将在此帖不断为大家攻破C语言。

<--朱兆祺于2013年10月18日

-->朱兆祺更新于2014年2月21日:
    深圳市馒头科技有限公司于2014年2月注册成立,当初命名为馒头科技,不管是吴坚鸿所说亲切还是谢贤斌所说草根。馒头科技永远以用户满意为我们的追求,馒头科技不追求锦上添花,但是我们很愿意为每一位客户雪中送炭。因为锦上添花每一个人都会做,但是雪中送炭却不是每一个人愿意去做。
    馒头科技往后将为每一位读者送上更为精美的学习资料。敬请期待。



第一节  C语言编程中的几个基本概念
http://bbs.elecfans.com/jishu_354666_1_1.html

第二节  数据存储与变量
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2088253&fromuid=222350

第三节  数学算法解决C语言问题
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2088283&fromuid=222350

第四节  关键字、运算符与语句
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2088352&fromuid=222350

第五节    C语言中的细节
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2088375&fromuid=222350

第六节  数组与指针
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2088417&fromuid=222350

第七节  结构体与联合体
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2088582&fromuid=222350

第八节  内存分配与内存释放
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2088596&fromuid=222350

第九节   笔试中的几个常考题
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2088606&fromuid=222350

第十节  数据结构之冒泡排序、选择排序
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2092632&fromuid=222350

第十一节   机试题之数据编码
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2096393&fromuid=222350

第十二节  机试题之十进制1~N的所有整数中出现“1”的个数
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2097513&fromuid=222350

第十三节  机试题之  遍历单链表一次,找出链表中间元素
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2103563&fromuid=222350

第十四节  机试题之全排序
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2105648&fromuid=222350


第十五节 机试题之大整数运算
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2109737&fromuid=222350


第十六节  机试题之大整数减法与乘法
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2115675&fromuid=222350

第十七节  算法之二分查找
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2128027&fromuid=222350

第十八节  数据结构之单向链表(颠覆你手中数据结构的三观)
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2139670&fromuid=222350

第十九节  数据结构之双向链表(接着颠覆你手中的数据结构三观)
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2156978&fromuid=222350

第二十节 数据结构之栈
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2193337&fromuid=222350


C语言技术公开课第一讲——编译环境给C语言带来的困扰,网络课程讲义
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2230571&fromuid=222350

第二十一节  通过加减法高效的求出两个无符号整数的商和余数
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2252461&fromuid=222350

第二十二节  表达式计算器(1)
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2280837&fromuid=222350

第二十三节  表达式计算器(2)
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2325485&fromuid=222350

第二十四节  表达式计算器(3)
http://bbs.elecfans.com/forum.ph ... 4547&fromuid=222350

第二十五节  表达式计算器(4)
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2395966&fromuid=222350

C语言技术公开课第三讲  const问题
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2434706&fromuid=222350

第二十六节  序列差最小
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2466868&fromuid=222350

第二十七节  sizeof与strlen的深入
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=354666&pid=2676569&fromuid=222350

第二十八节  C与C++中的const

第二十九节 do、while
第三十节 变量的生命周期

第一节  C语言编程中的几个基本概念
1.1      include< >与#include" "


1.   #include< >和#include" "有什么区别?
这个题目考查大家的基础能力,#include< >用来包含开发环境提供的库,
#include" "用来包含.c/.cpp文件所在目录下的头文件。注意:有些开发环境可以在当前目录下面自动收索(包含子目录),有些开发环境需要指定明确的文件路径名。
1.2      switch()
1.   switch(c) 语句中 c 可以是 int, long, char, float, unsigned int 类型?
其实这个题目很基础,c应该是整型或者可以隐式转换为整型的数据,很明显不能是实型(float、double)。所以这个命题是错误的。
1.3      const
1.   const有什么用途?
虽然const很常用,但是我相信有很多人仍然答不上来。
(1) 欲阻止一个变量被改变,可以使用const 关键字。在定义该 const 变量时,通常需要对它进行初 始化,因为以后就没有机会再去改变它了;
(2) 对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二者同时指定为 const;
(3) 在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4) 对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成员变量;
(5) 对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为“左值”。
1.4      #IFndef/#define/#endif
1.   头文件中的 #ifndef/#define/#endif 干什么用?
其实#ifndef、#define、#endif这些在u-boot、linux内核文件中经常见到,在这么大型的程序中大量使用,可见它的作用不可小觑。
这些条件预编译多用于对代码的编译控制,增加代码的可裁剪性,通过宏定义可以轻松的对代码进行裁剪。
#ifndef/#define/#endif最主要的作用是防止头文件被重复定义。
1.5      全局变量和局部变量
times New Roman">1.         全局变量和局部变量在内存中是否有区别?如果有,是什么区别?
全局变量储存在静态数据库,局部变量在堆栈。 其实,由于计算机没有通用数据寄存器,则函数的参数、局部变量和返回值只能保存在堆栈中。提示:局部变量太大可能导致栈溢出,所以建议把较大数组放在main函数外,防止产生栈溢出。
思考:如程序清单1. 1所示。会出现怎样的情况?
程序清单1. 1  大数组放在main函数中导致堆栈溢出
int main(int argc, char *argv[])
{
    int iArray[1024 * 1024];
    return 0;
}






评分

参与人数 3威望 +7 +7 积分 +10 收起 理由
eminjie + 2 + 2
A670521546 + 5 + 5 很精彩,期待您更多的分享
冰葑世纪 + 5 + 5 您的付出是论坛的动力,感谢您一直支持!.

查看全部评分

刘芸 2013-7-31 19:37:13
顶起!!!!!!!!!!!!!
回复

举报

zzq宁静致远 2013-8-1 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 uiTemp;

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 i, j;
int iNoSwapFlg = 0;
void *pvThis = NULL;
void *pvNext = NULL;

/*
  *  冒泡排序
  */
i = stAmount - 1;
do {
  
  iNoSwapFlg = 0;
  pvThis = pvData;
  pvNext = (char *)pvData + stSize;
  j = i;
  
  do {
   if (CompareFun(pvThis, pvNext) > 0) {
    __swap(pvThis, pvNext, stSize);
    iNoSwapFlg = 1;
   }
   pvThis = pvNext;
   pvNext = (char *)pvNext + stSize;
  } while (--j != 0);
  
  if (iNoSwapFlg == 0) {
   break;
  }
} 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 + stSize;
所以无论如何,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 = pvData;
  pvNext = (char *)pvData + stSize;
那么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 = pvNext;
pvNext = (char *)pvNext + stSize;
这样以此类推。
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:     元素内存大小
              CompareFun: 需要排序的数据类型、升序还是降序
**出口参数:  
*******************************************************************************/
void selectSort (void *pvData , size_t stAmount, size_t stSize , COMPAREFUN CompareFun)
{
int i , j  , k  ;
void *pvThis  = NULL;
    void *pvThat    = NULL;
/*
  *  冒泡排序
  */

#if 0
printf("pvData  = %#x\n" ,pvData ) ;
printf("pvThis  = %#x\n" ,pvBegin ) ;
printf("pvThat  = %#x\n" ,pvEnd ) ;
#endif
for ( i = 0 ; i < stAmount  ; i++ ) {
  
  k = i ;
  for ( j = i + 1  ; j < stAmount ; j++) {
         
   pvThis  = (char *)pvData + j*stSize;         
              pvThat  = (char *)pvData + k*stSize;
   if (CompareFun(pvThis  , pvThat  ) > 0) {
            
    k = j ;
   
   }
   
   if( k != i ) {
   
    pvThis  = (char *)pvData + i*stSize;         
                  pvThat  = (char *)pvData + k*stSize;                  
   
    __swap(pvThis  , pvThat  , stSize);
   
   }
   
  }
  
}
}

其实这个选择排序函数和冒泡排序函数只是改动了内部程序,其他地方都没有修改。道理是一样,我就不加说明。
触类旁通的思想真的很重要,当你庖丁解牛对待一个冒泡排序的时候,你会发现其他排序方法也就自然而然会了。
我们看看测试结果:
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
先测试一些数据,便于我们理解
第一组数据:
sizeof(iArray) = 36
sizeof(iArray[0]) = 4
&iArray = 0x402000
&iArray[0] = 0x402000
(char*)&iArray[0] + sizeof(iArray[0]) = 0x402004
&iArray[1] = 0x402004
&iArray[8] = 0x402020
第二组数据:
sizeof(strArray) = 20
sizeof(strArray[0]) = 4
strArray = 0x402024
&strArray = 0x402024
&strArray[0] = 0x402024
(char*)&strArray[0] + sizeof(strArray[0]) = 0x402028
&strArray[1] = 0x402028
strArray[0] = 0x403000
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
整型数组数据排序之前:
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'
请按任意键继续. . .

测试通过!

1 1回复

举报

zzq宁静致远 2013-8-2 08:31:57
第十一节   机试题之数据编码
某部队为了防止消息泄密从而对原始数据进行编码,编码规则如下。
1)        所有信息都为ASCII 编码;
2)        在收到原始密文后将字符进行二进制逆转,如字符'A'(0x41,0100 0001B)将数据逆转后为(0x82,1000 0010B);
3)        将逆转后的数据按照16 进制打印输出(原始数据允许空格),如字符串"ABCD EFGH"加密后的输出结果为:"8242C22208A262E212"。
为了加快编码解码速度现在需要你编写一个程序实现该密文的编码。
这个题目说到底就是将一个字符转化成二进制,再将这个二进制的高低位逆转,之后输出逆变后对应数据的ASCII。
二进制高低位逆转在12.4有详细讲解,为了算法不重复,这里采用逐位逆转方法进行解答。
// text.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <string.h>
/****************************************************************
**  函数名称:  Printb
**  函数功能:  输入一个数据,将其二进制反位输出
**  入口参数:  uiValue:待转换的值
**  出口参数:   None
**  返回值    uiSum 转换之后的值
****************************************************************/
unsigned int Printb( unsigned int uiValue)
{
    unsigned int uiSum = 0 ;
    /* 将数据的二进制逆位 */
    for ( int i = 0 ; i < 8 ; i++ )
{
       uiSum = ( uiSum << 1 ) + ( uiValue & 0x01 ) ;
       uiValue = ( uiValue >> 1 ) ;
    }
    return uiSum ;
}
/****************************************************************
**  函数名称:  main
**  函数功能:  主函数
**  入口参数:  argc* argv[]
**  出口参数:  none
**  返回值    0
****************************************************************/
int main(int argc, char* argv[])
{
    char s[100] ;
    unsigned int iArray[100] ;
    scanf( "%s" , s ) ;
    /* 将字符串转化为整型数据 */
    for ( int i = 0 ; i < strlen(s) ; i++ )
    {
       iArray = s ;
    }
    /* 以十六进制输出字符串数据 */
    for ( int j = 0 ; j < strlen(s) ; j++ )
    {
       printf( "%x" , iArray[j] ) ;
    }
    printf( "\n" ) ;
    /* 以十六进制输出译码后的字符串数据 */
    for ( int k = 0 ; k < strlen(s) ; k++ )
    {
       printf( "%x" , Printb( iArray[k] ) );
    }
    printf("\n") ;
    return 0;
}
编译结果:
ABCDEFGH
4142434445464748
8242c222a262e212
请按任意键继续. . .

回复

举报

zzq宁静致远 2013-8-2 14:24:59
第十二节  机试题目之十进制1~N的所有整数中出现“1”的个数

给定一个十进制数N,写下从1开始到N的所有整数,然后数一下其中出现的所有“1”的个数,比如:
1)   N = 2 ,写下1、2,这样只出现1个“1”;
2)   N= 12 ,我们会写下1、2、3、4、5、6、7、8、9、10、11、12,这样1的个数为5。
问题是:写一个函数f(N),返回1~N之间出现“1”的个数,比如:f(12) = 5。
这个题目带有几分找规律性质。本题解法可能较多,这里提供两种。
方法一:
// text.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
/****************************************************************
** Function name      oneNumber
** Descriptions       计算1的个数
** input parameters  uliNumber:输入的数据,即为要计算1的个数的数据
** output parameters void
** Returned value     uliCount1的个数
****************************************************************/
unsigned long int oneNumber ( unsigned long int uliNumber )
{
    /*  uliNumber传进来的值赋给uliTally */
    unsigned long int uliTally           = uliNumber ;
    /*  记录1的个数 */
    unsigned long int uliCount           = 0 ;   
    /*  提取位的权值 */
    unsigned long int uliFlag            = 1 ;
    /*  提取位 */
    unsigned int      uiFlag              = 0 ;
    /*  提取位的幂次方 */
    unsigned int      uiLog               = 0 ;
    /*
     *对数据逐位取提取位
     */
while (  ( uliTally / uliFlag )  != 0 )
{
       /* 从左开始计算,依次取出uiFlag*10^n */
       uiFlag   = ( uliTally / uliFlag ) % 10  ;
       /* 如果是 1 * 10^n ,则按1*10^n公式进行计算 */
       if ( 1 == uiFlag )
{
           uliCount += uiLog * ((unsigned long int)( uliFlag / 10)) + 1 ;
           /* 加上10^n后面数据 */
           uliCount += ( uliNumber % uliFlag  ) ;
       }  
/*
*如果是 uiFlag * 10^n ,则按uiFlag*10^n公式进行计算
*/
else
{
           /*( uliFlag ) * ( 1&&uiFlag )是为了uliFlag0,则是加0 */
           uliCount += ( uliFlag ) * ( 1&&uiFlag ) +
            uiFlag * uiLog * ( (unsigned long int)( uliFlag / 10) ) ;
       }
       /* 依次向左取 */
       uliFlag *= 10 ;
       /* uiLog = log10(uliFlag) */
       uiLog   += 1 ;
    }
    /* 返回1的个数 */
    return uliCount ;
}
/****************************************************************
** Function name      main
** Descriptions       输入输出
** input parameters   argc argv[]
** output parameters  void
** Returned value     0
****************************************************************/
int main(int argc, char* argv[])
{
    unsigned long int iNumber = 0 ;
    unsigned long int uliCountNumber = 0 ;
    printf("请输入iNumber:  ") ;
    scanf("%ld" , &iNumber) ;
    uliCountNumber =  oneNumber(iNumber) ;
    printf( "1~%d1的个数为: %d\n" , iNumber , uliCountNumber ) ;
    return 0;
}
函数头文件这里使用的是英语,但是格式是不变的。
编译结果:
请输入iNumber:  100
1~1001的个数为: 21
请按任意键继续. . .
方法二:
// text.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
/****************************************************************
** Function name      oneNumber
** Descriptions       计算1的个数
** input parameters   uliNumber:输入的数据,即为要计算1的个数的数据
** output parameters  void
** Returned value     uliCount1的个数
****************************************************************/
unsigned long int oneNumber ( unsigned long int uliNumber )
{
    /*
    **  记录1的个数
    */
    unsigned long int uliCount           = 0 ;   
    /*
    **  提取位左边的数
    */
    unsigned long int uliLeft            = 0 ;
    /*
    **  提取位右边的数
    */
    unsigned long int uliRight           = 0 ;
    /*
    **  提取位,此位对1进行计数
    */
    unsigned long int uliFlag            = 0 ;
    /*
    **  提取位的权值
    */
    unsigned int      uiFlag             = 1 ;
    /*
    ** 对数据逐位取提取位
    */
    while ( (uliNumber / uiFlag)!=0 )
{
       /*
       ** 提取位右边的数
       */
       uliRight = uliNumber % uiFlag          ;
       /*
       ** 提取位
       */
       uliFlag  = ( uliNumber / uiFlag ) % 10 ;
       /*
       ** 提取位左边的数
       */
       uliLeft  = ( uliNumber / uiFlag ) / 10 ;
       /*
       ** 判断提取位,分成01和大于等于2这三种情况
       */
       switch ( uliFlag )
{
        
       /*
       ** 如果提取位为0,那么1的个数等于提取位左边数乘以取提取位的数据
       */
       case 0  :
{
           uliCount += uliLeft * uiFlag ;
       } break ;
       /*
       ** 如果提取位为1,那么1的个数等于提取位左边数乘以取提取位的数据
       ** 再加上(0到提取位右边数)+1
       */
       case 1  :
{
           uliCount += uliLeft * uiFlag + uliRight + 1 ;
       } break;
       /*
       ** 如果提取位大于1,那么1的个数等于(提取位左边数+1)乘以
       ** 取提取位的数据
       */
       default :
{
           uliCount += ( uliLeft + 1 ) * uiFlag ;
       } break;
       }
       /*
       ** 提取位向左移动一位
       */
       uiFlag *= 10 ;
    }
    /*
    **  返回1的个数
    */
    return uliCount ;
}
/****************************************************************
** Function name      main
** Descriptions       输入输出
** input parameters   argc argv[]
** output parameters  void
** Returned value     0
****************************************************************/
int main(int argc, char* argv[])
{
    unsigned long int iNumber = 0 ;
    unsigned long int uliCountNumber = 0 ;
    printf("请输入iNumber:  ") ;
    scanf("%ld" , &iNumber) ;
    uliCountNumber =  oneNumber(iNumber) ;
    printf( "1~%d1的个数为: %d\n" , iNumber , uliCountNumber ) ;
    return 0;
}

回复

举报

zzq宁静致远 2013-8-2 15:15:45
林晓强 发表于 2013-7-31 14:13
你好,我正在学习c语言,能不能发一份到我的邮箱里,谢谢了!

您可以在华为网盘:http://dl.vmall.com/c0whhxnqyd下载我整理的相关文档,或者加入QQ群;284013595进行下载。我就不一一发送了。
回复

举报

zzq宁静致远 2013-8-2 15:17:12
yuyang8425 发表于 2013-7-31 14:15
发一份给我好吗?,谢谢!

您可以在华为网盘:http://dl.vmall.com/c0whhxnqyd下载我整理的相关文档,或者加入QQ群;284013595进行下载。我就不一一发送了。
回复

举报

zzq宁静致远 2013-8-2 15:18:30

您可以在华为网盘:http://dl.vmall.com/c0whhxnqyd下载我整理的相关文档,或者加入QQ群;284013595进行下载。我就不一一发送了。
回复

举报

zzq宁静致远 2013-8-2 15:19:31
yongchao5453 发表于 2013-7-31 17:19
能发一份么 正学c语言呢  万分赶集

您可以在华为网盘:http://dl.vmall.com/c0whhxnqyd下载我整理的相关文档,或者加入QQ群;284013595进行下载。我就不一一发送了。
回复

举报

xu889 2013-8-2 15:47:43
1511928461@qq.com     这是我的邮箱,可以发一份给我吗,谢谢
回复

举报

kangte 2013-8-3 21:14:00
{:1:}赞一个
回复

举报

dongyumin 2013-8-3 21:32:16
楼主非常善于思考、善于整理啊。厉害厉害。精华一个。
回复

举报

胡海松 2013-8-3 22:47:17
最难能可贵的是楼主无私的把这些经验和资料共享,谢谢。。
回复

举报

Hoiloun 2013-8-3 22:51:40
长见识了。
回复

举报

A670521546 2013-8-4 01:48:41
LZ继续连载下去,能像鸿哥弄一个一系列
回复

举报

苏磊阿累 2013-8-4 07:45:17
很好的东西。。。。学习。。。
回复

举报

全丽杰 2013-8-4 08:31:29
学习中 正在看呢  
回复

举报

zzq宁静致远 2013-8-4 12:44:04
第十三节  机试题之  遍历单链表一次,找出链表中间元素
单链表最大的特点就是“不走回头路”,不能实现随机存取。如果我们想要找一个数组a的中间元素,直接a[len/2]就可以了,但是链表不行,因为只有a[len/2 - 1] 知道a[len/2],其节点不知道。因此,如果按照数组的做法依样画葫芦,要找到链表的中点,我们需要做两步(1)知道链表有多长(2)从头结点开始顺序遍历到链表长度的一半的位置。这就需要1.5n(n为链表的长度)的时间复杂度了。有没有更好的办法呢?答案是有的。想法很简单:两个人赛跑,如果A的速度是B的两倍的话,当A到终点的时候,B应该刚到中点。这只需要遍历一遍链表就行了,还不用计算链表的长度。
下面这个程序算法就是只遍历单链表一次,即能找出链表中间元素。
typedef struct _list_node {
    int         iData;
    _list_node *next;
}ListNode;
ListNode *FindList(ListNode *head)
{
    ListNode *p1, *p2;
    if ( (NULL == head) || (NULL == head->next) )
    {
       return head;
    }
    p1 = head;
    p2 = head;
   
while (1)
    {
       if ( (NULL != p2->next) && (NULL != p2->next->next) )
       {
           p2 = p2->next->next;
           p1 = p1->next;
       }
       else
       {
           break;
       }
    }
    return p1;
}

回复

举报

摩卡1655 2013-8-4 22:50:56
{:3:}{:3:}{:3:}{:3:}
回复

举报

zzq宁静致远 2013-8-5 09:11:30
第十四节  机试题之全排序
写一个程序,对任意一串字符串进行全排序。如123的全排序为:123,132,213,231,312,321.
这个题目使用数学很简单,因为高中的排列组合一个式子就把这个题目给KO了,C语言其实也很简单,无非是列举出所有排列顺序罢了。
// text.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
//读者在这里思考两个问题:
//1.这个函数的作用是什么?
//2.inline是做什么用的?
inline void Swap(char *a, char *b)
{
    /* 交换ab */
    char temp = *a;
    *a = *b;
    *b = temp;
}
//读者想想如何列举出所有的排列顺序
void Perm(char list[], int k, int m)
{
    /* 生成list [k:的所有排列方式 */
    int i = 0;
    /* 输出一个排列方式 */
    if (k == m)
    {
       for (i = 0; i <= m; i++)
       {
           putchar(list);
       }
       putchar('\n');
    }
    else
    {
       /* list[k:有多个排列方式 */
       /* 递归地产生这些排列方式 */
       for (i = k; i <= m; i++)
       {
           Swap (&list[k], &list);
           Perm (list, k+1, m);
           Swap (&list [k], &list );
       }
    }
}
//测试
int main(int argc, char* argv[])
{
   
    char s[] = "1234";
    Perm(s, 0, 3);
    return 0;
}
编译结果:
1234
1243
1324
1342
1432
1423
2134
2143
2314
2341
2431
2413
3214
3241
3124
3142
3412
3421
4231
4213
4321
4312
4132
4123
请按任意键继续. . .

回复

举报

斗魂 2013-8-5 16:09:17
zan........................
回复

举报

评论

高级模式
您需要登录后才可以回帖 登录 | 注册

发经验
课程
    关闭

    站长推荐 上一条 /10 下一条

    快速回复 返回顶部 返回列表