《明解c语言》读书笔记
1-3 输入和显示
- puts函数只接受 一个 实参,并且在结尾自动换行
2-1运算
- 求商运算符 :除法运算符" / ",如果是两个整数相除结果为整数如果需要保留小数时 必须将其中一个除数转换为浮点数。5/3=1 3/5=0 6.0/4=1.5 (List01)
- 求余运算符 int%int
- 用printf函数打印%时,要避免使用转换功能,必须写成%%,printf(“5%%3=%d\n”,5%3);当使用不具备转换说明功能的puts函数时,写一个%即可。
- 操作数类型不同时,会进行向上转型(int——>double),然后再运算。
- 还是百分比问题,代码改动,因为输入的用户习惯是整数而不是实数,所以将输入整数向上转型后再进行求商运算
#include<stdio.h>
int main()
{
float num1,num2;
puts("请输入第一个数:");
scanf("%d",&num1);
puts("请输入第一个数:");
scanf("%d",&num2);
printf("num1是num2的%f%%",double(num1)/num2*100);
getch();//持续显示dos窗口
return 0;
}
- 条件运算符,是一个三目运算符。n1?n2:n3
- switch语句:如果case语句后面没有break,则会顺序执行下来。

4-1 do语句
- 逆向显示数值(规定是正数)
1 #include<stdio.h> 2 int main() 3 { 4 int num; 5 do 6 { 7 puts("请输入一个正数:"); 8 scanf("%d",&num); 9 if(num<0) 10 puts("请重新输入一个正数!"); 11 }while(num<0); 12 13 puts("逆序的正数变为:"); 14 15 do 16 { 17 printf("%d",num%10); 18 num=num/10; 19 }while(num!=0); 20 21 getch(); 22 23 return 0; 24 }
- 4-13:输入整数后,循环显示1234567890,显示的位数和输入的整数值相同。例如输入12,显示:123456789012
1 #include<stdio.h> 2 int main() 3 { 4 int i,j,num; 5 j=1; 6 printf("enter a number:"); 7 scanf("%d",&num); 8 for(i=1;i<=num;i++) 9 { 10 printf("%d",j); 11 j++; 12 if(j==10) 13 j=0; 14 } 15 getch(); 16 return 0; 17 }
4-15 输入九九乘法表增加横轴标题
1 #include<stdio.h> 2 void main() 3 { 4 int i,j; 5 printf(" | "); 6 for(i=1;i<=9;i++) 7 { 8 printf("%3d ",i); 9 } 10 putchar('\n'); 11 for (i=0;i<=50;i++) 12 { 13 printf("-"); 14 } 15 putchar('\n'); 16 for(i=1;i<=9;i++) 17 18 { 19 printf(" %d |",i); 20 for(j=1;j<=9;j++) 21 { 22 23 printf(" %3d",i*j); 24 } 25 putchar('\n'); 26 } 27 getch(); 28 }
:
5-1数组
- 数组的声明和初始化
1)声明数组的时候,元素个数必须是常量 (但是可以宏定义)
int n=5;int a[n];(FALSE)
2)初始化如不指定元素个数,数组会根据初始化值的个数自动进行设定
int vc[]={1,2,3,5,7};
3)初始化初始值数量不足时,会自动用0对剩余的元素进行初始化
必须是以int vc[5]={0,1}的形式初始化
如果类似于
int test[5];
int i;
for(i=1;i<4;i++)
test[i]=i;
那么 test[0],test[4]是不定值
4)不能通过赋值进行初始化
int vc[3];
vc={1,2,3};
只能单个初始化 vc[0]=1;
练习:以10分为单位显示出学生的成绩分布图
结果:

代码:
1 #include<stdio.h> 2 #define NUMBER 80 //人数上限 3 void main() 4 { 5 int i,j; 6 int num; //实际人数 7 int fenshu[NUMBER];//学生分数 8 int fenbu[11]={0};//分数分布 9 10 printf("请输入学生人数:"); 11 do 12 { 13 scanf("%d",&num); 14 if(num<1||num>NUMBER) 15 printf("\a人数范围[1到%d]:",NUMBER); 16 }while(num<1||num>NUMBER); 17 18 puts("请输入学生分数:"); 19 20 for(i=0;i<num;i++) 21 { 22 printf("%2d号:",i+1); 23 do 24 { 25 scanf("%d",&fenshu[i]); 26 if(fenshu[i]<0||fenshu[i]>100) 27 printf("\a分数范围[0到100]:"); 28 }while(fenshu[i]<0||fenshu[i]>100); 29 fenbu[fenshu[i]/10]++; 30 } 31 32 puts("\n ---分布图---"); 33 printf(" 100:"); 34 for(j=0;j<fenbu[10];j++) 35 putchar('*'); 36 putchar('\n'); 37 38 for(i=9;i>=0;i--) 39 { 40 printf("%3d-%3d:",i*10,i*10+9); 41 for(j=0;j<fenbu[i];j++) 42 putchar('*'); 43 putchar('\n'); 44 } 45 getch(); 46 }
这里面有些关于if while循环输入和分布统计的格式需要注意
练习:写一段程序,求出矩阵x乘以y的值
我第一次写的程序可谓“人神共愤”,要数清楚原矩阵和结果矩阵的维数才能进行for循环,贴出来反省一下
1 #include<stdio.h> 2 void main() 3 { 4 5 int x[2][3]={{1,2,3},{4,5,6}}; 6 int y[3][2]={{1,5},{5,3},{8,1}}; 7 int mc[2][1]={0}; 8 int i,j; 9 //计算 10 for(i=0;i<2;i++) 11 { 12 for(j=0;j<3;j++) 13 { 14 15 mc[i][1]+=x[i][j]*y[j][i]; 16 17 } 18 } 19 //输出 20 for(i=0;i<2;i++) 21 { 22 23 24 printf("%3d",mc[i][1]); 25 putchar('\n'); 26 } 27 28 29 getchar(); 30 }
后来看到网上的模板,用函数调用写的层次分明,原帖:http://blog.sina.com.cn/s/blog_8e392fc20101br1e.html
结果:

