写在开始:电子信息 901c语言程序设计 学习内容及问题解决记录
目录
一、变量、常量及标准化输入输出
1.浮点数输入输出
--输入scanf:
①double类型读入必须用%lf,否则无法进行。
②浮点数读入时,格式规定不能要求小数点后位数。
--输出 printf
①小数点形式输出 和 科学计数法输出,默认为小数点后六位,分别为****.123456 和 1.123456e+12,根据编译器不同,科学计数法也可能为1.123456e+123
②在对位数进行要求时,小数点、e、+ 、(+)后的数字 均算入位数。假设要求%m.nf 或 %m.ne,首先处理小数位数要求,科学计数法记得整数部分只能1位(1<=m<2),接下来按照格式补充e+12等位。此时,如果总位数 大于 m,则正常输出,不需要裁剪,若总位数小于m,则默认右对齐,左侧空格。
若有左对齐(-)要求,则左对齐,右侧空格;若需要补0,则在左侧空格处补0,左对齐则无法补,右侧仍空格。
空格、补0和符号:若要求显示正负号(+),符号应在空格后,若是补0情形,符号应在0前。
2.字符串格式化输出
形如:printf("%10.03s","****");
整数位:代表输出长度,若所截断长超过整数位,全部输出,不足则左侧补空格
小数位:代表从第0位开始,所截字符串长度
printf("%.4s\n","1234567890");
printf("%5.4s\n","1234567890");
结果:
1234
1234
3.整数
--2.1两整数相除,向下取整(结果只取整数部分,而非四舍五入)。
--2.2 int类型强制转换,向下取整(结果只取整数部分,而非四舍五入)。
二、运算表达式及优先级
1.&& > || 以及逻辑运算短路
int a,b,c;
a=b=c=1;
++a||++b&&++c;
结果:a=2,b=1,c=1
分析:在逻辑运算中:!> && >||,可以理解为,在本题中,没有括号所以要按照优先级进行运算。&&优先级较高,所以先括起来,=》++a||(++b&&++c),此时,后进行运算的 || ++a=2,为+(逻辑值等于1),所以发生了短路,故本该先运算的&&以及其两侧,因为短路被省掉了。
短路现象中,通常是,本该先**再##,但短路发生后,导致**省略(可怜的优先级被忽视了)
2.a?b:c
int x=1,y=0;
printf("%d\n",x-->(y+x)?5:25>y++?'1':'2');
printf("x=%d,y=%d\n",x,y);
结果
5
x=0,y=0
分析:根据结果,以及个人调整代码数据发现,第一个y=0,第2个y=0,第一个x=1,第2个x=0。在条件运算符中,结合方向位从右向左。但是明显知道,所谓优先级低的左侧,其实未受右侧y++影响。
个人理解:优先级《=》在没有括号的时候,分析时可以自动加括号,然后就可以从左向右分析。考虑到++是一目运算符,>是二目运算符,高于三目运算符(条件运算符),所以变成了
x-->(y+x)?5:((25>y++)?'1':'2')
然后从左到右进行计算,发现走了5那一条,所以后面的不影响结果(相当于短路?)。
如果把括号里改成 y+x+1,则导致走 路2,然后先看括号里,y=0,25>0,y++(变成y=1),所以走路1,结果为‘1’,对应49 对应结果:49 x=0,y=1,与分析一致。
关于第2个y的值,通过调整25,变成 1时,结果仍为49,但变成0时,结果为50。故第2个y在能抵达的情况下,值为0。理解没有问题。
P.S.将此处优先级理解带入 逻辑运算符短路中,如上 1.&& > || 以及逻辑运算短路 中,++a||++b&&++c 可以变成 (++a) || ( (++b) && (++c) ),然后从左侧看,括号1,或运算,若括号1为真,则不需要看右边,若为假,则判断右侧括号,需要先看++b,若++b为假,则不需要右边,若为真,则看++c。这样理解可以通过。
总结:如红字。
3.k++
问题1:关于 int k=10; printf("%d\n",(k++)+(k++));结果输出。教材1 P34 认为,输出结果为10+10=20,k最终值为10+2=12。本人运行结果为 10+11=21.(参见下 问题2 尝试解释)
理解:教材2 P102指出,两种方案均可行,但准确来说,C语言标准并未定义结果应该是什么(与所使用编译器有关)。在P105 <副作用和序列点> part中表示,C语言保证程序执行下一条语句前,对 k递增两次,但未指明在本句中,是在子表达式求值后递增,还是所有表达式求值后再递增。
问题2:3+(k++)
在 自增/自减 外有括号时,并不影响其作用,如本问题,结果仍为 3+k,然后执行 k++。
-》在指针中 *(str++),同理,即:先取指针str所指值,然后指针向后移一位。
问题3:在printf中,存在以下状况:int u=3;
printf("%d,%d\n",u,u); // 3,3
printf("%d,%d\n",u++,u); // 3,4
printf("%d,%d\n",u,u++); // 4,3
printf("%d,%d\n",++u,u); // 4,4
printf("%d,%d\n",u,++u); // 4,4
printf("%d,%d\n",++u,u++); // 5,3
printf("%d,%d,%d\n",u,u++,++u); // 5,4,5
printf("%d\t%d\t%d\t%d\t%d\t",u++,u,u++,++u,++u);//6 7 5 7 7
尝试性总结:
1.u,++u 的值总是变化之后的,无论其位置,均为递增最后的u
2.u++ 的值 从左往右递减,依次为(设最终u=n):n-1, n-2, n-3 ...
尝试性解释:
无,据说与编译器有关,其实我也没想出个解释
浑说一个:u++其实相当于 u+1,输出 (u+1)-1,理解为退一。编译器用变量 m=0 记录u++数目,从左往右先读一边,遇到 ++(无论++u或u++),就m=m+1,然后把结果的m,若是u++,就告诉这个u++,记住啊你该退这个m位,遇到 ++u,就跟他说,你就是最终结果啊记住,遇到 u就说,你不就是u吗,最终结果也是u,那你就偷个懒,最终结果就是你了。所以。。。
我啥也不明白,如果再加上 - -,我可以表演一个劈叉
似乎也可以用,如下例子:(我简直是天才谢谢)
printf("%d\t%d\t%d\t%d\t%d\t",u++,u--,u--,u++,++u);//3 4 5 4 4
此处用m,退一 为-1,增一为+1,且会累计;
u++ | u-- | u-- | u++ | ++u | |
m变化 | -1 | +1 | -1 | +1 | -1 |
赋予m值 | -1 | 0 | 1 | 0 | -1 |
结果 | 3=4+(-1) | 4=4+(0) | 5=4+(1) | 4=4+(0) | 4 |
printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\n",++u,++u,u,u++,u++,++u,++u); //9 9 9 6 5 9 9
++u | ++u | u | u++ | u++ | ++u | ++u | |
m变化 | -1 | -1 | 0 | -1 | -1 | -1 | -1 |
赋予m值 | -1 | -2 | -2 | -3 | -4 | -5 | -6 |
结果 | 9 | 9 | 9 | 6=9+(-3) | 5=9+(-4) | 9 | 9 |
经printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",++u,++u,u,u++,u--,++u,++u,u++);//8 8 8 5 6 8 8 3验证可行
4.位运算符
运算符 | 含义 | 运算规则 | 示例 |
~ | 按位取反 | 1变0,0变1 | ~1001=0110 |
& | 按位与 | 同1则1,有0为0 | 1&0=0 |
| | 按位或 | 同0则0,有1为1 | 0011 | 1001 = 1011 |
^ | 按位异或 | 同则为0,不同为1 | 0011^1001=0001 |
三、循环结构
1.while(printf()"*")
printf 函数的返回值是输出字符的个数。
题目:下面程序的循环控制表达式是否与‘0’等价
int t=0;
while(printf("*"))
{
t++;
if(t>3) break;
}
解释:printf 输出1 个字符,返回值为1,其在while中,对应逻辑值,为1。’0‘对应的ASCII值为80≠0,故其对应的逻辑值,也为1,所以等价。此外,还与 ’1‘,’2‘...等价。
2.continue & break
--switch语句
continue:实际作用于循环语句,而非控制switch;本轮switch到此结束,然后移到switch,改变循环变量后再选择一次。
break:作用于switch,结束所在switch过程
3.
四、数组与字符串
1.数组
1.1- 10*0正确与否
数组定义:int a[10]={10*0}; 是正确的,相当于 int a[10]={0};
1.2- char s[5]=""; 正确与否
正确。该 s 相当于 {'\0','\0', '\0','\0', '\0'}
1.3- 错误代码段修正。
//错误代码
char a[6],b[]="China";
a=b;
printf("%s\n",a);
//修正后代码
char b[]="China";
char *a=b;
printf("%s\n",a);
//
int i;
for(i=0;i<9;i++){
printf("%c:%d\n",a[i],a[i]);
}
修正后代码结果:
China
C:67
h:104
i:105
n:110
a:97
:0
:0
:0
+:43
总结:c语言的巧妙在于指针的灵活运用,指针可以作数组理解,数组名也可以作指针理解。此处代码,将数组名作为指针,因此指针指向的内存区域,大小等于数组一位的大小,指针加一就等于数组的下标加一。指针问题最需要弄清两个问题:1是指针指向哪里 2是指针指向的是多大的内存区域(这样指针在移动的时候能明确移动的位置大小,或者读取的时候才知道要读多长)
2.字符串函数
总结:1.字符串名代表其首项地址,字符串函数所处理的是 从字符串首项 到 第一个\0 的字符段,如 puts,strlen,strlwr,strupr
2.c语言的字符串,代表的是从首项地址开始,对 此及后面的存储空间 按照数据类型划分小格,所以不存在溢出,使用printf可以限制,使用puts可以清楚看到 超出规定长度 的完整字符串,如strcat,strcpy。
3.在连接strcat中,会删去前一个字符串的\0,连接后在最后安上 \0。在复制粘贴strcpy中,会将新字符串粘进去,后接\0,若原字符串更长,后面会存在未影响部分。
4.影响了就是影响了,没影响的地方就是保留原样。影响字符串后接上\0,只使用puts就啥也看不到了(粉饰太平hhhh)。
2.1- gets & scanf
①gets:格式 gets(char[ ]),形如 gets(s1); 每次只能从终端获取一个字符串。以回车作为结束。
②scanf::格式scanf("%s%s",char[ ],char[ ]),形如scanf("%s%s",s1,s2);每次可以从终端获取一个或多个字符串,串之间以空格结束(测试时 回车也可以)。(不用加&,因为数组名称天生代表地址)
③scanf 后面接 gets:如
aa bb <CR>cc dd<CR>
scanf("%s%s",a1,a2);
gets(a3);gets(a4);
其中 scanf 依靠空格和回车划分,所以 a1=”aa“,a2="bb",gets从scanf结束开始,到后面第一个回车结束,截取不含回车的一部分,故a3=”“,若bb后有空格,则a3=”□“,a4从a3后继续,到下一个回车结束,截取不含回车部分,a4=”cc dd“ 。
上述逻辑经验证可行。若先用gets,且输入回车,则与a3同样情况,得到空数组。
2.2- puts & printf
①puts
char s1[]="ab\0ef\0";
int i;
for(i=0;i<6;i++){
printf("%d\t",s1[i]);
}
printf("\n");
puts(s1);
puts(s1);
结果:
97 98 0 101 102 0
ab
ab
总结:1.给字符数组赋值时,从 数组名所代表的地址 开始依次 把字符串所有字符赋入,
2.使用for循环输出,实际上不会考虑数组实际长度,不会报溢出错误,所以 ’\0' 不会影响继续输出。
3.使用puts,即从 数组名所代表的地址 开始,到 '\0' 止
4.puts会自动换行。无论后面还有没有输出,光标都会下移一行,若此后无输出,将会出现一行空行。
②printf
printf("%s",s1),效果与 puts 同
for循环结合printf("%c",s1[i]); 效果受for循环上限限制
2.3- strcat
将字符数组2中的字符串连接到字符数组1的字符串后面
int i;
char s1[20]="I want to ";//len=10
char s2[]="study ";//len=6
strcat(s1,s2);
for(i=0;i<16;i++){//s1效果
printf("%c",s1[i]);
}
printf("\n");
for(i=0;i<10;i++){//s2是否存在
printf("%c",s2[i]);
}
printf("\n");
strcat(s1,s2);
for(i=0;i<20;i++){//s1溢出效果
printf("%c",s1[i]);
}
for(i=0;i<25;i++){//s1溢出效果
printf("%c",s1[i]);
}
printf("\n");
puts(s1);
结果:(乱码不要在意,那是没定义的地方)
I want to study
study Y%
I want to study stud
I want to study study
I want to study study
总结:1.相当于将 字符数组2 复制到 字符数组1末尾,对2无影响。(如结果 行1,2)
2.c语言中,数组长度并不是限制。所以s1不够大时,仍可以将整个s2复制进去,但用for打印会受限,用puts打印时,就可以看到完整的s2了。(如结果 行3,4,5)
2.4- strcpy
将字符串2复制到字符数组1。
int i;
char s1[20]="little";//len=10
char s2[]="tend";
char s3[]="dictionary";
strcpy(s1,s2);
for(i=0;i<15;i++){//短字符串 复制
printf("%c",s1[i]);
}
printf("\n");
printf("s1[4]=%d\n",s1[4]);
strcpy(s1,s3);
for(i=0;i<15;i++){//长字符串 复制
printf("%c",s1[i]);
}
s2[2]='\0';//s2中有\0
strcpy(s1,s2);
for(i=0;i<15;i++){//字符串中有\0
printf("%c",s1[i]);
}
结果:
tend e
s1[4]=0
tend
dictionary
dictionary
te tionary
总结:1.用 比原字符串短 的字符串,则结果中,短字符串从开始依次替换对应位,以\0结束(若使用puts,则只显示短字符串)。若有未被替换的原字符串字符,不做处理。(如结果 行1,2,3)
2.原字符串中有没有\0不影响复制进去,也许会影响puts。s2中若有\0,会影响复制的部分(即会复制 开头至\0,然后正常粘贴进去)(如结果 行6)。
3.用 更长的字符串,则完全替换成新字符串。(如结果 行4,5)
规则:
复制和粘贴两步。
复制部分,从数组名对应的第一项位置开始,到\0结束。
粘贴部分,从数组2第一项开始,将复制部分 依次逐位 粘贴进去。
若比之前长,就相当于完全替换,若较之前短,则会出现 xxxx\0xxx\0情况,使用printf时取决于所定上限,使用puts时输出 新粘贴部分。
2.5- strlen
测试字符串长度。
int i;
char s1[]="ab\0ef\0";
char s2[10]="asdf";
char s3[10]={'2','3',};
printf("%d,%d,%d\n",strlen(s1),strlen(s2),strlen(s3));
结果:2,4,2
总结:从输入的数组开始,到\0结束,测算有效字符的长度,具体字符串 如s1 占几位,并不重要。
2.6- strcmp
char a[]="STOP",b[]="STOP ";
printf("%d\n",strcmp(a,b));
结果:1
总结:逐位比较,若出现不同字符,则比较字符的大小(ASCII码);若全部相同,则认为相等;若一个结束,另一个未结束(如上例),ASCII值最小为0,对应 '\0',所以先结束的小。
2.7- strupr
int i;
char s1[]="ab\0ef\0";
strupr(s1);
for(i=0;i<6;i++){//字符串中有\0
printf("%c",s1[i]);
}
结果:AB ef
总结:只处理 从第一项s1[0]位置至\0段的有效字符。
※3.排序算法
选择排序,冒泡排序,插入排序。
3.1- 选择排序
原理解释:
利用每次 遍历 i 位到最后,找到最小位,如果最小位不是 i 位,则交换两位的值。
代码展示:(从大到小排序)
(1)数组做形参(用 int i , j 遍历)
void sort(int a[],int n)
{
int mark,temp,i,j;
for(i=0;i<n-1;i++)
{
mark=i;
for(j=i+1;j<n;j++)
{
if(a[mark]<a[j]) mark=j;
}
if(i!=mark)
{
temp=a[i];
a[i]=a[mark];
a[mark]=temp;
}
}
}
(2)指针做形参(用指针遍历)
【当然,指针做形参也可以如(1) 用 int i , j 遍历,会更方便,本例供理解】
void sort(int *a,int n)
{
int *mark,*temp,*i,*j; //i,j是指针,移动指针遍历
temp=(int *)malloc(sizeof(int));
for(i=a;i<(a+n-1);i++)
{
mark=i;
for(j=i+1;j<(a+n);j++)
{
if(*mark<*j) mark=j;
}
if(i!=mark)
{
*temp=*i;
*i=*mark;
*mark=*temp;
}
}
}
(3)链表做形参(用指针遍历)
struct node{
int num;
struct node *next;
};
void sort(struct node *head)
{
struct node *mark,*temp,*p,*q;
//mark:指向最小值节点;p:每次要排好序的节点;q:用来和mark结合,进行遍历比较
temp=(struct node *)malloc(sizeof(struct node));
for(p=head;p->next!=NULL;p=p->next)
{
mark=p;
for(q=p->next;q!=NULL;q=q->next)
{
if(mark->num<q->num) mark=q;
}
if(mark!=p)//如果最小值不是p所指节点,则交换两节点内整数值
{
temp->a=p->a;
p->a=mark->a;
mark->a=temp->a;
}
}
}
3.2- 冒泡排序
原理解释:
利用 i 对排好序的位置计数,利用 j 对未排好序的元素,依次比较相邻元素,并将未按顺序的交换顺序,最终将最大/小换到最后一位。
代码展示:
(1)指针做形参 (用 int i , j 遍历)
void sort(int *a,int n){
int i,j,temp
for(i=1;i<n;i++){
for(j=0;j<n-i;j++){
if(*(a+j)>*(a+j+1)){
temp=*(a+j);*(a+j)=*(a+j+1);*(a+j+1)=temp;
}
}
}
}
(2)数组做形参(用 int i , j 遍历)
void sort(float a[],int n){
int i,j;float t;
for(i=1;i<n;i++){
for(j=0;j<n-i;j++){
if(a[j]>a[j+1]){
f=a[j];a[j]=a[j+1];a[j+1]=f;
}
}
}
}
冒泡排序注:
1.只需要换好 n-1 位即可,所以 i 为1~(n-1)
2.由于是 j 与 j+1比较交换,所以第一轮最后一个是 n-2位,所以 j<n-i
3.根据实际排序元素,注意更改 数组/指针 a 的类型
3.2.1-双向冒泡排序
原理解释:
普通版使用 i 和 n-i 标识两端,i<n 标识排序进程,双向冒泡使用 low和high 在两端,low<high标识进度
代码展示:
void BiBubbleSort(int a[],int n)
{
int low=0,high=n-1,flag,i,temp;
while(low<high)
{
flag=0;
for(i=low;i<high;i++)//正向冒泡,从low开始,最后到high-1与high相比较
{
if(a[i]>a[i+1])
{
temp=a[i];a[i]=a[i+1];a[i+1]=temp;
flag=1;
}
}
high--;//正向结束,high上是最大值,high-1
if(flag!=1) break;//遍历一遍,发现没有需要交换,说明排序成功,故结束
for(i=high;i>low;i--)//反向冒泡,从high开始,最后到low与low+1相比较
{
if(a[i]<a[i-1])
{
temp=a[i];a[i]=a[i-1];a[i-1]=temp;
flag=0;
}
}
low++;//正向结束,low上是最小值,low+1
}
}
3.3- 插入排序
原理解释:
逐次对 1~n-1位元素进行插入,使用 temp保存当前要插入值
从i-1位开始判断,将大于的后移,一直到0位或者不再大于当前值,最后在所有大于的元素和小于的元素间空出一位,填入temp。循环停止时,将在目标位之前(不满足条件位),所以需要加一再赋值。
代码展示:
void sort(int *a,int n){
int i,j,temp;
for(i=1;i<n;i++){//从1开始,与前面比较
temp=a[i];
for(j=i-1;j>=0&&a[j]>temp;j--){//如果j位比当前位temp大,则需要后移,小于则停止
a[j+1]=a[j];
}
a[j+1]=temp;//循环停止在小于temp位,j+1位原本大于,已被移走,当前为空
}
}
3.3.1 二分插入排序
与简单插入法相比,调整了寻找插入位置的方法,使用二分查找。减少比较次数。
void halfInsertSort(int a[],int len){
int i,j,temp;
int low,high,mid;
for(i=1;i<len;i++){
temp=a[i];
low=0;high=i-1;
while(low<=high){
mid=(low+high)/2;
if(a[mid]>temp)high=mid-1;
else low=mid+1;
}//其结果为 high (待插入temp) low
for(j=i-1,j>high;j--){
a[j+1]=a[j];
}
a[high+1]=temp;
}
}
//最后移位也可以为
/*
for(j=i-1;j>=low;j--){
a[j+1]=a[j];
}
a[low]=temp
*/
3.4 二分查找法 (折半查找法)
//找到则返回对应下标,未找到返回 0
int fun(int *a,int n,int m){//int数组 a,数组长度 n,待查找数字 m
int low=0,high=n-1,mid;
while(low<=high){
mid=(low+high)/2;
if(a[mid]>m){
high=mid-1;
}
else if(a[mid]<m){
low=mid+1;
}
else{
return mid;
}
}
return -1;
}
五、指针
1.指针与二维数组
规则:(注:此处 描述里的 a,代表 指针或者表达式;表达式里的a,代表二维数组)
① &a,即 取a的地址,取的是将a视作一个整块,其第一个字节的地址,即a的首地址。
②对于 无其他符号(如 *,&)影响的 a:若 a 指向数组,则 a指代 a数组的第一个元素的地址,即相当于 &a[0],a[0]相当于&a[0][0];若 a 指向元素,则 a指代a元素的值,如 a[2][3]。
③*a,与&符号将后面的元素看成整块不同,其是在a的含义基础上进行的取数。即若 a指向数组,则 a指代 数组a第一个元素地址,*a即为 数组a 第一个元素,如,*a 等价于 a[0] ,相当于 二维数组a第0行的数组 的数组名,代表 二维数组a第0行的首地址;对于 *a[0],由前句分析,a[0]是一维数组名,指代数组第一个元素地址,所以*a[0]取该数组的第一个元素,相当于a[0][0];若 a代表元素,则不能使用 *。
&a | 二维数组a的首地址 |
a,&a[0],&(*a), | 二维数组第0行首地址 |
a[0],a[0]+0,*a,*(a+0)+0,&a[0][0], | 二维数组第0行第0个元素地址 |
*(a[0]),*(a[0]+0),*(*a),*(*(a+0)),a[0][0], | 二维数组第0行第0个元素值 |
(注:此处元素代表最小单位 如int型,char型)
五、指针
1.优先级
1.1 *(ptr++) V.S.(*ptr)++
问题1- *(ptr++)
本语句作用为:取ptr指针所指的值,指针ptr+1。以 自减 -- 为例如下:
int x[]={1,3,5,7,9,11},*ptr;
ptr=x;
printf("%d\t",*(ptr--));
ptr=x;
printf("%d\t",*ptr--);
ptr=x+2;
printf("%d\t",*(--ptr));
ptr=x+2;
printf("%d\t",*--ptr);
结果:1 1 3 3
分析,对于自增/自减来说,加不加括号并不影响,不仅是指针,普通运算同样不影响,如 int i=1; 3+(i++)的结果为4 【本处 同 \\二、指针\\3.k++\\问题2】
问题2- (*ptr)++
本语句作用为:取指针 ptr所指的值,然后将ptr所指位上的值 自增,示例如下:
int x[]={1,3,5,7,9,11},*ptr;
ptr=x+2;
printf("%d\t",(*ptr)--);
printf("%d\t",ptr-x);
printf("%d\t",*ptr);
结果:5 2 4
分析:第一个语句,取 x[2]值,并使x[2]=x[2]-1;根据第2个语句,可知,指针未移动;根据第3个语句可知,x[2]减少1
2.字符数组 V.S. 字符指针
前言:一般来说,字符数组名就是字符指针名,通常都能用,但 整体赋值时不同
字符数组 | 字符指针 | |
scanf("%s",str) | √ | × |
str = "asdf" | 初始化√,赋值× | √ |
定义 char *s=“asdf”,字符串s为常量,不能修改
六、函数
1.变量域
书本p109
1.2 "a\0\04r+045\'b"
七、编程题总结
1.素数 isPrime()
int isPrime(int n){
int i,isP=1;
for(i=2;i<=n/2;i++){
if(n%i==0)
{isP=0;break;}
}
return isP;
}
注:为了统一路径return,定义了isP,也可以不用,分两次 返回 0 和 1
2.文件相关操作
2.1文件打开
if(fp=open("text.dat","r")==Null) {
printf("cannot open file!\n");exit(0);
}
2.2 二进制文件读入结构体 - fread(buffer, size ,count, fp)
//本段只读一个
struct rec r;
while(!feof(fp)){//非文件尾判断
fread(&r,sizeof(struct rec),1,fp);
}
2.3 结构体写入二进制文件 - fwrite(buffer,size,count,fp)
//结构体数组 写入文件
//struct student stud[10];
for(i=0;i<n;i++){
if(fwrite(&stud[i],sizeof(struct student),1,fp)!=1){
printf("File-writing error!\n");
}
}
2.4 文本文件读入结构体 - fscanf(fp,"%s%d%c",name,&n,&a)
//本段读入一个
while(feof(fp)==0){
fscanf(fp,"%s%d%c",name,&n,&a);
}
2.5 结构体写入文本文件 - fwrite(fp,"%s%d%c",name,n,a)
//书上未找到,仿写结构体写入二进制
for(i=0;i<n;i++){
if(fprintf(fp,"%s%d%c",name,n,a)!=1){
printf("File-writing error\n");
}
}
2.6文本文件逆序输入(递归方法)
void reverse(void){
char ah2;
if(!feof(file2)&&(ch2=fgetc(file2)!='\n')){//如果file2未到尾,且当前ch2不是'\n'
reverse();
fputc(ch2,file1);
}
}
3.字符串相关操作
3.1子串在母串中出现次数
int count(char *str,char *substr){
int i,j,k,num=0;
for(i=0;i<strlen(str);i++){
for(j=i,k=0;str[j]==substr[k];j++,k++){ //j遍历str,k遍历substr,对应项相等
if(substr[k]=='\n'){//对应项相等,保持到substr尾,则说明有一个子串
num++;break;
}
}
}
return num;
}
3.2 字符串长度(strlen),字符串大小(strcmp),字符串复制(strcpy)
//strlen
char *s="asdf";
int count=0;
while(*a!='\0'){
count ++;
}
//strcmp
int strcmp(char *a,char *b){
for(;*a==*b;){
if(*b=='\0') return 0;
a++;b++;
}
return(*a-*b)
}
//strcpy
void strcpy(char *a,char *b){
while((*a=*b)!='\0'){
a++;b++;
}
}
3.3 不改变串的内容逆序输出串--递归
void inverp(char *a){
if(*a!='\0'){
inverp(a+1);
}
putchar(*a);
}
3.4 删除字符串中所有空格
//本代码思路不是删除空格,而是复制所有非空格,最后strcpy复制回原串
#include <string.h>
#include <ctype.h>
void delspace(char *p){
int i,t;
char c[80];
for(i=0,t=0;p[i];i++){
if(!isspace(p[i])) c[t++]=p[i];//不是空格就复制
}
strcpy(p,c);
}
4.链表相关
4.1 对所给数组 int a[] 构造链表
struct node{
int num;
struct node * next;
};
struct node * creat(int a[],int n){
struct node *head,*rear,*p;
rear = head=NULL;
int i
if(n<0) break;
//构造头节点
p=(struct node*)malloc(sizeof(struct node));
p->num=a[0];p->next=NULL;
head=rear=p;
//后续节点
for(i=1;i<n;i++){
p=(struct node*)malloc(sizeof(struct node));
p->num=a[i];
p->next=rear->next;//给新节点赋值
rear->next=p;
rear=p;//新节点接入,移动尾节点
}
return head;
}
4.2 循环链表之选猴王
//选猴王 --循环链表
int selectking(linklist *head,int c){
linklist *p,*q;
int i=0;
do{
i++;
if(i%c!=0){
q=p;
p=p->next;
}
else{
q->next=p->next;
i=0;
}
}while(p->next!=p);
return p->data;
}
4.3 有序链表插入新节点
//有序链表插入新节点--我自己写的,没有检验对错
void(linklist *head,int num){
linklist *p,*temp;
p=head;
*temp=(linklist *)malloc(sizeof(linklist));
temp->data=num;
if(head==NULL){
head=temp;
}
else if(num<head->data){
temp->next=head;
head=temp;
}
else{
while(p->next!=NULL){
if(num>p->next->data){
p=p->next;
}
else if(num>p->data&&num<p->next->data){
temp->next=p->next;
p->next=temp;
}
}
}
}
4.4 两有序链表合并,并删除相同值
//将两有序链表融合,并删除相同节点。
linklist * engage(linklist *head1,linklist *head2){
linklist *p1,*p2,*p,*head;//p1遍历链表1 p2遍历链表2 p遍历结果链表 head结果链表头
p1=head1;
p2=head2;
if(p1==NULL){
head=head2;
}
else if(p2==NULL){
head=head1;
}
if(p1->data<p2->data){
head=p1;
p1=p1->next;
}
else{
head=p2;
p2=p2->next;
}
p=head;
while(p1!=NULL&&p2!=NULL){
if(p1->data<p2->data){
p->next=p1;
p=p->next;
p1=p1->next;
}
else{
p->next=p2;
p=p->next;
p2=p2->next;
}
}
if(p1==NULL){
p->next=p2;
}else{
p->next=p1;
}
return head;
}
八、选择题错误记录
8.1 字节数
char 1;int 2;long 4;float 4 ;double 8
int:-32768 至 32767 ;double读入用lf ;
8.2 赋值语句 运算符
e: 3e2,e之后必为整数;
%求余运算:两操作数必须为整型;
while(1<=x<=2):合法,等于(1<=x)<=2,其结果必为 1;
8.3 概念选择题
① sizeof
② #define double 3.14159
③ 数组定义:类型说明符 数组名 [常量表达式]
【常量表达式包括 常量 和 符号常量 如 4 和 #define m 3】
数组引用:下标可以是 常量表达式、变量或表达式,变量或表达式必须有确定的值。
④ 函数的形参和实参分别占用不同的存储单元
⑤c语言所有函数都是外部函数
-在c语言中,函数中变量的隐含存储类别是auto
-在c语言中,函数的隐含存储类别是 extern
⑥只有在使用才为该类型变量分配内存的存储类型说明:auto 和 register; static:编译时赋初值。
⑦
建议练习【函数】:第五章 函数_ - 百度文库 (baidu.com)
8.4 优先级
(1)普通运算符
一目:sizeof(),++,--,+正,-负,^
二目:&&>||
三目:a?b:c--从右往左
(2)指针相关
①*(pt+1)[2]:先算[ ],再算*;
②*p->b++: *((p->b)++):p->b=p->b+1,p指针所指的b内的地址加一,即后移一位。*取的是加之前的。其结果就是:取当前p对应当前b所指的值,b地址后移一位。
③++*p->b:b指针所指的值+1,且结果为+1之后的值
②③中p为结构体指针,a,b为结构体成员,b为char指针
④(*p).a: 成员运算符“.”优先级高于 *,故括号不能省;->也高于 *,如 *(++p)->b相当于*((++p)->b)
尝试性总结:*比其后面的运算要晚
8.5 错误
括号不配对:编译错误
8.6 字符
' '-32 (space); '0'-48; 'A' -65; 'a'-97
'\ddd'-八进制 '\xhh'-十六进制 printf("%o,%x",d,d);
'\b':退格(替换不是删除)printf("abc\b")--ab printf("abc\b\n")--abc
8.7 预编译
① stdio.h
printf(); scanf(); FILE 【FILE *fp】;
② string.h
puts(); gets(); str*()【strcat,strcpy,strcmp,strlen】
③ stdlib.h
malloc(); free(); exit();
8.8 元素定义
char a[ ]={0,1,2,3,4,5}; 合法