《明解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 */
View Code

判断当前编译器下的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的正方形B和一个边长为86的长方形。这个长方形又可以分为一个边长为6的正方形和一个边长为62的长方形。最后这个边长为62的长方形被分为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(&current);
    localtime_s(&local,&current);
    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就读取了整个时间数值(包括分秒)

 

posted @ 2014-11-26 22:17  Daringoo  阅读(1174)  评论(0)    收藏  举报