代码:
1 #include<stdio.h> 2 #define SIZE 20 //预定义要进行转置的矩阵最大大小为 20*20 3 //为了函数参数传递的方便,将行和列的具体大小定义为全局变量 4 int a_column; 5 int a_row ; 6 int b_row; 7 int b_column; 8 int c[SIZE][SIZE]={0}; 9 //矩阵的输入函数 10 void inputMatrix(int a[][SIZE] , int n, int m){ //二维数组参数下标必须要确定 11 int i,j; 12 for(i = 0;i < n;i++){ 13 for(j = 0;j < m;j++){ 14 scanf("%d",&a[i][j]); 15 } 16 } 17 } 18 //矩阵的输出函数 19 void outputMatrix(int c[][SIZE] , int n, int m){ 20 int i,j; 21 for(i = 0;i < n;i++){ 22 for(j = 0;j < m;j++){ 23 printf("%d ",c[i][j]); 24 } 25 printf("\n"); //每次打印完一行后进行换行 26 } 27 } 28 //矩阵的乘法算 29 void matrixMultiplication(int a[][SIZE], int b[][SIZE]){ 30 int i,j,k; 31 for(i = 0;i < a_row;i++){ 32 for(j = 0; j < b_column ; j++){ 33 for(k = 0; k < a_column;k++){ 34 c[i][j] = c[i][j]+ a[i][k] * b[k][j]; 35 } 36 } 37 } 38 } 39 int main() 40 { 41 //定义数组并初始化 42 int a[SIZE][SIZE]={0}; 43 int b[SIZE][SIZE]={0};//定义数组,注意要初始化 44 //矩阵行,列数的确定 45 printf("请输入第一个矩阵的行数 : "); 46 scanf("%d",&a_row); 47 printf("\n请输入第一个矩阵的列数 :"); 48 scanf("%d",&a_column); 49 //函数调用及主功能实现 50 printf("请输入矩阵A ( %d X %d 形式)\n" , a_row,a_column); 51 inputMatrix(a , a_row, a_column); 52 b_row=a_column; 53 printf("注意:根据数学原理,您将输入的第二个矩阵的行数为 %d \n",b_row); 54 printf("请输入矩阵B的列数 : "); 55 scanf("%d",&b_column); 56 printf("请输入矩阵B ( %d X %d 形式) : \n" , b_row , b_column); 57 inputMatrix(b, b_row , b_column); 58 //调用相乘函数 59 matrixMultiplication(a,b); 60 printf("A与B相乘后的矩阵C是 :\n"); 61 outputMatrix(c, a_row , b_column); 62 getchar(); 63 return 0; 64 }
5-15 求解质数
1 #include<stdio.h> 2 int main() 3 { 4 int no,i; 5 printf("请输入一个数字:"); 6 scanf("%d",&no); 7 for(i=2;i<no;i++) 8 { 9 if(no%i==0) 10 { 11 printf("不是质数"); 12 break; 13 } 14 15 } 16 if(no==i) 17 printf("是质数"); 18 getch(); 19 return 0; 20 }
一开始printf输成prinf,结果链接出问题。vs2010既不显示关键词也不告诉我哪儿错了,害我找半天。
然后就是最后的if(no==i)如果写在for循环里面永远都跳不出来。因为i==no时候就跳出了for循环了,还怎么执行这个if啊。。。
5-18 求质数第二版
1 #include<stdio.h> 2 /*判断1000以内的质数*/ 3 void main() 4 { 5 int num,i; 6 int str=0; 7 int prime[500]; 8 unsigned long counter=0; 9 prime[str++]=2; 10 prime[str++]=3; 11 /*原理:从5开始尝试是否能被已经得到的质数整除*/ 12 for(num=5;num<1000;num+=2) 13 { 14 for(i=1;i<str;i++) 15 { 16 counter++; 17 if(num%prime[i]==0) 18 19 break; 20 } 21 if(i==str) 22 prime[str++]=num; 23 } 24 printf("1000以内的质数为\n"); 25 for(i=0;i<str;i++) 26 { 27 printf("%5d",prime[i]); 28 } 29 printf("\n计算次数为:%lu",counter); 30 getch(); 31 } 32 33
该方法基于的原理是:质数一定是奇数,并且不能被小于它的质数整除。
第17行中,直接从prime[1]=3开始除,因为没有除以2的必要。但是一开始编的时候笔者想省一下所以就赋str=1,并且省略了第9行。想的是不是省略的都会被赋值为0吗,反正我又不除以prime[0](也就是2)。这样一来暂且不说27行输出的时候第一个质数2没法输出,输出的也不是0,居然是一个奇大的数,看来是内存没有被初始化调用了本来占用此位置的资源了。那么问题来了,数组开头省略的那些初始化也能被自动赋值为0吗?
另外,第24行一开始写进了for循环导致输出重复。

