原文网址:http://bbs.elecfans.com/jishu_354666_1_1.html
再过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
1.1 #include< >与#include" "
1.2 switch()
1.3 const
1.4 #ifndef/#define/#endif
1.5 全局变量和局部变量
2.1 变量的声明与定义
图2. 1 数据存储方式
|
图2. 3 指针加1取字符型数据
|
图2. 2 指针加1取整型数据
|
3.1 N!结果中0的个数
第四节 关键字、运算符与语句 1.1 static 1. 如程序清单4. 1所示,请问输出i、j的结果?
程序清单4. 1 static
#include <stdio.h>
static int j ;
void fun1(void)
{
static int i = 0 ;
i++ ;
printf("i = %d " , i );
}
void fun2(void)
{
j = 0 ;
j++ ;
printf("j = %d
" , j );
}
int main(int argc, char *argv[])
{
int k = 0 ;
for( k = 0 ; k < 10 ; k++ ){
fun1() ;
fun2() ;
printf("
");
}
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. 如程序清单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
" , i) ;
}
return 0;
}
这个题目主要考察对for循环的理解。我们先来看看运行程序会输出什么?
First = 0 Second = 0 Fourth = 0
Third = 1 Second = 1 Fourth = 1
Third = 2 Second = 2 Fourth = 2
Third = 3 Second = 3 Fourth = 3
Third = 4 Second = 4 Fourth = 4
Third = 5 Second = 5 Fourth = 5
Third = 6 Second = 6 Fourth = 6
Third = 7 Second = 7 Fourth = 7
Third = 8 Second = 8 Fourth = 8
Third = 9 Second = 9 Fourth = 9
Third = 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. 如程序清单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
" , sizeof( a ) ) ;
printf( "sizeof(b) = %d
" , sizeof( b ) ) ;
return 0;
}
这个题目比较简单,由于char 是1个字节、short是2个字节,所以本题答案是:
sizeof(a) = 6
sizeof(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
" , sizeof( a ) ) ;
printf( "sizeof(b) = %d
" , sizeof( b ) ) ;
return 0;
}
是数组指针呢,还是指针数组呢?这里涉及*和[]和优先级的问题。我告诉大家的是这两个数组存放的都是指针变量,至于为什么,在后续章节会详细解释,然而指针变量所占的字节数为4字节,所以答案:
sizeof(a) = 24
sizeof(b) = 24
请按任意键继续. . .
1. 或许大家都知道,++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
" , iNumber ) ;
这里输出是:4!!!4 = 1+1+2。
int i = 0 ;
int iNumber = 0 ;
iNumber = (++i) + (i++) + (++i) ;
printf( "iNumber = %d
" , iNumber ) ;
这里输出是:4!!!4=1+1+2。
int i = 0 ;
int iNumber = 0 ;
iNumber = (++i) + (++i) + (i++) ;
printf( "iNumber = %d
" , iNumber ) ;
这里输出是:6!!!6=2+2+2。
这里至少能说明两个问题,其一,先执行前面两个,再执行第三个;其二,(i++)这个i的自加是最后执行!
int i = 0 ;
int iNumber = 0 ;
iNumber = (++i) + (i++) + (++i) + (++i) + (i++) ;
printf( "iNumber = %d
" , iNumber ) ;
这个会是多少?!答案是:10!!!10=1+1+2+3+3!
不同的编译器或许会存在不同的答案,希望读者自行进行验证。
1. 如程序清单4. 5所示,运行程序,当显示Enter Dividend: , 输入的是a,按下Enter之后程序会怎么运行?
程序清单4. 5 scanf()函数的输入
#include<stdio.h>
int main(void)
{
float fDividend,fDivisor,fResult;
printf("Enter Dividend:");
scanf("%f",&fDividend);
printf("Enter Divisor:");
scanf("%f",&fDivisor);
fResult=fDividend/fDivisor;
printf("Result is: %f
",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 , fResult ;
int iRet ;
char cTmp1[ 256 ] ;
printf( "Enter Dividend
" ) ;
iRet = scanf( "%f" , &fDividend ) ;
if ( 1 == iRet )
{
printf( "Enter Divisor
" ) ;
iRet = scanf( "%f" , &fDivisor ) ;
if ( 1== iRet )
{
fResult = fDividend / fDivisor ;
printf( "Result is %f
" , fResult ) ;
}
else
{
printf( "Input error ,not a number!
" ) ;
gets(cTmp1) ;
return 1 ;
}
}
else
{
printf( "Input error , not a number!
" ) ;
gets(cTmp1) ;
return 1 ;
}
return 0 ;
}
1. 如程序清单4. 6所示,请问输出会是什么?
程序清单4. 6 scanf()函数的返回值
int a , b ;
printf( "%d
" , scanf("%d%d" , &a , &b) ) ;
输出输入这个函数的返回值?!答案是:2。只要你合法输入,不管你输入什么,输出都是2。那么我们就要深入解析scanf这个函数。scanf()的返回值是成功赋值的变量数量。
1. 阅读如程序清单4. 7所示,想想会输出什么?为什么?
程序清单4. 7 const作用下的变量
const int iNumber = 10 ;
printf(" iNumber = %d
" , iNumber) ;
int *ptr = (int *)(&iNumber) ;
*ptr = 100 ;
printf(" iNumber = %d
" , iNumber) ;
const的作用在第四章已经详细讲了,这里就不再累赘,答案是:10,10。这里补充一个知识点:
const int *p 指针变量p可变,而p指向的数据元素不能变
int* const p 指针变量p不可变,而p指向的数据元素可变
const int* const p 指针变量p不可变,而p指向的数据元素亦不能变
1. 如程序清单4. 8所示程序,输出什么?
程序清单4. 8 *ptr++
int iArray[3] = { 1 , 11 , 22} ;
int *ptr = iArray ;
printf( "*ptr++ = %d
" , *ptr++ ) ;
printf( "*ptr = %d
" , *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. 9所示程序,输出会是什么?
程序清单4. 9 *++ptr
int iArray[3] = { 1 , 11 , 22} ;
int *ptr = iArray ;
printf( "*++ptr = %d
" , *++ptr ) ;
printf( "*ptr = %d
" , *ptr ) ;
这个解释和上面解释差不多,就是++ptr和ptr++的差别,所以这里的两个输出都是:11。同样的道理,*++ptr和*(++ptr)是等效。
再如程序清单4. 10所示,输出又会是什么?
程序清单4. 10 (*ptr)++
int iArray[3] = { 1 , 11 , 22} ;
int *ptr = iArray ;
printf( "(*ptr)++ = %d
" , (*ptr)++ ) ;
printf( "*ptr = %d
" , *ptr ) ;
这个的输出是:1,2。原因请读者分析。
|
-
图4.1 ptr++
1.1 “零值”比较
1.1 数组、数组元素、指针的大小
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);
1.1 结构体内存对齐问题
1.1 结构体在STM32的应用
1.1 结构体在联合体中的声明
|
1.1 malloc
1.1 strcpy