C语言上

虽然考研初试27号就结束了,现在已经过去了三十多天,但是看了看我只看了七八十页的计算机网络,感觉再浪下去要把自己腿皮断了,开始复试的积极准备阶段吧。

1.31:

下载安装codeblocks并实现调试使用(有一说一这个下载速度是真的sloooooooooooooooooow)
基本等于没干什么事情吧,顺手把巨人第一季下载了,这个倒是费了蛮大功夫的。

2.1:

今日重学了C语言(黄迪明 1`59 直到三目运算符)
下面是废话笔记时间
编程四大阶段:需求分析,设计开发,编写文档,以及最后的维护。
算法:有输入,有输出,有穷性,确定性,高效性。
程序结构:顺序,循环,选择,调用。
标识符:只能由字母,下划线和数字构成,并且第一个字符只能是字母或者下划线。
关键字统称为保留字。

编辑(.c 类型文件)
编译,编译完成后生成.obj 类型文件
连接,连接完成后生成 exe 可执行文件

没有标注的统一认为是有符号数 signed
float 四字节 double 8字节 long double 16字节(真稀奇啊)
char 一个字节

如果一个常量后面加上一个字母u或者U 表示无符号数常量,同样如果加上的是一个l或者L,就表示是一个long型,都加同理。

.56以及96. 都是合理的浮点数表示方式
浮点数通常还可以使用指数的方式来表示,例如1E8,2.5E-6 但是要求E前后必须有数字的同时,指数部分只能为整数。

浮点常量默认为double,如果加上f或者l 则具体变换即可。
至于浮点数在计算机内的具体表示,由于涉及到二进制整数位1的舍弃,所以比较复杂,教材这里并没有详细说明。

C语言编译程序通常会在字符串的末尾自动加上 ‘\0’ 作为字符串的结束标志。

连等赋值不被允许,形如int i=j=k=50;
自增运算符只能用于变量,不可运用于常量和表达式

int main()
{
    int  a=3;
    printf("%d\n",-a++);
    printf("%d\n", a);
}

自增运算符号存在着陷阱,如上代码。首先由于++不可对表达式使用,那么只能是a++再取反,这时候由于++后缀的特性,输出的值最终是-3 第二个输出的a则是4
PS:此特性对C++同理

然后又是垃圾运算符优先级时间,此处直接把隔壁C++的贴过来吧
后置++ --要比前置++ --优先级要高一点,不过应该不会考这种shit东西吧 (好在++i++是违法的)

  1. :: 和()
  2. [] . -> 后置++ 后置-- 类型转换
  3. 前置++ ,前置--,大量一元逻辑运算符号(~)
  4. .* ->*
  5. 乘除取余
  6. 加减
  7. 左移右移
  8. 大于 小于 大于等于 小于等于
  9. 等于和不等于
  10. & ^ |(三者优先级不等嗷 从高到底)
  11. && ||(同上不等
  12. 三元运算符号?: 它后面是一大堆赋值等号懒得写了

值得一提的是 int a=b=c=50: 的确不合法,但是int a,b,c; a=b=c=50是合法的,前者的主要错误只是在于变量的定义和使用现后顺序存在问题。

逻辑运算只在乎最后的结果是不是0 如果是0就是假 否则统一认为是真并将结果作为1

运算顺序总是十分复杂且难以记忆的,总结就是 优先后++ -- 然后是前++ 前--
再然后是 乘除取余 然后加减 然后大小比较 然后等于不等于比较 再然后逻辑运算(其中位逻辑运算大于普通的逻辑运算 且最高的是~ 依次是& ^ | ),最后是赋值运算和其他的高级赋值运算。

补码等于反码+1
按位与 &
按位或 |
按位取反 ~
按位异或 ^

如果i与j进行按位与,但是两个数字的位数不同,i为long j为int,那么会在右端对齐的同时依据j的符号在左侧补上合适的0或者1

异或 指的是 如果两个位相同则为0 如果不同则为1
按位与将特定位置置为0 或则是置为1 异或则可以将特定位取反
因为与1异或 则一定会与原来的只相反 而与0异或则保持原数值不变

位逻辑运算符号针对二进制位,其只可以运用在整型上,
此处指出位逻辑运算符号和逻辑运算符号的区别:

  1. 前者针对二进制位,后者针对表达式,前者只可以运用在整形上,且需要计算具体数值,后者只需要判断真假。
  2. 前者的两个运算对象可以交换,后者由于运算的不完全性,一旦运算出确定结果就不会再向下运算,因此后者运算对象交换会可能会导致程序后续结果输出不同。

2.2&&2.3:

两天早上摸鱼,统一睡到大天亮。
重修C语言之从60~86页第二章测试题
这两天摸鱼比较严重 昨天抽空上了个黄金 今天看了半天大江大河 希望后面几天能加把劲吧 我也是加把劲骑士了。

三目运算符号的运算顺序为从右到左
P60的 例题2-2 是一个很好的例子 首先三目运算符号先算表达式1 然后才会考虑计算表达式2或表达式3 这将导致最后i的值不同,同时注意 i>=j>=k的运算结果。

神奇的逗号表达式 i=35,i2; 该语句i=15 但是整个表达式的值为30 即
j=(i=3 * 5,i * 2)的结果j=30,i=15;

float类型数据,即使是两个float类型数据相加,仍然都将其转化为double类型进行运算。
数据的自动类型转换,其中是针对两个操作数逐步逐步转换的,而不是一次性统一转换,也就是6.0/4+5.5 不等于 6/4+5.5 的原因。

三目运算符号 单目运算符以及赋值运算符都是从右到左,其中务必需要注意的就是三目运算符的运算方向。
可以确定运算方向是从左到右的三个运算符分别是|| && 和,
其可以举一个非常的例子如下:

int main()
{
    int a,b,c;
    a=0;
    b=1;
    c=0;
    if(a++||b++&&c++)
    {
        printf("这个式子成立");
    }
    printf("%d,%d,%d",a,b,c);
}

以及

int main()
{
    int a,b,c;
    a=0;
    b=0;
    c=0;
    if(a++||b++&&c++)
    {
        printf("这个式子成立");
    }
    printf("%d,%d,%d",a,b,c);
}

我们可以明确的看到,如果三者全为0时,只会判断a++和b++,但是b不为0时则会计算c++,我们可以知道c++是最后运算的,如果设置a=1,那么只有a的数值发生了变化,这是由于尽管||运算的优先级没有&&高,但是由于强制从左往右算,其式子等同于i++||(b++&&c++) ,这就是关键,程序依然会先判断i++再考虑运算右侧的内容。

转义字符中 \101 中这个101指的是八进制
putchar() 函数输出int 变量时会自动转化为ascil码

printf函数输出样式 %d %o(八进制) %x(16进制) %u(无符号十进制)
%f (浮点数 自带六位小数) %e (指数形式) %g (数值最小宽度)

修饰符号
l指的是长整型
m指定数据输出的宽度(指的是域宽)
.n 对实数型数据指定输出n位小数或者输出字符串的前n个字符
+表示输出带符号数
- 左对齐
意思也就是如果使用m 指定数据输出的宽度,如果输出宽度不足m 左边补上空格,可以做到右侧对齐的效果,左对齐用- 实现即可,其和C++ left right差不多,都需要注意如果一旦输出实际数字大于指定长度,则无视指定长度。

%g的优势就在于合理的选择%f还是%e,将输出数值的长度做到最小,其中比较愚蠢的东西就是C++的float的精度并做不到完全的精确,正如234.896存储的结果输出是234.895996一样,只是一个近似值而不是完全相等的确切值。

输出格式之 %m.ns 如果n>m那么以n为准确保n位正确输出,同时m>n时同样保证右对齐,并且左侧用空格补齐。

%后面只有XSG可以不区分大小写,我想这么细节的内容应该不会考吧。
如果时prinf内有算式计算,通常是从右往左开始计算。
如果想要输出% 需要连续使用两个%
但是单双引号则是 \' 和 \" 的方式来输出

getchar() 我又想起来当初被这个东西支配的恐惧 这个东西会将空格和回车作为标准输入接受,反正非常让人发指这个东西。

下面这个又是神经病的东西,这本书讲的实在是太细了,给大佬磕头了。这个东西对空格的要求非常奇怪,就是scanf你填充空格的地方没有空格不会出现异常,但是如果没有填充空格的地方你输入空格,就会炸。


注意

其次如果是浮点型数据输入,是不可以使用m.nf的方式来控制输入的,也就是你不可以指定小数点后位数,但是可以通过仅使用m的方式确定小数的输入整个长度,而不能控制其输入的小数点长度。

%*4d 这个 * 用来忽视接下来的输入,也就是这个输入请求被屏蔽。

2.4:

今日完成C语言第二章课后习题
以及第三章部分内容 总的来说看到了103页
规范的指数形式要求 小数部分小数点前有且只有一位非零数字。
且指数形式中,指数部分只能是整数,否则认为不合法。

负数取余运算,这里涉及到一个没讲到的知识点,负数取余C语言的余数和被除数同号,这样同样也可以判断负数除法的机制,其都是向0方向收缩。

三目运算符的优先级大于赋值运算
无论是正负数 移位操作都等于直接乘除对应的值。

scanf 读入%md时 如果读入的数据不足m 则直接忽视m的要求即可

2.5:

今日计划C语言到150~160页 顺便回去看看一直没看的计算机网络 唉

以下代码可以实现循环读入,直到a为234为止跳出循环

void main ()
{
    int a=0;
    for(a=0;a!=234;)
    {
        scanf("%d",&a);
    }
}

九九乘法表很简单 这里就不再赘述
下面是 十进制转化为2进制

void main ()
{
   int a;
   scanf("%d",&a);
   printf("%x\n",a);
   for(int i=0;i<32;i++)
   {
       unsigned int temp=0; //务必记住 这里一定要设置为unsigned 数 
       //否则无法得到正确结果 因为负数移位编译环境不同结果可能不同
       temp=a&0x80000000;
       printf("%1x",temp>>31);
       if(i%4==3)  //此处是为了每四位输出一个空格方便校验
       {
           printf(" ");
       }
       a<<=1;
       //printf(" %x\n",a);
   }
}

当然了 采用标准的公式法也是可以的,但是需要递归。

void function(int a)
{
    int temp=a%2;
    a=a/2;
    if(a!=0)
    {
        function(a);
    }
    printf("%d",temp);
}
void main ()
{
   int a;
   scanf("%d",&a);
   printf("%x\n",a);
   if(a>=0)
   {
        function(a);
   }
   else{
    // 如果是负数有点麻烦 、
    // 需要数组 如果是c++我直接就写上了 以后有空再写吧
   }
}

%04x 表示不足4位用0填充
判断是否在某一位包含特定数字的标准写法

void main ()
{
    int a;
    scanf("%d",&a);
    printf("a=%d\n",a);
    for(int i=0;i<=100;i++)
    {
        int j=i;
        int flag1=1,flag2=1;
        while(flag1&&j>0)  //关键就在这里,%10判断是否相等,不相等再/10 即可
        {
            if(j%10==a)
            {
                flag1=0;
                break;
            }
            j/=10;  
        }
        if(flag1==0)
        {
            j=i;
            j*=j;
            while(flag2&&j>0)
            {
                if(j%10==a)
                {
                    flag2=0;
                    break;
                }
                j/=10;
            }
        }
        if(flag2==0)
        {
            printf("%d, %d\n",i,i*i);
        }
    }
}

下运算为输出十六进制从右到左的第k位

void main ()
{
    int a;
    unsigned b;
    scanf("%d",&a);
    b=(unsigned)a;
    printf("%x\n",b);

    int k;
    scanf("%d",&k);
    b>>=4*(k-1);
    b=b&0xf;
    printf("%x",b);
}

continue语句 将推转出循环结构,忽略continue后面的语句,直接下一次循环,在while语句则之间进去检测控制表达式,而for语句则是直接跳转语句3,然后进行判断

2.6:

显然 2.5号的任务和自己的执行力相比是不切实际的,不知道今天能不能补上2.5号的坑。

goto语句无法实现由循环外跳转到循环内的效果,此不合法。

输出日历例题

#include<stdio.h>
void main ()
{
   int year,one_month,one_day,xingqi;
   scanf("%d %d %d %d",&year,&one_month,&one_day,&xingqi);
   int flag=(year%4==0&&year%100!=0)||(year%400==0);

   int inputmonth;
   printf("---------------------\n");
   scanf("%d",&inputmonth);
   int start=1,end=0;
   int month[12]={31,28+flag,31,30,31,30,31,31,30,31,30,31};
   for(int i=0;i<inputmonth-1;i++)
   {
       start+=month[i];
   }
   for(int i=0;i<inputmonth;i++)
   {
       end+=month[i];
   }
   printf("%d,%d\n",start,end);
   for(int i=0;i<7;i++)
   {
       printf("    星期%d",i+1);
   }
   printf("\n");
   int start_day=(start+xingqi-1)%7;
   if(start_day==0)
   {
       start_day=7;
   }
   printf("%*d",(start_day)*9,1); //这个我想这本书没有教过这个东西的用法,
   //实际上是(start_day)*9是 *的内容 也就是后面两个数据一个用来指定输出宽度
   for(int i=2;i<=end-start+1;i++)
   {
        //
        printf("%9d",i);
        start_day++;
        if(start_day%7==0)
        {
            printf("\n");
        }
   }
}

实际效果:


实际效果

不使用原代码的主要原因是原代码实在是写的太复杂。。。。。

图书管理系统就不写了,实在没什么实现的难度,就不浪费自己时间了。

课后题3.7: 看成加减乘除了 那还是比较麻烦的,通常是用栈来做,但是毕竟不像C++可以直接stack怼脸,还要自己弄,只有加减法就很简单了,如下(好像要求是浮点数,算了就这样吧

void main ()
{
    int sum1;
    char yunsuan;
    scanf("%d",&sum1);
    char oldyunsuan='#';
    while((yunsuan=getchar())!=';')
    {
        //只要运算符号不是;就继续正常运算
        int sum2;
        scanf("%d",&sum2);
        //此处获得两个运算符号
        //printf("%d,%d",sum1,sum2);
        switch(yunsuan)
        {
        case '+':
            sum1+=sum2;
            break;
        case '-':
            sum1-=sum2;
            break;
        }

    }
    printf("%d",sum1);
}

3.8 (下次这种题目还是不写了吧。。)

void main ()
{
   double a;
   scanf("%lf",&a);
   double sum=0;
   for(int i=0;i<=15;i++)
   {
       double sumtempfenmu=1;
       for(int j=1;j<=i;j++)
       {
           sumtempfenmu*=j;
       }
       sum+=pow(a,i)/sumtempfenmu;
   }
   printf("%f",sum);
}

质数 毕竟早就忘了怎么分配动态数组了 大学到底学了个啥玩意
你是不是nt

#include<stdio.h>
void main ()
{
    //经典判断质数
    //去年复试就脑瘫了 1不是质数哥哥
    int a[50];
    int count=0;
    for(int i=2;i<=100;i++)
    {
        if(count==0) //如果count=0显然这个数一定是质数
        {
            a[count++]=i;
        }
        else
        {
            int flag=1;
            for(int j=0;j<count;j++)
            {
                if(i%a[j]==0) //如果没有余数一定不是质数
                {
                    flag=0;
                    break;
                }
            }
            if(flag)
            {
                a[count++]=i;
            }
        }
    }
    for(int i=0;i<count;i++)
    {
        printf("%d\n",a[i]);
    }
}

3.17 移位操作 这题其实有点意思

我是用for循环做到每步移位的 看了一眼答案 其实可以一步到位

#include<stdio.h>
void main ()
{
    int i,j;
    scanf("%d %d",&i,&j);
    printf("%x,%d\n",i,j);
   //为了简单起见我们统一认为i是无符号数 否则何来循环移位这一说
    unsigned int temp=i;
   if(j>0) //将i徐娜换左移
   {
        for(int z=0;z<j;z++) //左移n位
        {
            unsigned int single=temp&0x80000000;
            single>>=31;
            temp<<=1;
            temp=temp|single;
        }
   }
   else //循环右移
   {
        for(int z=0;z<j;z++)
        {
            unsigned int single=temp&0x00000001;
            single<<=31;
            temp>>=1;
            temp=temp|single;
        }
   }
   printf("%x",temp);
}

如果不采用循环则使用下列格式即可

#include<stdio.h>
void main ()
{
    int i,j;
    scanf("%d %d",&i,&j);
    printf("%x,%d\n",i,j);
   //为了简单起见我们统一认为i是无符号数 否则何来循环移位这一说
    unsigned int temp=i;
   if(j>0) //将i徐娜换左移
   {
       //如果循环左移 那么就需要截取最右侧的j位
       //最简单的办法就是将这个东西右移32-j位
       unsigned temp2=temp>>(32-j);
       temp<<=j;
       temp=temp|temp2;
   }
   else //循环右移
   {
        j=-j; //先调整为个正数
        unsigned temp2=temp<<(32-j);
        temp>>=j;
        temp=temp|temp2;
   }
   printf("%x",temp);
}

第三章完结。

2.6日总结:

今日完成了第三章的复习132页 课后题答案在51页到66页 进度还是差很多 明天要出去,带本计网看看吧,电脑就不带了。

2.9:

C语言 黄(153页)
静态数组定义,在定义大小时,大小必须是一个常量表达式,也就是一个整形常量(实际上codeblocks 可以支持非常量实现 vs则不可以)

也就是下面这个东西,本质上时不符合C++/C规范的,但是部分编译器也可以正确运行。

int p;
scanf("%d",&p);
int a[p];

如果初始化数组变量时省略数组大小,则自动默认设置为初始化变量的个数。

二维数组,在全部赋值的情况下,可以省略第一维的定义长度,但是不可以省略第二维的定义长度。

要注意的是,看似这两种赋值方法是等价的,但实际上组成的数组长度是不同的,并且后者如果没有留出给 '\n' 的空间,还会导致编译错误。


image.png

这里要指出的是codeblocks 所用的mingv编译器非常的包容,大概娘化了也肯定是个大姐姐,即使你第二种方法越界了,也不会报编译错误,但是vs则不行,大概是个锉刀吧。

C语言的scanf读入字符串时不能包入类似分隔符和回车符的空白符号。
但是gets则可以成功的读入空格字符,在遇到换行符时则会停止读入。

除此之外,使用gets 或者 scanf中的%s读入字符串时,不需要使用取地址符号,因为数组名本身就代表了数组首地址。

对于printf采用%s格式输出时,则一旦遇到\0符号即立刻停止输出,不论\0之后是否还有有意义内容,而puts则会将字符串结束标记的\0转换为\n,实现自动换行

接下来是大家并不喜闻乐见的字符串处理函数:

  1. 字符串长度测量函数 strlen
    使用方法为 strlen(字符数组名)
  2. 字符串复制函数 strcpy
    即将字符串2的所有字符一个一个复制到字符数组1中,直到遇到结束标志“\0”为止,并且同时也写入结束标志。
    strcpy(A,B) 其会将B的内容复制到A中,

然后是常见错误:
word1=word2 编译错误。

strncpy 可将字符串2的前n个字符复制到字符串1中。
strncpy(字符串1,字符串2,复制的字符个数);

字符串连接函数 strcat
strcat(数组一,数组二) 即取消第一个字符数组中的字符串的结束标志 “\0”,把第二个字符串拼接到第一个字符串后面,并且把拼接的结果存放到第一个字符数组中。也就是整个过程中,第二个数组不会发生变化,而第一个则会成为拼接结果。

字符串比较函数 strcmp
这个东西会将第一个字符串和第二个字符串进行大小的比较:
如果两个字符串相等,则返回0
如果1大于2,则返回一个正整数
如果1小于2,则返回一个负整数

strstr 用来查找一中是否有2的子串
strchr 用来查找一中是否有2这个字符
二者一旦查找到结果,返回第一个匹配对象以及其之后的所有字符。

atof atoi 将字符串转化为浮点数和整数
itoa 将值转化为字符串 特指整型
其有三个参数 第一个为转化的整形数字,第二个为字符数组名,第三个参数为进制。

2.12:

字符串电报转换

#include<stdio.h>
#include<string.h>
void main ()
{
    char worinige[100];
    gets(worinige);
    puts(worinige);
    for(int i=0;i<strlen(worinige);i++)
    {
        if(worinige[i]>='A'&&worinige[i]<='Z')
        {
            //¼´Îª´óд×Öĸ
            worinige[i]=(worinige[i]-'A'+27)*3%52;
        }
        else if(worinige[i]<='z'&&worinige[i]>='a')
        {
            worinige[i]=(worinige[i]-'a'+1)*3%52;
        }
    }
    for(int i=0;i<strlen(worinige);i++)
    {
        if(worinige[i]<=26)
        {
            worinige[i]=worinige[i]-1+'a';
        }
        else
        {
            worinige[i]=worinige[i]-27+'A';
        }
    }
    puts(worinige);
}

定义数组时直接定义变量,并为变量分配了相应的内存空间,定义结构体则类似于定义一个模板。

无法直接用scanf和printf输出结构体变量,但是可以相互之间用等号赋值。

务必要知道的是,如果结构体内有字符数组变量,那么scanf时不需要加上&,这点务须注意。

结构体的初始化:

void main ()
{
   struct wori{
    int x;
    int y;
    }ppp={10,20};
    printf("%d",ppp.x);
}

结构体是可以在函数体内定义的,并不是一定要定义成全局的。

使用哨兵标记法,可以免去查找过程中每一步都要检测数组是否越界的问题。
也就是设置A[0]为x,从后往前依次判断即可。

void main ()
{
   int a[10];
   for(int i=1;i<10;i++)
   {
       scanf("%d",&a[i]);
   }
   int x;
   scanf("%d",&x);
   a[0]=x;
   int i;
   for(i=9;a[i]!=x;i--);
   printf("%d",i);
}

折半查找 折半查找的注意之点在于while循环中判断left和right大小时,二者应当是小于等于的关系。

#include<stdio.h>
#include<string.h>


void main ()
{
   int a[10];
   for(int i=0;i<10;i++)
   {
       scanf("%d",&a[i]);
   }
   int x;
   scanf("%d",&x);
   printf("-----------------------------------------\n");
   int left=0,right=9;
   int mid=(left+right)/2;
   while(left<=right)
   {
        mid=(left+right)/2;
    //    printf("%d",a[mid]);
        if(a[mid]<x)
        {
            left=mid+1;
        }
        else if(a[mid]>x)
        {
            right=mid-1;
        }
        else
        {
            printf("%d is the %d number",x,mid);
            break;
        }
   }
}

直接插入排序
说实话我都忘记了这个东西怎么弄了

#include<stdio.h>
#include<string.h>


void main ()
{
   int a[10];
   for(int i=0;i<10;i++)
   {
       scanf("%d",&a[i]);
   }
   printf("-----------------------------------------\n");

   for(int i=1;i<10;i++)
   {
       int temp=a[i];
       int j;
       //这个东西用来记录当前的值,因为一旦移位就会改变a[i]的值
       for(j=i-1;j>=0;j--)
       {
           if(temp<a[j]) //如果当前值较小
           {
               a[j+1]=a[j];
           }
           else
           {
               break;
           }
       }
       //for循环停止后, 即表明temp大于当前的a[j],那么temp适合的位置就是j+1
       a[j+1]=temp;
   }
   printf("\n");
   for(int i=0;i<10;i++)
   {
       printf("%d ",a[i]);
   }
   printf("\n");
}

这里有一个问题,其实就是哨兵问题,如果设置哨兵,那么a[0]就代替了temp的位置并且同时控制了for循环的结束,因为我比较反感数组不从0开始这个事情,就不写了。

printf() 中%* 用来动态指定输出宽度
scanf() 中%* 用来省略接下来的输入
strstr函数

void main ()
{
    char sp[50];
    char pp[21];
    gets(sp);
    gets(pp);
    puts(sp);;
    puts(pp);
    printf("%s",strstr(sp,pp)); //如果不匹配,则会返回null
}

strstr 字符串匹配
strctr 字符匹配
strcmp 字符比较
strcpy 字符复制
strncpy 字符前n位复制
strcat 字符拼接

字符串比较和赋值
当然了 这个赋值是不必要的,完全可以用结构体的等号直接赋值。

#include<stdio.h>
#include<string.h>

typedef struct student
{
    char name[21];
}student;
void main()
{
    student sts[10];
    for(int i=0;i<10;i++)
    {
        char temp[21];
        scanf("%s",temp);
        int j;
        for(j=i-1;j>=0;j--)
        {
            if(strcmp(temp,sts[j].name)<0)
            {
                strcpy(sts[j+1].name,sts[j].name);
            }
            else
            {
                break; //直接插入排序 务必不要忘记这个break;
            }
        }
        strcpy(sts[j+1].name,temp);
        for(int p=0;p<=i;p++)
        {
            puts(sts[p].name);
        }
        puts("-----------------------");
    }
}

2.13:

第四章习题:
4.2: 开灯

#include<stdio.h>
#include<string.h>
void main()
{
    int a;
    scanf("%d",&a); //输入a=10;
    //因为数组大小必须设置为常量,C++还是写成 int* p=new int[num];
    //这样比较好,但是C语言这样写是不行的,所以这里暂时就不写动态构造数组了。
    int shuzu[10+1]={0};
    for(int i=1;i<=a;i++)
    {
        for(int j=1;j<=a;j++)
        {
            if(i>=j&&i%j==0)
            {
               shuzu[i]++;
            }
        }
        if(shuzu[i]%2==1)
        {
            printf("the %d light is open\n",i);
        }
    }
}

4.3:简单选择排序

#include<stdio.h>
#include<string.h>
void main()
{
    //简单选择排序
    int a[10];
    for(int i=0;i<10;i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i=0;i<10;i++)
    {
        int max=a[0];
        int x=0;
        for(int j=0;j<10-i;j++)
        {
            if(max<a[j])
            {
                max=a[j];
                x=j;
            }
        }
        int temp;
        temp=a[10-i-1];
        a[10-i-1]=max;
        a[x]=temp;
    }
    for(int i=0;i<10;i++)
    {
        printf("%d ",a[i]);
    }
}

4.4: n阶幻方

#include<stdio.h>
#include<string.h>
typedef struct point
{
    int x;
    int y;
}point;
void main()
{
    //4.4 幻方
    //这里在这里我们设置为5 还是那个没有教动态设置数组的问题
    int a[7][7]={0};
    //我们设置为7 答案设置为5 我们设置的大一点
    int n=7;
    point start;
    start.x=0;
    start.y=(0+n-1)/2;
    int now=1;
    while(now<=n*n)
    {

        a[start.x][start.y]=now;
        now++;
        //正常情况下x 应该-- y应当++ 这样才会往右上角移动
        if(start.x==0&&start.y!=n-1)
        {
            //这表示这个点向上越界
            start.x=n-1;
            start.y++;
        }
        else if(start.y==n-1&&start.x!=0) //右侧越界
        {
            start.x--;
            start.y=0;
        }
        else if(start.x==0&&start.y==n-1)
        {
            start.x++;
        }
        else
        {
            printf("%d %d %d\n",start.x,start.y,now);
            if(a[start.x-1][start.y+1]==0) //如果这个点没有数
            {
                start.x--;
                start.y++;
            }
            else
            {
                start.x++;
            }
        }
        for(int i=0;i<=n-1;i++)
        {
            for(int j=0;j<=n-1;j++)
            {
                printf("%4d",a[i][j]);
            }
            printf("\n");
        }
        printf("\n");
    }
    for(int i=0;i<=n-1;i++)
    {
        for(int j=0;j<=n-1;j++)
        {
            printf("%4d",a[i][j]);
        }
        printf("\n");
    }
}

4.5:
这题比较简单,就不写了 要注意的是strlen是不会把\n计算到字符串长度中的,这点还是要注意一下
4.6:
识别空格问题,判断单词个数的问题
这个东西判断的要领就是只要是由空格变为非空格 就算作一个单词
答案的写法我看了半天,大概是统计新空格的个数,他认为每个单词后面都有一个空格,所以要判断最后的字符是不是空格。

#include<stdio.h>
#include<string.h>

void main()
{
    char strings[1000];
    gets(strings);
    int lengths=strlen(strings);
    int flag=1;
    int count=0;
    int i=0;
    //我觉得一开始想的太复杂了
    //我觉得设置flag 只需要统计flag从1变成0的次数就可以了
    while(i<lengths)
    {
        if(strings[i]==' ')  //flag=1变化为0 时计算一个字符 且初值设为1 可以防止开头有空格或者没空格的情况
        {
            flag=1;
        }
        else
        {
            if(flag==1)
            {
                count++;
                flag=0;
            }
        }
        i++;
        //如果结尾有空格 由于flag只会从1变化为0 但是变化不回1 所以不会计数
    }
    printf("%d",count);
}
测试用例

三点共线:

#include<stdio.h>
#include<string.h>
#include<math.h>
typedef struct point{
    double x;
    double y;
}point;
int equal(double x,double y)
{
    if(fabs(x-y)<1e-7)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
void main()
{
   //学生管理系统我们就不写了
   point point1,point2,point3;
   scanf("%lf%lf%lf%lf%lf%lf",&point1.x,&point1.y,&point2.x,&point2.y,&point3.x,&point3.y);
   double length;
   length=sqrt(pow((point1.x-point2.x),2)+pow((point1.y-point2.y),2));
   printf("the length is %f\n",length);
   if(equal(point1.x,point2.x))
   {
       //如果前两个点竖向共线
       //那么就不存在k值,所以就需要判断是否三点共线
       if(equal(point2.x,point3.x))
       {
           printf("a line and shuline\n");
       }
       else
       {
           printf("no a line\n");
       }
   }
   else{
        double k=(point1.y-point2.y)/(point1.x-point2.x);
        double b=point1.y-k*point1.x;
        double temp=point3.x*k+b;
        printf("%f %f\n",k,b);
        if(equal(temp,point3.y))
        {
            printf("a line\n");
        }
        else
        {
            printf("no a line\n");
        }
   }
}

4.10 分子弹问题
要注意分发子弹问题要同步发放。
这个我是用数组来额外存储空间的,但是同样可以只用一个int来代替这个数组。

void main()
{
   int a[10]={10,2,8,22,16,4,10,6,14,20};
   int flag=0;
   int count=0;
   while(flag==0)
   {
       int temp[10];
       for(int i=0;i<10;i++)
       {
           temp[i]=a[i];
       }
       for(int i=0;i<10;i++)
       {
           if(temp[i]%2==1)
           {
               temp[i]++;
           }
       }
       for(int i=0;i<10;i++)
       {
           a[i]=temp[i]/2+temp[(i+1)%10]/2;
       }
       count++;
       flag=1;
       for(int i=0;i<10;i++)
       {
           if(a[i]!=a[(i+1)%10])
           {
               flag=0;
           }
       }
       for(int i=0;i<10;i++)
       {
           printf("%4d",a[i]);
       }
        printf("\n");
   }
   printf("分发次数为%4d\n",count);
}

如果使用int来代替数组,则需要反向思维,从后往前考虑(从前往后则无法实现),从后往前则不需要记录这个数组的内容,代码如下:

void main()
{
   int a[10]={10,2,8,22,16,4,10,6,14,20};
   int flag=0;
   int count=0;
   while(flag==0)
   {
       int temp;
       for(int i=0;i<10;i++)
       {
           if(a[i]%2==1)
           {
               a[i]++;
           }
       }
       //首先重置为偶数才行
       temp=a[9];
       for(int i=9;i>0;i--)
       {
           a[i]=a[i]/2+a[i-1]/2;
       }
       a[0]=a[0]/2+temp/2;
       flag=1;
       count++;
       for(int i=0;i<10;i++)
       {
           if(a[i]!=a[(i+1)%10])
           {
               flag=0;
           }
       }
       for(int i=0;i<10;i++)
       {
           printf("%4d",a[i]);
       }
        printf("\n");
   }
   printf("the count is %4d\n",count);
}

4.11 保龄球问题:
实在不是很能理解这个题目和答案的关系,我个人倾向于原题是这么个逻辑:
(1) 若某一轮的第一次滚球就击倒全部十个柱,则本轮不再滚球。(若是第十轮则还需另加两次滚球)。该轮得分为本次倒柱数 10 与以后两次滚球所击倒柱数之和。
(2) 若某一轮的第一次滚球未击倒十个柱,则可对剩下未倒的柱再滚球一次。如果这两次滚球击倒全部十个柱,则本轮不再滚球(若是第十轮则还需另加一次滚球),该轮得分为本次倒柱数10与以后一次滚球所击倒柱数之和。
(3) 若某一轮的两次滚球未击倒全部十个柱,则本轮不再继续滚球,该轮得分为这两次滚球击倒的柱数这和。
总之,若一轮中一次滚球或两次滚球击倒十个柱,则本轮得分是本轮首次滚球开始的连续三次滚球击倒柱数之和(其中有一次或两次不是本轮滚球)。若一轮内二次滚球击倒柱数不足十个,则本轮得分即为这两次击倒柱数之和。

#include<stdio.h>
#include<string.h>
#include<math.h>

int fequal(double x,double y)
{
    if(fabs(x-y)<1e-7)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

void main()
{
    int score[10]={0};
    int state1=0,state2=0,state=0;
    int nowscore=0;
    for(int i=0;i<10;i++)
    {
        printf("------------this is the %d round-----------\n",i);
        scanf("%d",&nowscore);
        score[i]=nowscore;
        if(state1==2&&state2==2) //如果两个好球 那么就需要加上第三轮的第一个球
        {
            score[i-2]+=nowscore;
        }
        if(state2==2||state2==1) //第二轮球无论是什么 都要加上这个球
        {
            score[i-1]+=nowscore;
        }
        if(nowscore==10)
        {
            state=2;
        }
        else
        {
            scanf("%d",&nowscore);
            score[i]+=nowscore;
            if(score[i]==10)
            {
                state=1;
            }
            else{
                state=0;
            }
            if(state2==2) //上一轮是一个好球,那么就要加上这一轮的两个球
            {
                score[i-1]+=nowscore;
            }
        }
        state1=state2;
        state2=state;

    }
    //前面九轮都得以处理 但是因为第十轮有点问题
    if(state2==1) //这个表示第十轮为成功
    {
        scanf("%d",&nowscore);
        score[9]=score[9]+nowscore;
    }
    else if(state2==2) //这个表示第十轮为好球 奖励两个球
    {
        scanf("%d",&nowscore);
        score[9]+=nowscore;
        if(state1==2) //这个表示第九轮也是好球
        {
            score[8]+=nowscore;
        }
        scanf("%d",&nowscore);
        score[9]+=nowscore;
    }
    int total=0;
    printf("---------------------------\n");
    for(int j=0;j<10;j++)
    {
        printf("%d\n",score[j]);
        total+=score[j];
    }
    printf("%d",total);
}

搞明白了也不是很复杂,就这么大点事情。

4.12: 素数差问题,问题不大过程搞得很复杂,实在没必要
这本书的课后题目多多少少有点问题,大概都是外国教材翻译过来的,这个翻译水平多多少少有点拉。

void main()
{
    int a[2000];
    int count=0;
    a[0]=2;
    count=1;
    for(int i=3;i<2000;i++)
    {
        int flag=1;
        for(int j=0;j<count;j++)
        {
            if(i%a[j]==0) //如果没有余数 就表明不是素数
            {
                flag=0;
            }
        }
        if(flag==1)
        {
            a[count]=i;
            count++;
        }
    }
    int bushu[2000];
    for(int i=0;i<count-1;i++)
    {
        bushu[i]=a[i+1]-a[i];
    }
    for(int i=0;i<count-1;i++)
    {
        printf("%6d",bushu[i]);
        if(i%10==0)
        {
            printf("\n");
        }
    }
    printf("\n");
    for(int i=0;i<count-1;i++)
    {
        int sum=0;
        for(int j=i;j<count-1;j++)
        {
            sum+=bushu[j];
            if(sum==1898)
            {
                printf("start is %d and the end is %d\n",a[i],a[j+1]);
            }
        }
    }
}

第五章:
指针变量的类型指的是指针变量指向的变量的类型,而不是自身的类型。
既然教材里提到 常量指针 我就提一下
const int* p
int* const p
两个区别,第一个是表明*p是常量 也就是p指向的内容是一个常量不可改变,但是p可以指向另外一个常量
第二个是表明p是常量,也就是p自身的内容不可改变,比如p指向了整形a,那么p就不可以再指向别人,但是a的值可以被修改。

p++ 结合顺序由于++ 和 同优先级,并且从右往左,所以原式等于(p++)
如果是v=
p++ 那么由于p先执行++运算 但是并不立刻改变p的值,所以实际上v得到的是p指针指向的变量的值。

而v=++p 则先进行++p 再进行运算,所以将p下一个地址的变量值赋值给v

指针相减运算,两个指向相同数据类型的指针相减,差表示两个指针所指向的数据元素之间所相差的元素个数。
这点可能难以理解,以下实例可以证明:

实例

即两指针的差会自动除以当前变量类型的长度。
设置int* p=a; //a为数组名
p[1]和a[1]是合法且等价的。

2.15:

指针这里有很多问题,当年学的时候就没太搞明白
假设二维数组a[3][4] 那么p=a 等价于p=a[0] 等价于p=&a[0][0]
但是有些东西就很奇葩了,比如说p=&a p=&a[0]的 虽然p的值都完全相同,但是其意义则完全不同。

同时要注意的是,定义多维数组,如果定义数组的同时初始化,最高位可以省略让编译器自动填充,但是后面的位数则不能省略。

int a[][3]={1,2,3,4,5,6,8,9,10,11,12}; //这样是合法的

这里似乎不同编译器执行的结果完全不同,实际上

int a[][3]={1,2,3,4,5,6,8,9,10,11,12};
    int *p1,*p2,*p3,*p4,*p5;
    p1=a;
    p2=a[0];
    p3=&a[0][0];
    p4=&a;
    p5=&a[0];
    printf("%d,%d,%d,%d,%d\n",p1,p2,p3,p4,p5);
    printf("%d,%d,%d,%d,%d\n",p1+1,p2+1,p3+1,p4+1,p5+1);

在mingw里面运行是没什么问题的,但是VS是不能运行的,因为指针赋值类型不同,
按照当前的C标准,这个程序最标准的写法应该是这样的

    int a[][3] = { 1,2,3,4,5,6,8,9,10,11,12 };
    int (*p1)[3];
    int* p2;
    int* p3;
    int (*p4)[4][3] ;
    int (*p5)[3];
    p1 = a; //实际上 a等于&a[0] a等于数组第一个元素的地址
    p5 = &a[0];
    p2 = a[0]; //a[0]等价于 &a[0][0]
    p3 = &a[0][0];
    p4 = &a;
    printf("%d,%d,%d,%d,%d\n", p1, p2, p3, p4, p5);
    printf("%d,%d,%d,%d,%d\n", p1 + 1, p2 + 1, p3 + 1, p4 + 1, p5 + 1);

也就是实际上 a等价于&a[0] a[0]等价于&a[0][0]

如果p是一个结构体指针,那么调用结构体内变量应该使用 (p).data
因为 . 的运算优先级要高于
因此必须要用括号括起来。
如果使用 -> 运算符,就完全可以不用考虑这个括号问题

然后是检测字符串指针内容
while(*p) 只有指向内容是\0时才会判断为假,否则都为真。

2.16:

例题5-10 合并字符串

void main()
{
    //合并字符串
    char a[30],b[30];
    scanf("%s %s",a,b);
    printf("%s\n%s\n",a,b);
    char *pa,*pb;
    pa=a;
    pb=b;
    while(*pa)
    {
        pa++;
    }
    while(*pb)
    {
        *pa++=*pb++;
    }
    *pa='\0';
    printf("%s\n%s\n",a,b);
}

字符串排序 先用冒泡塞了一遍,又用直插塞了回去,大概就是这么个过程吧。

#include<stdio.h>
#include<string.h>
#include<math.h>
void main()
{
    char p[10][15];
    for(int i=0;i<10;i++)
    {
        scanf("%s",p[i]);
    }
    printf("----------------------------- ---------------------------------\n");
    for(int i=0;i<10;i++)
    {
        printf("%15s",p[i]);
    }
    printf("\n----------------------------- ---------------------------------\n");
    for(int i=0;i<9;i++)
    {
        int flag=0;
        //首先是冒泡排序
        for(int j=0;j<10-1-i;j++)
        {
            if(strcmp(p[j],p[j+1])>0)
            {
                char temp[15];
                strcpy(temp,p[j]);
                strcpy(p[j],p[j+1]);
                strcpy(p[j+1],temp);
                flag=1;
            }
        }
        if(flag==0)
        {
            break;
        }
    }
    for(int i=0;i<10;i++)
    {
        printf("%15s",p[i]);
    }
    printf("\n----------------------------- ---------------------------------\n");
    for(int i=1;i<10;i++)
    {
        char temp[15];
        strcpy(temp,p[i]);
        int j;
        for(j=i-1;j>=0;j--)
        {
            if(strcmp(p[j],temp)<0)
            {
                strcpy(p[j+1],p[j]);
            }
            else
            {
                break;
            }
        }
         strcpy(p[j+1],temp);
    }
     for(int i=0;i<10;i++)
    {
        printf("%15s",p[i]);
    }
    printf("\n----------------------------- ---------------------------------\n");
}

简单选择排序本身最大的优势就是较少的交换次数,但是本书为了减少额外的一个存储空间,大大加大了交换次数,实在是.....一言难尽。

删除字符串1中包含的字符串2的字符

#include<stdio.h>
#include<string.h>
#include<math.h>
void main()
{
    char a[30],b[30];
    char *p,*q;
    p=a;
    q=b;
    gets(a);
    gets(b);
    int deletenum=0;
    for(p=a;*p!='\0';p++)
    {
        for(q=b;*q!='\0';q++)
        {
            if(*p!=*q)
            {
                printf("-%c %c-\n",*p,*q);
                //如果两者不相等
                *(p-deletenum)=*p;
            }
            else
            {
                deletenum++;
                break;
            }
        }
    }
    *(p-deletenum)='\0';
    printf("\n%s",a);
}

下面是简化版本

#include<stdio.h>
#include<string.h>
#include<math.h>
void main()
{
    char a[30],b[30];
    char *p,*q;
    p=a;
    q=b;
    gets(a);
    gets(b);
    int deletenum=0;

    for(p=a;*p!='\0';p++)
    {
        int flag=0;
        for(q=b;*q!='\0';q++)
        {
            if(*p==*q)
            {
                deletenum++;
                flag=1;
                break;
            }
        }
        if(flag==0)
        {
            *(p-deletenum)=*p;
        }
    }
    *(p-deletenum)='\0';
    printf("\n%s",a);
}

第五章习题
5.2

void main()
{
    char *point[5]={
        "first",
        "second",
        "third",
        "forth"
    };
    while(*point[2])
    {
        printf("%c",*point[2]++); //这里先执行[] 再执行++ 再执行*
        //因此这个式子等于取数组内的地址,输出值,然后将数组内地址执行”+1“操作
    }
}

5.4

void main()
{
    int data[12]={12,34,56,12,34,56,3,54,6,7,89,12};
    int sum=0;
    int* p=data;
    for(int i=0;i<12;i++)
    {
        sum+=*p++;
    }
    printf("%d",sum);
}

检测字符串2在字符串1中出现的位置和次数
其实没答案里面写的那么复杂,这样就好。

#include<stdio.h>
#include<string.h>
#include<math.h>
void main()
{
    char string1[100];
    char string2[100];
    gets(string1);
    gets(string2);
    char* p1=string1, *p2=string2;
    char* pfind=NULL;
    char* temp=p1;
    int length=strlen(p2);
    int place[100];
    int count=0;
    while(pfind=strstr(temp,p2))
    {
        place[count]=strlen(p1)-strlen(pfind);
        count++;
        temp=pfind+strlen(p2);
    }
    printf("string1 has %d string2\n",count);
    for(int i=0;i<count;i++)
    {
        printf("the %d place is %d\n",i+1,place[i]);
    }
}

比较两个字符串是否相等
额 你直接strcmp不就好了吗
我不写了,有C库函数,真没必要一个一个字符比较。

第六章:
函数不允许嵌套定义
但是可以嵌套调用,这是很浅显的道理。

main函数虽然说不可以被其他函数调用,但是main函数本身实际上是可以调用main函数自身的。

如何省略函数原型:

  1. 如果被调函数的返回值是整形或者是字符型,不必考虑被调函数的位置,
  2. 如果被调函数的定义出现在主调函数之前
  3. 提前说明

C99标准的确可以实现第一条,但是C++ 11之后的标准则不可
同时第一条 如果是char类型作为函数的返回值,依然编译错误

C语言 如果实参数量和形参不匹配,编译不会报错
不过这上面的东西都是一些陈芝麻烂谷子地事情,所以我觉得弄不弄清楚都差不多,反正现在也没人会这样写。

然后参数调用时参数运行的顺序也是没有规定的,大部分编译器统一从右往左算,而最离谱的a++ 以及 ++a 这种东西
++a 这种东西 是算完后,等到最后会从内存里面取数,而a++则是直接将a的运算结果导入,啊,实际上讨论这个东西实在是没什么意义。

 int a=10;
 printf("%d %d %d %d",a++,++a,a,a++);

在mingw 和 vs 中输出的值都为 12 13 13 10
而书上的例子并没有实际意义

2.17:

将数组名称作为参数传递时,其传递的是数组的头地址,而不是整个数组的值传递。

数组名作为函数参数时:

  1. 形参数组和实参数组的类型必须一致,
  2. 形参数组和实参数组的长度可以不相同,因为在调用时,只传送首地址而不检查形参数组的长度。
  3. 因此在函数形参表中,可以不给出形参数组的长度。

传递二维/高维数组时,可以省略第一维的长度。
函数指针
int (*p)(int a,int b)
唯一需要注意的就是函数指针这个括号

汉诺塔递归问题

#include<stdio.h>
#include<stdlib.h>
void HNT(int m,char start,char mid,char end)
{
    if(m==1)
    {
        printf("%c -> %c\n",start,end);
    }
    else
    {
        HNT(m-1,start,end,mid);
        HNT(1,start,mid,end);
        HNT(m-1,mid,start,end);
    }
}
int main()
{
    printf("汉诺塔问题\n");
    int number=4;
    HNT(number,'a','b','c');
}

命令行 argc表示命令行参数的个数,包括可执行程序名本身,argv[] 定义为指向字符串常量的指针数组,数组元素是分别指向命令行中可执行文件名和各个命令行参数字符串的指针。
正如 本章实例所说:



那么argc标注的参数个数为3 而argv字符串指针数组,这个数组内元素的个数为argc+1 第一个表示可执行文件名,后面为输入参数,最后一个为空表示结束。

命令行参数 本书教学部分就讲的不清不白的,不看了(本身也是自选内容)

按序输出五个国家的名称

#include<stdio.h>
#include<stdlib.h>
void sortCountryname(char name[][100],int low,int line)
{
    for(int i=1;i<low;i++)
    {
        char temp[100];
        strcpy(temp,name[i]);
        int j=0;
        for(j=i-1;j>=0;j--)
        {
            if(strcmp(temp,name[j])<0)
            {
                strcpy(name[j+1],name[j]);
            }
            else
            {
                break;
            }
        }
        strcpy(name[j+1],temp);
    }
}
int main()
{
    char countryname[5][100];
    for(int i=0;i<5;i++)
    {
        gets(countryname[i]);
    }
    puts("----------------------------");
    sortCountryname(countryname,5,100);
    for(int i=0;i<5;i++)
    {
        puts(countryname[i]);
    }
}

唉 到底什么时候才会讲动态分配内存,我傻了。

递归函数并不能节省内存空间和提高运行速度。
第六章习题处理
6.3 处理字符串

#include<stdio.h>
#include<stdlib.h>

void produce(char* strings)
{
    int length=strlen(strings);
    int count=0;
    char old=NULL;
    int i;
    for(i=0;i<length;i++)
    {
        if((strings[i]>'0'&&strings[i]<'9')||(strings[i]>'a'&&strings[i]<'z')||(strings[i]>'A'&&strings[i]<'Z'))
        {
            old=strings[i];
            strings[i-count]=strings[i];
        }
        else
        {
            if(strings[i]==old)
            {
                count++;
            }
            else
            {
                strings[i-count]=strings[i];
                old=strings[i];
            }
        }

    }
    strings[i-count]='\0';
}
int main()
{
    char strings[100];
    gets(strings);
    puts(strings);
    produce(strings);
    puts(strings);
}

6.4 百十个位数 乘积

void produce(int num)
{
    int gewei=0;
    int shiwei=0;
    int baiwei=0;
    gewei=num%10;
    shiwei=(num/10)%10;
    baiwei=(num/100)%10;
    int sum=pow(gewei,3)+pow(shiwei,3)+pow(baiwei,3);
    if(sum==num)
    {
        printf("%4d\n",sum);
    }
}

int main()
{
    for(int i=0;i<=999;i++)
    {
        produce(i);
    }
}

6.5 二元一次方程解

#include<stdio.h>
#include<stdlib.h>

void produce(float a,float b,int c)
{

    float det=pow(b,2)-4.0*a*c;
    float x1;
    float x2;
    if(det<0)
    {
        printf("对不起,在实数域内没有合适的解\n");
    }
    else
    {
        det=sqrt(det);
        x1=(-b+det)/(2*a);
        x2=(-b-det)/(2*a);
        printf("x1=%10.4f x2=%10.4f",x1,x2);
    }
}

int main()
{
    float a,b,c;
    scanf("%f %f %f",&a,&b,&c);
    produce(a,b,c);
}

6.6 最小公倍数

#include<stdio.h>
#include<stdlib.h>

int zuixiaogongyueshu(int a,int b)
{
   int p=0;
   while(a%b!=0)
   {
       //如果a%b==0 就代表结束运算
       int temp=a%b;
       a=b;
       b=temp;
   }
   return b;
}
int find(int a,int b)
{
    return a*b/zuixiaogongyueshu(a,b);

}

int main()
{
    int a[5];
    for(int i=0;i<5;i++)
    {
        scanf("%d",&a[i]);
    }
    int temp=a[0];
    for(int i=1;i<5;i++)
    {
        temp=find(temp,a[i]);
        printf("--%d--\n",temp);
    }
    printf("%d",temp);
}

求一到三万的所有质数

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int a[10000];
    int count=0;
    for(int i=2;i<=30000;i++)
    {
        if(count==0)
        {
            a[count]=i;
            count++;
        }
        else
        {
            int flag=0;
            for(int j=0;j<count;j++)
            {
                if(i%a[j]==0)
                {
                    flag=1;
                    break;
                }
            }
            if(flag==0)
            {
                a[count]=i;
                count++;
            }
        }
    }
    int temp=0;
    for(int i=0;i<count;i++)
    {
        if(temp==1&&i%10==9)
        {
            printf("\n");
        }
        else
        {
            temp=1;
            printf("%6d",a[i]);
        }
    }
}

求完备数

#include<stdio.h>
#include<stdlib.h>

void find(int num)
{
    int sum=0;
    int a[100];
    int count=0;
    for(int i=1;i<num;i++)
    {
        if(num%i==0)
        {
            sum+=i;
            a[count]=i;
            count++;
        }
    }
    if(sum==num)
    {
        printf("%6d",num);
        for(int i=0;i<count;i++)
        {
            printf("%6d",a[i]);
        }
        printf("\n");
    }

}
int main()
{
    for(int i=1;i<30000;i++)
    {
        find(i);
    }
}

6.8 互满数

#include<stdio.h>
#include<stdlib.h>

int find(int num)
{
    int sum=0;
    int a[300];
    int count=0;
    for(int i=1;i<num;i++)
    {
        if(num%i==0)
        {
            sum+=i;
            a[count]=i;
            count++;
        }
    }
    return sum;
}
int main()
{
    int p=0;
    for(int i=1;i<30000;i++)
    {
        p=find(i);
        if(i<p&&i==find(p))
        {
            printf("%7d %7d\n",i,p);
        }
    }
}

2.20:

6.11 字符串比较函数

#include<stdio.h>
#include<stdlib.h>
void bijiao(char*a,char *b)
{
    while(*a!='\0'&&*b!='\0')
    {
        if(*a>*b)
        {
            printf("a>b");
            return;
        }
        else if(*a<*b)
        {
            printf("a<b");
            return;
        }
        a++;
        b++;
    }
    if(*a)
    {
        printf("a b 前端完全相等 但是a更长 所以a大");
    }
    else if(*b)
    {
        printf("b更大");
    }
    else
    {
        printf("a==b");
    }
}
int main()
{
    char a[100],b[100];
    gets(a);
    gets(b);
    if(strcmp(a,b)==0)
    {
        printf("a和b相等\n");
    }
    else if(strcmp(a,b)>0)
    {
        printf("a>b\n");
    }
    else
    {
        printf("a<b\n");
    }
    bijiao(a,b);
}

输出组合数 并且输出所有可能的组合
这道题 有点意思 这题需要拿数组做 之前没想起来

#include<stdio.h>
#include<stdlib.h>
int jiecheng(int k)
{
    int sum=1;
    for(int i=1;i<=k;i++)
    {
        sum*=i;
    }
    printf("%4d",sum);
    return sum;
}
void function2(int n,int k,int* a)
{
    if(k>=1)
    {
        for(int i=n;i-k>=0;i--)
        {
            a[k]=i;
            function2(i-1,k-1,a);
        }
    }
    else
    {
        for(int i=a[0];i>=1;i--)
        {
            printf("%4d",a[i]);
        }
        printf("\n");
    }

}
void function(int k,int n)
{
    int p=jiecheng(n)/(jiecheng(k)*jiecheng(n-k));
    printf("共有%d种组合\n",p);
    int a[10];
    a[0]=k;
    function2(n,k,a);
}
int main()
{
   int n;
   int k;
   scanf("%d %d",&n,&k);
   function(k,n);
}

排序输出

#include<stdio.h>
#include<stdlib.h>
void function2(int n,int k,int* a,int *flag)
{
    if(k>=1)
    {
        for(int i=1;i<=n;i++)
        {
            if(flag[i]==0)
            {
                flag[i]=1;
                a[k]=i;
                function2(n,k-1,a,flag);
                flag[i]=0;
            }
            else
            {
                continue;
            }
        }
    }
    else
    {
        for(int i=a[0];i>=1;i--)
        {
            printf("%4d",a[i]);
        }
        printf("\n");
    }
}
void function(int k,int n)
{
    int a[10];
    a[0]=k;
    int flag[10]={0};
    function2(n,k,a,flag);
}
int main()
{
   int n;
   int k;
   scanf("%d %d",&n,&k);
   function(k,n);

}

这里涉及到字符的组合排序问题 下面将拓展这个算法的实际内容
参考内容为:
https://blog.csdn.net/qq_42552533/article/details/88606550?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.control&dist_request_id=d5755e2c-0ba8-445b-9ca8-d4eeb77b9868&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.control

#include<stdio.h>
#include<stdlib.h>
void swap(char* p,char *q)
{
    char temp=*p;
    *p=*q;
    *q=temp;
}
void core_function(char* a,int left,int right)
{
    if(left<right)
    {
        for(int i=left; i<=right; i++)
        {
            swap(&a[left],&a[i]);
            core_function(a,left+1,right);
            swap(&a[left],&a[i]);
        }
    }
    else
    {
        puts(a);
    }

}
void function(char* a,int length)
{
    core_function(a,0,length-1);
}
int main()
{
    char a[40];
    gets(a);
    int length=strlen(a);
    function(a,length);
}

如果要实现含有重复字符的筛查,交换前需要判断,left和right(包括left左边界是否有和right边界相同的元素,如果有,则不发生交换)

#include<stdio.h>
#include<stdlib.h>
void swap(char* p,char *q)
{
    char temp=*p;
    *p=*q;
    *q=temp;
}
int judge_ISorNot_swap(char* a,int left,int right)
{
    int flag=1;
    for(int i=left; i<right; i++)
    {
        if(a[i]==a[right])
        {
            flag=0;
        }
    }
    return flag;
}
void core_function(char* a,int left,int right)
{
    if(left<right)
    {
        for(int i=left; i<=right; i++)
        {
            if(judge_ISorNot_swap(a,left,i))
            {
                swap(&a[left],&a[i]);
                core_function(a,left+1,right);
                swap(&a[left],&a[i]);
            }
        }
    }
    else
    {
        puts(a);
    }

}
void function(char* a,int length)
{
    core_function(a,0,length-1);
}
int main()
{
    char a[40];
    gets(a);
    int length=strlen(a);
    function(a,length);
}

随机数之圆周率

#include<stdio.h>
#include<stdlib.h>

int function()
{


    float x1f=rand()*1.0/RAND_MAX;
    float x2f=rand()*1.0/RAND_MAX;

   // printf("%10f %10f\n",x1f,x2f);
    if(pow(x1f,2)+pow(x2f,2)<1)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
int main()
{
    srand(time(NULL));
    int i=0;
    int sum=0;
    while(i<100000)
    {
        sum+=function();
        i++;
    }
    printf("%f",sum*4.0f/100000);
}

身份证比较 这题不算很难 细节我就不写了
但是要注意的是如果不使用for循环挨个挨个赋值,使用strncpy 务必注意最后加上 \0 否则会粘贴后的字符串没有截断标志,会出现很多问题。

extern 可以用来扩展 全局变量的作用范围
例如m是在程序中部定义的全局变量 使用extern即可将其作用扩展到指定的函数内(该函数比m定义要早)
同时 extern进行引用时,可以不标注变量类型。

同时如果局部变量和外部变量重名,那么外部变量会被屏蔽在函数内无法被访问。

要指出的是 外部变量一定是全局变量。
只有自动变量和形参可以作为寄存器变量,调用时分配寄存器,调用结束后释放寄存器。

这里要指出的是:只要不是在函数内部定义的变量就是外部变量,所以外部变量的定义还是蛮大的,extern关键字只是为了扩大作用域罢了,不是加上extern才叫外部变量,同时static也可以用来修饰外部变量,就是全局静态变量,一旦设置为全局静态变量,那么就只能在当前.c文件内使用,

外部变量定义时如果缺失初试值,缺省值自动设置为0.

静态变量,不论是局部静态变量还是全局静态变量,初始化只会初始化一次,即是定义的那一次会初始化一次初值。
例如下面代码:

void printfx()
{
    static int p=10;
    printf("%d\n",p++);
}
int main()
{
    int i=0;
    while(i<10)
    {
        i++;
        printfx();
    }
}

输出结果从10到19,而不是每次都会执行p=10。

malloc函数的返回值是void* 类型,如果分配成功,函数返回一个指向该区域起始地址的指针,如果内存不够,那么返回NULL。
所以分配动态空间时,需要对指针的类型进行转换,例如:
int* p=(int*)malloc(sizeof(int)*4);

calloc函数和malloc作用相同,但是调用参数有两个,第一个是n表示分配n个空间,第二个是size,表示单个空间的大小。
int* p=(int*)calloc(4,sizeof(int))
分配完内存后,使用free函数即可释放掉动态申请的存储空间,

realloc函数 原型如下
p= realloc(p, size) // void* realloc(void* p, unsigned int size)
作用是重新分配内存,如果后开辟的内存比原内存要大,则完全复制原内存种内容,如果要小,则只复制指定size的内容。
重分配内存之后,后分配的内存起始地址和源地址不一定相同。

2.21:

预处理程序仅仅在当前文件有效
预处理程序中,例如:
#define CESHI "hello my brother! "
CESHI将代表后方的字符串,如果字符串过长,既可以使用\符号来将其扩展到下一行
例如:

#define CESHI "8843e\
886\
221\
dianzikeji"

通过这种在某位增加反斜杠的方式可以拓展长度
宏定义允许嵌套
例如:

#define ONE 1
#define TWO ONE+ONE

TWO代表的就是2
define的进阶用法,参数调用:

#define MAX(a,b) a>b?a:b 
// 不需要指明 a和b的类型
// 不需要强调返回值
// MAX与左括号之间不允许有空格

宏定义时,最好将整个表达式和各个参数用圆括号括起来,否则宏定义可能会产生不可预见的错误,
例如:

#include<stdio.h>
#include<stdlib.h>
#define EVEN(a) a%2==0?1:0
int m=10;
int main()
{
    //显然 按照正常的理解
    //9+1=10那么显然%2==0 所以应当输出1
    printf("%d",EVEN(9+1));
    //但是实际结果是0,因为其最后调用结果是 9+1%2==0?1:0 先计算%再计算+最后计算==的情况下,显然结果不是0
    //所以输出的结果应该是0
}

带参数的宏定义只是程序预处理时进行简单的替换,没有变量类型的要求,可适用于任何类型的参数,
使用宏的优点就是运行速度快,因为在预处理阶段就被展开,省去了函数调用时的开销,但同时也使得程序代码的长度增大,而函数调用则由于函数只会被定义一次,在目标程序中只占用同样的存储空间,节省了存储空间,但是因为调用的时候涉及到参数的传递和返回,现场的保存和恢复,其都需要额外的时间开销。

如果要撤销宏定义,则需要使用#undef 宏标识符
条件编译:
#if
#else
#endif
要注意的是 if 的条件判断式中只能使用常量或者宏替换名组成判断表达式,而不能使用变量,因为其根本就是在编译阶段前进行的条件编译。
同时 条件编译还可以使用
#ifdef
#ifndef
来根据是否已经定义过/未定义过某些宏定义来进行相应的编译处理

任意进制转换 这道例题还是满有价值的,

#include<stdio.h>
#include<stdlib.h>
void function(int convertnum,int mod)
{
    extern char a[];
    if(convertnum/mod==0)
    {
        printf("%c",a[convertnum]);
    }
    else
    {
        function(convertnum/mod,mod);
        printf("%c",a[convertnum%mod]);
    }
}
char a[]="0123456789ABCDEF";
void main()
{

    int convert;
    scanf("%d",&convert);
    puts("-------------------------------------------");
    int mod;
    scanf("%d",&mod);
    function(convert,mod);
    puts("-------------------------------------------");
    printf("%d",0314);
}

额 7.3 7.4 我结合起来随便写了一下
出现了一个编译错误就是 全局变量或者静态变量(带static)的初始化都不可以使用变量
主要原因就是标准C定义静态变量/全局变量的时候,事先在全局内存区分配内存,然后就会立刻进行初始化操作(在编译阶段,即代码执行之前) 这个时候变量还没有确定的值,自然无法完成初始化操作。

// main.c
#include<stdio.h>
#include<stdlib.h>

extern int* data;
extern int num;
void swap(int* p,int* q)
{
    int temp=*q;
    *q=*p;
    *p=temp;
}
void main()
{
    USE();
    int flag=0;
    for(int i=0;i<num-1;i++)
    {
        for(int j=0;j<num-1-i;j++)
        {
            if(data[j]>data[j+1])
            {
                swap(data+j,data+j+1);
                flag=1;
            }
        }
        if(flag==0)
        {
            break;
        }
    }
    for(int i=0;i<20;i++)
    {
        printf("%4d",*(data+i));
    }
}
//------------------------------------------------
//twice.c
//------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
int* data=0;
int num=20;
void USE()
{
    data=calloc(20,sizeof(int));//就是这行代码并不能写在data定义的后面
    for(int i=0;i<20;i++)
    {
        scanf("%d",data+i);
    }

}

看群消息学了一个冷知识

int a,b;
b=(a=9);

此时 b和a的值都是9

2.22:

位域 位域变量C99规定只能是int 或者是 unsigned int
‘这里要提到一个问题,就是*p.a 是先执行.运算然后才执行*运算

位域运算过程中,不允许取一个位域变量的地址
不允许超过整形边界
位域的详细说明链接:
https://blog.csdn.net/yixixi/article/details/2961293 //这个详细描述了位域的规则
https://blog.csdn.net/u010575913/article/details/22886667 //这个举例了一些位域大小的判断例题
下面为复制过来的规则说明:

  1. 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字 段将紧邻前一个字段存储,直到不能容纳为止;

  2. 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字 段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

  3. 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方 式,Dev-C++采取压缩方式;

  4. 如果位域字段之间穿插着非位域字段,则不进行压缩;

  5. 整个结构体的总大小为最宽基本类型成员大小的整数倍。

位域实现奇偶校验位输出:

#include<stdio.h>
#include<stdlib.h>
typedef struct test
{
    unsigned C0:1;
    unsigned C1:1;
    unsigned C2:1;
    unsigned C3:1;
    unsigned C4:1;
    unsigned C5:1;
    unsigned C6:1;
    unsigned C7:1;
}test;
int main()
{
    char num[10]="123456789";
    for(int i=0;i<10;i++)
    {
        test* p=num+i;
        int sum=p->C0+p->C1+p->C2+p->C3+p->C4+p->C5+p->C6+p->C7;
        sum=sum%2;
        printf("%c%d%d%d%d%d%d%d%d%d\n",num[i],sum,p->C0,p->C1,p->C2,p->C3,p->C4,p->C5,p->C6,p->C7);
    }
}

从这里我们可以看出来现在的windows系统中,都是小端存储,即低字节处存放低位信息,例如:
1 ascii码为 49 为00110001
但是本例将字符地址赋值给test型指针后,依次输出(按照地址由低到高)C0 C1 C2 ........结果为10001100
所以可见高位被存储到高位地址,低位被存储到低位地址。

所谓大端和小端,指的就是低地址内存放的是高位字节还是低位字节。

结构体嵌套

#include<stdio.h>
#include<stdlib.h>
typedef struct test
{
    unsigned C0:1;
    unsigned C1:1;
    unsigned C2:1;
    unsigned C3:1;
    unsigned C4:1;
    unsigned C5:1;
    unsigned C6:1;
    unsigned C7:1;
}test;
typedef struct test2
{
    int year;
    int month;
    int day;
}data;
typedef struct test3
{
    data data1;
    int ppp;
}databig;
int main()
{
    databig big={1,2,3,890}; //注意结构体变量的初始化方式。
    printf("%d %d %d %d",big.data1.year,big.data1.month,big.data1.day,big.ppp);
}

联合和结构的主要区别在于:联合类型变量所用的空间不是各个成员所需的存储空间总和,而是联合成员中存储空间最大的成员所要求的字节数。

2.23:

联合
本身这个东西没什么好说的

#include<stdio.h>
#include<stdlib.h>
union test{
    int a;
    double b;
    char c;
};
int main()
{
    union test p={10};;//要注意的是 这种赋值方式是可以的
    //同样这样也是可以的 union test p.a=10
    //但是这样是不可以的 union test p=10;
    printf("%d",p.a);
}

这个厉害了 真的开眼了
利用联合交换指定数据的前后字节

#include<stdio.h>
#include<stdlib.h>
union test{
    int p;
    struct tests{
        char word1;
        char word2;
        char word3;
        char word4;
    }testp;
};
int main()
{
    union test test1,test2;
    int x;
    scanf("%x",&test1.p);
    printf("%x\n",test1.p);
    test2.testp.word1=test1.testp.word4;
    test2.testp.word2=test1.testp.word3;
    test2.testp.word3=test1.testp.word2;
    test2.testp.word4=test1.testp.word1;
    printf("%x\n",test2.p);
}

输出结果:
输出结果

枚举类型:
enum test{black,white,yellow};

#include<stdio.h>
#include<stdlib.h>
enum color{yellow,green,blue,red,black};
int main()
{
    enum color c=yellow;
    enum color p=green;
    enum color s=white; //这一行将不能执行
}

要注意的是 enum 本质上是将 枚举的各个变量依次赋值为 01234......
但如果不实用默认赋值,例如:
enum test{one,two,three,four=9,five,six}
这样会使得枚举变量依次代表0 1 2 9 10 11 12

依次输出枚举变量

enum color{yellow,green,blue=8,red,black};
int main()
{
    enum color p;
    for(p=yellow;p<=black;p++)
    {
        printf("%d",p);
    }
}

输出结果将从0到10,和我原来的预计还是挺不一样的。

C语言的文件系统分为缓冲型文件系统和非缓冲型文件系统
前者的输入输出函数称为流式I/O函数 后者的输出函数成为低级I/O函数

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容