求质数版本3
1 #include<stdio.h> 2 #include<math.h> 3 /*判断1000以内的质数*/ 4 void main() 5 { 6 int num; 7 int i; 8 int flag; 9 int counter=0; 10 printf("%5d%5d",2,3); 11 for(num=5;num<1000;num+=2) 12 { 13 flag=0; 14 for(i=3;counter++,i<=sqrt(num);i+=2) 15 { 16 counter++; 17 18 if(num%i==0) 19 { 20 flag=1; 21 break; 22 } 23 } 24 if(!flag) 25 printf("%5d",num); 26 } 27 printf("\n counter:%d",counter); 28 29 getch(); 30 } 31 32
注意第14行判断语句是有=,counter表示乘除法运算次数 。sqrt也是除法、、
chapter 6
没有返回值的函数:输出直角在右下角的三角形
1 #include<stdio.h> 2 void put_nchar(int ch,int no) 3 { 4 while(no-->0) 5 putchar(ch); 6 } 7 void main() 8 { 9 int i,ln; 10 printf("三角形有几层:"); 11 scanf("%d",&ln); 12 for (i=1;i<=ln;i++) 13 { 14 put_nchar(' ',ln-i); 15 put_nchar('*',i); 16 putchar('\n'); 17 } 18 19 }
第四行用while代替for循环,简单明了。
6-9将非负整数倒转输出
1 #include<stdio.h> 2 int scan_unit() 3 { 4 int num; 5 do 6 { 7 printf("请输入一个非负整数:"); 8 scanf("%d",&num); 9 if(num<0) 10 { 11 printf("输入有误,请输入一个非负整数:"); 12 scanf("%d",&num); 13 } 14 }while(num<0); 15 return num; 16 17 } 18 int rev_unit(int num) 19 { 20 int tmp=0; 21 do 22 { 23 tmp=tmp*10+num%10; 24 num/=10; 25 }while(num>0); 26 return tmp; 27 } 28 void main() 29 { 30 //主函数调用输入函数和逆序函数,简单明了 31 int number=scan_unit();//可以如此初始化 32 printf("该整数逆序后的值是%d\n",rev_unit(number)); 33 getch(); 34 }
6-10:输出几个学生的最高成绩
1 #include<stdio.h> 2 #define NUMBER 5 3 int stu[NUMBER]; 4 void scan_score_unit() 5 { 6 int i; 7 for(i=0;i<NUMBER;i++) 8 { 9 printf("第%d个学生分数为:",i+1); 10 scanf("%d",&stu[i]); 11 } 12 13 } 14 int top_score_unit() 15 { 16 int max=0; 17 int i; 18 for(i=0;i<NUMBER;i++) 19 { 20 if(stu[i]>max) 21 max=stu[i]; 22 } 23 return max; 24 25 } 26 void main() 27 { 28 printf("请输入学生分数:"); 29 scan_score_unit(); 30 printf("最高分数为:%d",top_score_unit()); 31 32 }
这里有几个问题没搞清楚
1,如果我要返回多个数值,比如打印最高分时输出哪个学生和多少分,怎么写被被调用函数,应该用指针,但是忘了怎么写了
2,第七,十八行如果把int i写在for循环里会报错,但是放出来就没问题。没这个道理的啊本来是
最后被调用函数写在调用函数前面。全局变量如这里的stu[NUMBER]写在函数之外
6_11计算不同科目的最高分
1 //计算数学和英语分数中的最高分 2 #include<stdio.h> 3 #define NUMBER 5 4 int eng[NUMBER]; 5 int mat[NUMBER]; 6 void scan_score_unit() 7 { 8 int i; 9 for(i=0;i<NUMBER;i++) 10 { 11 printf("\n第%d个学生",i+1); 12 printf("英语:"); 13 scanf("%d",&eng[i]); 14 printf(" 数学:"); 15 16 scanf("%d",&mat[i]); 17 } 18 19 } 20 int top_score_unit(int vc[],int no) 21 { 22 int max=vc[0]; 23 int i; 24 for(i=1;i<no;i++) 25 { 26 if(vc[i]>max) 27 max=vc[i]; 28 } 29 return max; 30 31 } 32 void main() 33 { 34 int max_e,max_m; 35 printf("请输入学生分数:"); 36 scan_score_unit(); 37 /*分两次分别调用top*函数计算不同科目的最高分*/ 38 max_e=top_score_unit(eng,NUMBER); 39 max_m=top_score_unit(eng,NUMBER); 40 printf("英语最高分数为:%d\n",max_e); 41 printf("数学最高分数为:%d\n",max_m); 42 getch(); 43 44 }
此处由于top*函数设置了形参,可以接收任意长度的任意数组
6-12:数组传参时不会改变参数的数值,但是如果传的是数组首地址,则函数的操作可能会改变原数组的值。为了防止这种情况,可以在申明形参的时候加上const就可以了
int top_score_unit(const int vc[],int no)
6-15:找寻数组元素的顺序查找法和哨兵查找法
顺序查找法比较常见,顺序比较,找到目标值后返回标号。当下标=数组元素的时候表示全部比较了一遍(因为下标从0开始),找寻失败。
哨兵查找法需要定义数组大小多一位,因为算法需要将目标值赋给数组最后一位,那么当找到目标值的下标等于最后一位时候,表示找寻失败,否则查询成功。
1 #include<stdio.h> 2 #define NUMBER 5 3 #define FAILED -1; 4 int search(int vc[],int key,int no) 5 { 6 int i; 7 vc[no]=key; 8 for(i=0;vc[i]!=key;i++) 9 ; 10 return (i==no?FAILED:i); 11 } 12 void main() 13 { 14 int i,vy,idx; 15 int vx[NUMBER+1]; 16 for(i=0;i<NUMBER;i++) 17 { 18 printf("vx[%d]:",i); 19 scanf("%d",&vx[i]); 20 } 21 printf("输入要查找的值:"); 22 scanf("%d",&ky); 23 idx=search(vx,ky,NUMBER); 24 if(idx==FAILED) 25 puts("\a查找失败"); 26 else 27 printf("%d是数组的%d号元素",ky,idx+1); 28 getch(); 29 30 31 }
chapter 7 基数转换
在说十进制转换其他进制数的话题之前。现对“十进制数转换为十进制数”的方法做一下说明。一个数除以10的余数,与这个数的末尾数字相等。例如1962除以10的余数为2,与末尾数字2相等。
此处除法运算1962/10的商为196,也就是1962右移一位后的值(删去末位的2),即十进制数除以10的意思是右移一位。接着用196除以10,得到的余数6就是倒数第2位的值。继续将此时的商19除以10
将一个数除以10,求得商和余数,再对商做同样的除法计算,重复这一过程,直到商为0为止,最后将所得的所有余数逆序排列,就得到了转换后的十进制数。
同样的,数字转换为2,8,16进制的时候,也是根据这样的原理来操作的。
7-2整型和字符型
(a)0,1,2,3,4,5,6,7,8,9,10 (无符号数)
(b)-5 -4 -3 -2 -1 0 1 2 3 4 (有符号数)
选取数据类型的时候,实现要确定处理的数会不会是负数,如果不是,并且需要处理较大的数值的时候,使用(a)较为合适。一般声明的变量类型视编译器而定,目前大多数情况规定为signed(有符号)类型
|
字符型 |
||
|
Signed char (-128~127) |
Char (0~255) |
Unsigned char (0~255) |
|
整型 |
||
|
Signed short int (-32768~32767) |
|
Unsigned short int (0~65535) |
|
Signed int (-32768~32767) |
|
Unsigned int (0~65535) |
|
Signed long int (-2147483648~2147483647) |
|
Unsigned long int (0~4294967295) |
c语言编译器在<limits.h>头文件中宏定义了各种数值类型的最大值和最小值
某个vc++6.0环境下的<limits.h>头文件(注意第五行)
1 /*** 2 *limits.h - implementation dependent values 3 * 4 * Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved. 5 * 6 *Purpose: 7 * Contains defines for a number of implementation dependent values 8 * which are commonly used in C programs. 9 * [ANSI] 10 * 11 * [Public] 12 * 13 ****/ 14 15 #if _MSC_VER > 1000 16 #pragma once 17 #endif 18 19 #ifndef _INC_LIMITS 20 #define _INC_LIMITS 21 22 #if !defined(_WIN32) && !defined(_MAC) 23 #error ERROR: Only Mac or Win32 targets supported! 24 #endif 25 26 27 #define CHAR_BIT 8 /* number of bits in a char */ 28 #define SCHAR_MIN (-128) /* minimum signed char value */ 29 #define SCHAR_MAX 127 /* maximum signed char value */ 30 #define UCHAR_MAX 0xff /* maximum unsigned char value */ 31 32 #ifndef _CHAR_UNSIGNED 33 #define CHAR_MIN SCHAR_MIN /* mimimum char value */ 34 #define CHAR_MAX SCHAR_MAX /* maximum char value */ 35 #else 36 #define CHAR_MIN 0 37 #define CHAR_MAX UCHAR_MAX 38 #endif /* _CHAR_UNSIGNED */ 39 40 #define MB_LEN_MAX 2 /* max. # bytes in multibyte char */ 41 #define SHRT_MIN (-32768) /* minimum (signed) short value */ 42 #define SHRT_MAX 32767 /* maximum (signed) short value */ 43 #define USHRT_MAX 0xffff /* maximum unsigned short value */ 44 #define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */ 45 #define INT_MAX 2147483647 /* maximum (signed) int value */ 46 #define UINT_MAX 0xffffffff /* maximum unsigned int value */ 47 #define LONG_MIN (-2147483647L - 1) /* minimum (signed) long value */ 48 #define LONG_MAX 2147483647L /* maximum (signed) long value */ 49 #define ULONG_MAX 0xffffffffUL /* maximum unsigned long value */ 50 51 #if _INTEGRAL_MAX_BITS >= 8 52 #define _I8_MIN (-127i8 - 1) /* minimum signed 8 bit value */ 53 #define _I8_MAX 127i8 /* maximum signed 8 bit value */ 54 #define _UI8_MAX 0xffui8 /* maximum unsigned 8 bit value */ 55 #endif 56 57 #if _INTEGRAL_MAX_BITS >= 16 58 #define _I16_MIN (-32767i16 - 1) /* minimum signed 16 bit value */ 59 #define _I16_MAX 32767i16 /* maximum signed 16 bit value */ 60 #define _UI16_MAX 0xffffui16 /* maximum unsigned 16 bit value */ 61 #endif 62 63 #if _INTEGRAL_MAX_BITS >= 32 64 #define _I32_MIN (-2147483647i32 - 1) /* minimum signed 32 bit value */ 65 #define _I32_MAX 2147483647i32 /* maximum signed 32 bit value */ 66 #define _UI32_MAX 0xffffffffui32 /* maximum unsigned 32 bit value */ 67 #endif 68 69 #if _INTEGRAL_MAX_BITS >= 64 70 /* minimum signed 64 bit value */ 71 #define _I64_MIN (-9223372036854775807i64 - 1) 72 /* maximum signed 64 bit value */ 73 #define _I64_MAX 9223372036854775807i64 74 /* maximum unsigned 64 bit value */ 75 #define _UI64_MAX 0xffffffffffffffffui64 76 #endif 77 78 #if _INTEGRAL_MAX_BITS >= 128 79 /* minimum signed 128 bit value */ 80 #define _I128_MIN (-170141183460469231731687303715884105727i128 - 1) 81 /* maximum signed 128 bit value */ 82 #define _I128_MAX 170141183460469231731687303715884105727i128 83 /* maximum unsigned 128 bit value */ 84 #define _UI128_MAX 0xffffffffffffffffffffffffffffffffui128 85 #endif 86 87 #ifdef _POSIX_ 88 89 #define _POSIX_ARG_MAX 4096 90 #define _POSIX_CHILD_MAX 6 91 #define _POSIX_LINK_MAX 8 92 #define _POSIX_MAX_CANON 255 93 #define _POSIX_MAX_INPUT 255 94 #define _POSIX_NAME_MAX 14 95 #define _POSIX_NGROUPS_MAX 0 96 #define _POSIX_OPEN_MAX 16 97 #define _POSIX_PATH_MAX 255 98 #define _POSIX_PIPE_BUF 512 99 #define _POSIX_SSIZE_MAX 32767 100 #define _POSIX_STREAM_MAX 8 101 #define _POSIX_TZNAME_MAX 3 102 103 #define ARG_MAX 14500 /* 16k heap, minus overhead */ 104 #define LINK_MAX 1024 105 #define MAX_CANON _POSIX_MAX_CANON 106 #define MAX_INPUT _POSIX_MAX_INPUT 107 #define NAME_MAX 255 108 #define NGROUPS_MAX 16 109 #define OPEN_MAX 32 110 #define PATH_MAX 512 111 #define PIPE_BUF _POSIX_PIPE_BUF 112 #define SSIZE_MAX _POSIX_SSIZE_MAX 113 #define STREAM_MAX 20 114 #define TZNAME_MAX 10 115 116 #endif /* POSIX */ 117 118 #endif /* _INC_LIMITS */
判断当前编译器下的char型是否是有符号的
1 #include<stdio.h> 2 #include<limits.h> 3 int main() 4 { 5 printf("这个编译器中的char型是");/*注意c语言是区分大小写的,之前我写的char_min就报错了*/ 6 if(CHAR_MIN) 7 puts("有符号的"); 8 else 9 puts("无符号的"); 10 }
窥探整数内部
输出非负整数的源码
1 #include<stdio.h> 2 int count_bits(unsigned x) 3 { 4 int count=0; 5 while(x){ 6 if(x&1U) 7 count++; 8 x>>=1; 9 } 10 return count; 11 } 12 int int_bits() 13 { 14 return count_bits(~0U); 15 } 16 void print_bits(unsigned x) 17 { 18 int i; 19 for(i=int_bits()-1;i>=0;i--) 20 { 21 putchar((x>>i)&1U?'1':'0'); 22 } 23 } 24 void main() 25 { 26 unsigned nx; 27 printf("请输入一个非负整数:"); 28 scanf("%u",&nx); 29 print_bits(nx); 30 putchar('\n'); 31 getch(); 32 33 }
结果:

count_bits主要是判断unsigned型的位数,这里是32位。注意第21行
8-2枚举类型
枚举常量的数值类型是int型 因此在返回类型为enum animal型的select函数中 可以返回int型变量的tmp值
1 enum animal select() 2 { 3 int tmp; 4 do 5 { 6 printf("0——狗 1——猫 2——猴 3——结束:"); 7 scanf("%d",&tmp); 8 }while(tmp<dog||tmp>Invaid); 9 return tmp; 10 }
为明确起见 也可以做如下强制转换 return ((enum animal)tmp)
Example 8-7
求两个整数的最大公约数
1 #include<stdio.h> 2 int gcdf(int vx,int vy) 3 { 4 return (vy==0? vx:gcdf(vy,vx%vy)); 5 } 6 int gcd(int vx,int vy) 7 { 8 return (vx>vy? gcdf(vx,vy):gcdf(vy,vx)); 9 } 10 int main() 11 { 12 int n1,n2; 13 puts("请输入两个整数"); 14 printf("整数1:"); 15 scanf("%d",&n1); 16 printf("整数2:"); 17 scanf("%d",&n2); 18 printf("最大公约数是:%d\n",gcd(n1,n2)); 19 getch(); 20 return 0; 21 22 } 23
注意第四行的递归。
根据第四行,我们用22和8来思考一下本程序求最大公约数的原理
现在有一个长为22、宽为8的长方形。这个正方形可以分为两个边长为8的正方形A 、B和一个边长为8,6的长方形。这个长方形又可以分为一个边长为6的正方形和一个边长为6,2的长方形。最后这个边长为6,2的长方形被分为3个边长为2的小正方形。此时“瓜分”结束,这两个数的最小公约数为2。
即:反复进行“从剩下的长方形中瓜分出正方形”的操作,知道全部是正方形为止。

8-9 计算标准输入流中出现的数字字符数
1 #include<stdio.h> 2 int main() 3 { 4 5 int i,ch; 6 int cnt[10]={0}; 7 while(1) 8 { 9 ch=getchar(); 10 if(ch=='\n') 11 break; 12 if(ch>='0'&&ch<='9') 13 cnt[ch-'0']++; 14 } 15 puts("数字字符出现的个数"); 16 for(i=0;i<10;i++) 17 printf("'%d':%d\n",i,cnt[i]); 18 19 getch(); 20 21 return 0; 22 }
c语言中的字符都作为非负数值来处理,因此在定义ch的时候是int类型 并且在12行可以用之直接与字符类型比较。
ch-'0'就相当于数组内下标的偏移了,当ch=‘0’的时候对应的数组元素是cnt[0] 当ch=‘1’的时候对应的数组元素就是cnt[1]了。
如12行的if循环可以替代“case '0':cnt[0]++”这样的语句,简单的用一个语句代替了10个case语句。
Ex8-8 计算标准输入流中出现的行数 输入为!时结束
1 #include<stdio.h> 2 int main() 3 { 4 5 int n,ch; 6 n=0; 7 while((ch=getchar())!='!') 8 { 9 if(ch=='\n') 10 n++; 11 } 12 printf("一共%d行\n",n); 13 14 getch(); 15 16 return 0; 17 }
9-3 字符串的初始化
最简便方法 char str[]="ABC";省略数组中的元素个数 直接将字符串赋给字符串变量str。其功能相当于 char str[]={'A','B','C','\0'};
但是除了初始化赋值的时候,我们不能将数组的初始值直接赋给数组变量:
如: str="ABC" 或者 str={'A','B','C','\0'} 【FALSE】
如之前的数组初始化定义中说道的,单个数组元素非初始化赋值是可以的。例如:str[0]='A'
9-2 二维数组看上去是二维表,但是在物理内存中是线性排列的。如图

字符串数组
char cs[3][6] = { "Turbo", "NA", "DOHC" };/*第一维数据可以省略*//*第一维数代表字符串个数,第二维数代表单个字符串长度,取最大的一个长度*/
for (int i = 0; i < 3; i++)
{
printf("cs[%d]=%s\n", i, cs[i]);
}
chapter 10 指针
1 int nx; 2 printf("nx 的地址是:%p\n",&nx);
%p是对象地址转换说明。
Ex10_1 编写函数,求出测试日期的前一天和后一天(要求能正确判断闰年)


1 #include<stdio.h> 2 int IsLeapYear(int *y) 3 { 4 int flag = 0; 5 if (*y % 400 == 0 || *y % 4 == 0 && *y % 100 != 0) 6 flag = 1; 7 return flag; 8 } 9 void yesterday(int *y, int *m, int *d) 10 { 11 int month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 12 int flag = IsLeapYear(y); 13 if (--*d == 0) 14 { 15 if (--*m == 2) 16 { 17 *d = month[2] + flag; 18 } 19 else if (*m==0) 20 { 21 (*y)--; 22 *m=12; 23 *d = 31; 24 } 25 else 26 { 27 *d = month[*m]; 28 } 29 } 30 //printf("flag=%d", flag); 31 32 } 33 void tomorrow(int *y, int *m, int *d) 34 { 35 int month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 36 int flag = IsLeapYear(y); 37 //先重新定义month[2]的值 38 month[2] = month[2] + flag; 39 // printf("flag=%d",flag); 40 if (++*d>month[*m]) 41 { 42 (*m)++; 43 *d = 1; 44 if (*m > 12) 45 { 46 (*m) = 1; 47 (*y)++; 48 } 49 50 } 51 52 } 53 void main() 54 { 55 int year, month, day,yy,mm,dd; 56 printf("请输入年月日:(XXXX XX XX)\n"); 57 scanf_s("%d%d%d", &year, &month, &day); 58 yy = year; 59 mm = month; 60 dd = day; 61 yesterday(&year, &month, &day); 62 tomorrow(&yy, &mm, &dd); 63 printf("前一天是:%d-%d-%d\n", year,month,day); 64 printf("后一天是:%d-%d-%d\n", yy,mm,dd); 65 } 66 getchar();
Ex10—2 编写函数,将三个int型整数升序排列void sort3(int *n1, int *n2, int *n3)
1 #include<stdio.h> 2 void sort2(int *n1, int *n2) 3 { 4 if (*n1<*n2) 5 { 6 int temp=0; 7 temp = *n1; 8 *n1 = *n2; 9 *n2 = temp; 10 } 11 } 12 void sort3(int *n1, int *n2, int *n3) 13 { 14 sort2(n1, n2); 15 sort2(n2, n3); 16 sort2(n1, n2); 17 //测试sort3函数里有没有进行成功 18 //printf("三个数升序排列为:%d,%d,%d\n", *n1, *n2, *n3); 19 20 } 21 void main() 22 { 23 int n1, n2, n3; 24 printf("请输入三个整数:\n"); 25 scanf_s("%d%d%d", &n1, &n2, &n3); 26 sort3(&n1, &n2, &n3); 27 printf("三个数升序排列为:%d,%d,%d\n", n1, n2, n3); 28 29 } 30 getchar();
第一次运行时三个整数值没有回传,因为在sort2中很天真的使用了地址交换。即
temp = n1;
n1 = n2;
n2 = temp;
其实犯了单向传递的错误。只有在自定义函数内改变形参地址所指的值,主函数里的实参的值(回传的地址所指向的对象)才会改变。
10-3数组与指针
原则上,不带下标运算符[]而单独出现的数组名是指向该数组第一个元素的指针。但是也有例外情况,具体如下:
1)数组名为sizeof运算符的操作对象时:得出的是该数组的长度
2)数组名为&运算符的操作对象时:会生成一个指向该数组的指针,而不是指向数组第一个元素的指针。
chapter 11 字符串和指针
在指针声明中,若在数据类型前加上const修饰符,该指针指向的值就不能被修改了。
例如:想通过s来重新赋值的程序是不能通过的。
void func(const char *s)
{
//全部错误
s[1]='5';
*(s+2)='6'
}
所以,在形参的申明中加上const后,函数的使用者就不必担心“传递的指针会被用来更改值”了。
正确与不正确的字符串赋值
1 #include<stdio.h> 2 char *str_cpy(char *d, const char *s) 3 { 4 char *t = d; 5 while (*d++ = *s++) 6 ; 7 return t; 8 } 9 void main() 10 { 11 char s1[] = "ABCD"; 12 char s2[] = "EFGH"; 13 str_cpy(s2, s1); 14 printf("s1:%s\ns2:%s\n", s1, s2); 15 16 } 17 getchar();
1 #include<stdio.h> 2 char *str_cpy(char *d, const char *s) 3 { 4 char *t = d; 5 while (*d++ = *s++) 6 ; 7 return t; 8 } 9 void main() 10 { 11 char s1[] = "ABCDE"; 12 char *p= "EFGH"; 13 str_cpy(p, s1); 14 printf("s1:%s\np:%s\n", s1, p); 15 16 } 17 getchar();
两个代码中都存在一个问题,如果s1的位数比被赋值字符串的位数要多,可能会存在写入非空的内存空间的问题。
另外,第二个代码无法运行,原因是:程序改写了指针p指向的字符串字面量的内容
比如下面的程序:
char *m = "hello";
*(m+1) = 's';
for(;*m != '\0';m++){
printf("%c\n",*m);
}
运行出错,但是用数组表示就能运行正确,如下:
int i = 0;
char w[] = "hello";
w[1] = 's';
while(w[i] != '\0'){
printf("%c\n",w[i]);
i++;
}
根本原因在于:
char *m = "hello";
"hello"保存在静态数据区,该数据不能修改.相当于一个常量(不是变量) char *m="hello" 等价于 const char *m="hello"
由指针m指向. 不能通过指针m来修改静态数据区的值.
char w[] = "hello";
"hello"保存在栈空间数组里. 数组名为w, 函数名为数组的首地址.
可以通过w[i]='a', 或*(w+i)='a'的形式来修改数组内容.
或者说:
字符串“hello”本身就是一个常量字符指针,而对于指针m,无非就是一个地址的拷贝,也就是“hello”地址的拷贝,相当于m指向一个字符串常量,字符串常量是不予许改变的!
而对于w[]来说就不一样了,虽然hello本身是常量,不过此时拷贝给w[]的不是地址,而是内容,也就是“hello”,也就是w本身拥有一个自己的hello副本,可以对其进行想要的合法操作,比如改变等!!
11—3 字符串处理库函数
Ex11-5 不使用下标运算符,编写函数,删除字符串总的所有数字字符(“AB1C9”变为“ABC”)
chapter 12 结构体
利用typedef定义结构体有一定的方便性,在申明对象时候可以省略struct gstudent的数据类型的写法,只写已经重写的结构体名 student
//定义结构体 struct gstudent{}; //声明对象 struct gstudent std1; struct gstudent *s; //定义结构体 typedef struct{}student; //声明对象 student std1; student *s;
数组和结构体同作为聚合类型的区别:
1)元素(成员)类型
2)可否赋值
即便两个数组元素个数相同,也不能相互赋值。但是,相同类型的结构体可以互相赋值,如下所示。
int a[6],b[6];
a=b;/*错误*/
struct gstudent gx,gy;
gx=gy;/*正确*/
既然结构体可以相互赋值,那么有延伸形式:以结构体作为函数的返回类型,返回的值直接被赋给了结构体变量(对象)
struct MyStruct set_value(...)
{
struct Mystruct temp;
...;
return temp;
};
void main()
{
struct MyStruct value;
value = set_value(...); //直接将设定了值的temp返回给主函数的结构体对象。
}
表示时间和日期的结构体
#include<stdio.h> #include<time.h> void put_date() { time_t current; struct tm local; char day[][8] = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" }; time(¤t); localtime_s(&local,¤t); printf("%4d年%2d月%2d日(%s)\n" ,local.tm_year + 1900 , local.tm_mon + 1 , local.tm_mday , day[local.tm_wday]); printf("%2d时%2d分%2d秒\n" , local.tm_hour , local.tm_min , local.tm_sec); } void main() { printf("今天是"); put_date(); }

chapter 13 文件处理
FILE型,表示标准流的stdin(标准输入流),stdout(标准输出流),stderr(标准错误流)都是指向FILE型的指针型。FILE型实在<stdio.h>头文件中定义的,该数据类型用于记录控制流所需要的信息,其中包含以下数据:
文件位置指示符:记录当前访问地址
错误指示符:记录是否发生了读取错误或写入错误
文件结束指示符:记录是否已达文件末尾
利用fopen读取文件时,注意加上文件的路径,比如“f:\\opencv\\abc.txt”
Ex13_4 将文件读入的个人信息按身高排序后显示。
文件信息如下:

程序如下:
1 #include<stdio.h> 2 #include<string.h> 3 typedef struct{ 4 char name[20]; 5 double height; 6 double weight; 7 }people; 8 void swap(people *a1, people *a2) 9 { 10 people tmp; 11 tmp = *a1; 12 *a1 = *a2; 13 *a2 = tmp; 14 15 } 16 //冒泡排序 17 void sorting(people P[], int n) 18 { 19 int i, j; 20 for (i = 0; i < n - 1; i++) 21 { 22 for (j = 1; j < n - i; j++) 23 { 24 if (P[j - 1].height>P[j].height) 25 { 26 swap(&P[j - 1], &P[j]); 27 } 28 } 29 } 30 } 31 void main() 32 { 33 FILE *fp; 34 char *filename = "abc.txt"; 35 people people,stu[10]; 36 int Number = 0; 37 if (fopen_s(&fp, filename, "r") != 0) 38 printf("该文件不存在\n"); 39 else 40 { 41 printf("文件存在\n"); 42 int i = 0; 43 //关于fscanf读取数据的问题,字符串长度是10,注意位置,在变量之后加上长度 44 while (fscanf_s(fp, "%s%lf%lf", people.name, 10,&people.height, &people.weight) == 3) 45 { 46 stu[i] = people; 47 i++; 48 } 49 Number = i; 50 51 //排序 52 sorting(stu, Number); 53 //求平均值 54 double avaHeight = 0.0; 55 double avaWeight = 0.0; 56 for (int i = 0; i < Number; i++) 57 { 58 avaHeight += stu[i].height; 59 avaWeight += stu[i].weight; 60 } 61 avaHeight = avaHeight / Number; 62 avaWeight = avaWeight / Number; 63 //输出 64 for (int i = 0; i < Number; i++) 65 { 66 printf("%-10s%5.1f%5.1f\n", stu[i].name, stu[i].height, stu[i].weight); 67 } 68 printf("平均:%5.1f%5.1f\n", avaHeight, avaWeight); 69 } 70 71 72 }
结果如下:

关于文件的输入输出函数:
我们所常见的标准输入流stdin和标准输出流stdout都是指向FILE型的指针
因此 下面两条语句的功能相同
scanf("%d",&x) <=>fscanf(stdin,"%d",&x) 都是从标准输入流读取整数值并保存至x
printf("%d",x) <=>fprintf(stdout,"%d",x) 都是向标准输出流写入整数x的十进制数值
所以当标准输入输出流换成指向任意文件的文件指针fp时,fscanf表示从文件中读入数值并保存至x,fprintf表示向文件写入x的值。
Ex13_5显示程序上次的运行时间和运行内容,如是第一次运行则显示“程序第一次运行”
1 #include<stdio.h> 2 #include<time.h> 3 char filename[] = "abc.txt"; 4 void put_data() 5 { 6 FILE *fp; 7 time_t t; 8 struct tm local; 9 time(&t); 10 localtime_s(&local,&t); 11 if (fopen_s(&fp,filename,"w")!=0) 12 { 13 printf("文件打开失败\n"); 14 } 15 else 16 { 17 char s[20]; 18 printf("请输入你的心情:"); 19 scanf_s("%s", s,20); 20 fprintf(fp, "%d %d %d %d %d %d\n%s" 21 , local.tm_year + 1900 22 , local.tm_mon + 1 23 , local.tm_mday 24 , local.tm_hour 25 , local.tm_min 26 , local.tm_sec 27 ,s); 28 29 } 30 fclose(fp); 31 } 32 void get_data() 33 { 34 FILE *fp; 35 if (fopen_s(&fp, filename, "r") != 0) 36 printf("本程序是第一次运行,之前没有内容\n"); 37 else 38 { 39 int year, month, day, hour, min, sec; 40 char s[20]; 41 fscanf_s(fp, "%d%d%d%d%d%d%s", &year, &month, &day, &hour, &min, &sec,s,20); 42 printf("上一次运行的时间是%d年%d月%d日%d时%d分%d秒\n当时的心情:%s\n" 43 , year, month, day, hour, min, sec, s); 44 45 } 46 fclose(fp); 47 } 48 void main() 49 { 50 51 get_data(); 52 put_data(); 53 54 55 }
第20行 写入文本文档的当前时间要分隔开来%d %d 这样方便读取文本文档中数值的时候能够识别 不然get_data读取的第一个%d就读取了整个时间数值(包括分秒)

浙公网安备 33010602011771号