美文网首页C语言C语言&嵌入式
C语言学习笔记之实战篇

C语言学习笔记之实战篇

作者: xcbyao | 来源:发表于2020-02-16 01:38 被阅读0次

    0x00 前言概览

    学语言基础有了,剩下的就是编程了,没什么可说的,就是不断调试程序,在编程的过程中,一遍又一遍地复习巩固基础知识。此文收集整理了很多案例,能够全部达到独立完成,基本就算是入门了,之后就是尝试一些小项目,进一步提高自己的编程能力,这将是一条学无止境之路……(部分代码有坑,咳咳……)

    0x01 切入正题

    • 怎么获得一个数的百位,十位和个位
    百位数:num/100 因为 int 是整数型,小数部分会省略。比如 765/100 得到7
    十位数:num%100/10。比如765%100先得到65,65/10得到6
    个位数:num%10。比如765%10得到5
    
    • 利用异或 ^ 来交换两个数的值,且不引入其他变量
    #include<stdio.h>
    int main( )
    {
        unsigned int a=60;         //0011 1100
        unsigned int b=13;         //0000 1101
        printf("a=%d,b=%d",a,b);   //输出a,b的值
        printf("\n");
        a=a^b;                     //a=a^b=0011 0001
        b=a^b;                     //b=a^b=0011 1100 相当于b1=(a^b)^b
        a=a^b;                     //a=a^b=0000 1101 相当于a1=(a^b)^((a^b)^b)
        printf("a=%d,b=%d",a,b);   //输出a,b的值
    }
    
    • 利用位与 & 运算,判断一个整数是否为2的整数次幂

    二进制数的位权是以2为底的幂,如果一个整数 m 是 2 的 n 次幂,那么转换为二进制之后只有最高位为 1,其余位置为 0,再观察 m-1 转换为二进制后的形式以及 m&(m-1) 的结果,例如:

    2 --> 0000 0010        1 --> 0000 0001        2&1 --> 0000 0010 & 0000 0001 = 0
    4 --> 0000 0100        3 --> 0000 0011        4&3 --> 0000 0100 & 0000 0011 = 0
    8 --> 0000 1000        7 --> 0000 0111        8&7 --> 0000 1000 & 0000 0111 = 0
    

    可以看出所有的 1 完美的错过了,根据位与的特点可知 m&(m-1) 的结果为 0。
    如果整数 m 不是 2 的 n 次幂,结果会怎样呢?例如 m=9 时:

    9 --> 0000 1001        8 --> 0000 1000        9&8 --> 0000 1001 & 0000 1000 != 0
    利用这一特点,即可判断一个整数是否是2的整数次幂。
    
    #include <stdio.h>
    int num;
    int func(int num)
    {
        if ((num>0)&&(num&(num-1))==0)
        {
            printf("%d是2的整数次幂",num);
        }
        else
        {
            printf("%d不是2的整数次幂",num);
        }
        return ((num>0)&&(num&(num-1))==0); //返回值为 1,则输入的正整数为 2 的整数次幂,返回值为 0 则不是。
    }
    
    int main()
    {
        printf("请输入要查询的数\n");
        scanf("%d",&num);
        func(num);
    }
    
    • 打印九九乘法表
    /*输出9*9口诀。共9行9列,i控制行,j控制列。*/
    #include <stdio.h>
    int main(){
        int i,j,result;
        for (i=1;i<10;i++){
            for(j=1;j<10;j++){
            result=i*j;
            printf("%d*%d=%-3d",i,j,result); /*-3d表示左对齐,占3位*/
            }
        printf("\n"); /*每一行后换行*/
        }
    }
    
    • 打印三角形星星堆
    #include <stdio.h>
    int main()
    {
        int i, j, k;
        for(i=1; i<5; i++)
        {
            /* 观察每行的空格数量,补全循环条件 */
            for(j=i; j<5; j++)  
            {
                printf(" ");    //输出空格
            }
            /* 观察每行*号的数量,补全循环条件 */
            for( k=0;k<2*i-1;k++) 
            {
                printf("*");   //每行输出的*号
            }
            printf("\n");     //每次循环换行
        }
        return 0;
    }
    
    • 用 do while 求算术平方根
    #include <stdio.h>
    double DoSqrt(double z){
        double a=1;
        double b=0;
        double c=0;
        do{
            if(b*b<z){
                b+=a;
            }
            else{
                c=b;
                b-=a;
                a/=10;
            }
        }while(a>0.000001);
    
        return (b+c)/2;
    }
    
    int main(){
        double x, y;
        printf("请输入一个数字:");
        scanf("%lf", &x);
        if(x<0){
            printf("输入错误。");
        } else {
            y=DoSqrt(x);
            printf("%g 的平方根为: %g.\n", x, y);
        }
    
        int z=1;
        do{
            main();
            z++;
        }while(z>10);
    
        return 0;
    }
    
    • 使用for循环打印9×9乘法表
    #include <stdio.h>
    int main() 
    { 
        // 定义相乘数字i,j以及结果result
        int i, j, result;
         for(i=9;i>=1;i--)
         {
            for(j=1;j<=i;j++)
            {
                printf("%d*%d=%d ",i,j,result=i*j);
            }
            printf("\n");
         }
        return 0;
    }
    
    • switch与if语句的应用(计算是该年的第几天)
    #include <stdio.h>
    int main() 
    { 
        /* 定义需要计算的日期 */
        int date = 0;
        int year = 2008;
        int month = 8;
        int day = 8;
    
        switch(month)
        {
            case 12:date+=30;
            case 11:date+=31;
            case 10:date+=30;
            case 9:date+=31;
            case 8:date+=31;
            case 7:date+=30;
            case 6:date+=31;
            case 5:date+=30;
            case 4:date+=31;
            case 3:
    
            if((year%4==0&&year%100!=0)||year%400==0)
            {
                date+=29;
            }
    
            else
            {
                date+=28
            }
            case 2:
            date+=31;
    
            case 1:
            date+=day;
    
            printf("%d年%d月%d日是该年的第%d天",year,month,day,date);
            break;
    
            default:
            printf("error");
            break;
        }
        return 0;
    } //continue只能用在循环体内
    
    • 数组查找功能

    当我们购物之后,拎着购物袋回到家,会一一检查购物袋中的物品看是否缺少或者都是想购之物。
    那么应用到程序中,可以使用数组查找功能,看看是否存在该数据,如果存在并返回该元素的下标。

    #include <stdio.h>
    int getIndex(int arr[5],int value)
    {
        int i;
        int index;
        for(i=0;i<5;i++)
        {
           /* 请完善数组查询功能 */
           if(arr[i]==value)
            {
                index=i;
                break;
            }  
           index=-1;
        }
        return index;
    }
    
    int main()
    {
        int arr[5]={3,12,9,8,6};
        int value = 8;
        int index = getIndex(arr,value);      //这里应该传什么参数呢?
        if(index!=-1)
        {
            printf("%d在数组中存在,下标为:%d\n",value,index);             
        }
        else
        {
            printf("%d在数组中不存在。\n",value);    
        }
        return 0;    
    }
    
    • 分数问题
    #include <stdio.h>
    #define N 10
    //打印分数 
    void printScore(int score[])
    {
        int i;
        printf("\n");
        for(i=0;i<N;i++)
        {
            printf("%d ",score[i]);               
        }
        printf("\n");     
    }
    //计算考试总分 
    int getTotalScore(int score[])
    {
        int sum = 0;
        int i;
        for(i=0;i<N;i++)
        {
            sum+=score[i];                
        } 
        return sum;
    }
    //计算平均分 
    int getAvgScore(int score[])
    {
        return getTotalScore(score)/N;   
    }
    //计算最高分 
    int getMax(int score[])
    {
        int max = -1;
        int i;
        for(i=0;i<N;i++)
        {
            if(score[i]>max)
            {
                max = score[i];              
            }                
        } 
        return max;
    }
    //计算最低分 
    int getMin(int score[])
    {
        int min =100;
        int i;
        for(i=0;i<N;i++)
        {
            if(score[i]< min)
            {
                min = score[i];              
            }                
        } 
        return min;
    }
    //分数降序排序 
    void sort(int score[])
    {
        int i,j;
        for(i=N-2;i>=0;i--)
        {
            for(j=0;j<=i;j++)
            {
                if(score[j]<score[j+1])
                {
                    int temp;
                    temp = score[j];
                    score[j] = score[j+1]; 
                    score[j+1]=temp;                  
                }                 
            }                   
        }
        printScore(score);     
    }
    
    int main()
    {
        int score[N]={67,98,75,63,82,79,81,91,66,84};
        int sum,avg,max,min;
        sum = getTotalScore(score);
        avg = getAvgScore(score);
        max = getMax(score);
        min = getMin(score);
        printf("总分是:%d\n",sum);
        printf("平均分是:%d\n",avg);
        printf("最高分是:%d\n",max);
        printf("最低分是:%d\n",min);
        printf("----------成绩排名---------\n");
        sort(score);
        return 0;    
    }
    
    • 素数问题
    #include<stdio.h>
    #include<math.h>
    int main(){
        int i,j;
        printf("100以内的素数有:\n");
        for(i=2;i<100;i++){
            for(j=2;j<sqrt(i);j++){
                if(i%j==0){
                break;
                }
            }
                if(j>sqrt(i)){
                printf("%d,\t",i);
                }
        }
    }
    
    • 数的阶乘
    #include <stdio.h>
    double factorial(unsigned int i)
    {
       if(i <= 1)
       {
          return 1;
       }
       return i * factorial(i - 1);
    }
    
    int  main()
    {
        int i = 15;
        printf("%d 的阶乘为 %f\n", i, factorial(i));
        return 0;
    }
    
    • 斐波那契数列:
    #include <stdio.h>
    int fibonaci(int i)
    {
       if(i == 0)
       {
          return 0;
       }
       if(i == 1)
       {
          return 1;
       }
       return fibonaci(i-1) + fibonaci(i-2);
    }
     
    int  main()
    {
        int i;
        for (i = 0; i < 10; i++)
        {
           printf("%d\t\n", fibonaci(i));
        }
        return 0;
    }
    
    • 猴子吃桃问题

    猴子第一天摘下N个桃子,当时就吃了一半,还不过瘾,就又多吃了一个。第二天又将剩下的桃子吃掉一半,又多吃了一个。以后每天都吃前一天剩下的一半零一个。到第10天在想吃的时候就剩一个桃子了,问第一天共摘下来多少个桃子?并反向打印每天所剩桃子数。

    #include <stdio.h>
    int getPeachNumber(int n)  
    {
        int num;    
        if(n==10)
        {
           return 1;      
        } 
        else
        {
            num = (getPeachNumber(n+1)+1)*2;  
            printf("第%d天所剩桃子%d个\n", n, num); 
        }
        return num;
    }
    
    int main()
    {
        int num = getPeachNumber(1);
        printf("猴子第一天摘了:%d个桃子。\n", num);
        return 0;
    }
    
    • 年龄问题

    有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人,又说比第2人大两岁。问第2个人,说比第1个人大两岁。最后 问第1个人,他说是10岁。请问第5个人多大?

    程序分析:
    利用递归的方法,递归分为回推和递推两个阶段。要想知道第5个人岁数,需知道第4人的岁数,依次类推,推到第1人(10岁),再往回推。

    #include <stdio.h> 
    int dfs(int n)
    {
        return n == 1 ? 10 : dfs(n - 1) + 2;
    }
    int main() 
    {
        printf("第5个人的年龄是%d岁", dfs(5)); 
        return 0;
    } 
    
    • 打车问题

    北京市出租车打车计费规则如下:
    每公里单价计费2.3元
    起步价13元(包含3公里)
    晚上23点(含)至次日凌晨5点(不含)打车,每公里单价计费加收20%。
    每次乘车加收1元钱的燃油附加税。
    小明每天上下班都要打车,公司和家的距离为12公里,上午上班时间为9点,下午下班时间为6点。 计算小明每天打车的总费用。

    #include <stdio.h>
    float taxifee(int clock,int miles)
    {
        float money;
        if(miles<=3)
        {
            money=14;
            printf("费用为14\n");
        }
        else
        {
            if(clock>=23 || clock<5)
            {
                money=13+1+2.3*(miles-3)*1.2;
                printf("夜间车费为:%f\n",money);
            }
            else
            {
                money=13+1+2.3*(miles-3);
                printf("日间车费为:%f\n",money);
            }
        }
        return money;    
    }
    
    int main()
    {
        printf("打的总费用:%.1f\n",taxifee(9,12)+taxifee(18,12));
        return 0;
    }
    
    • 冒泡排序

    冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。

    过程演示:

    #include <stdio.h>
    void bubble_sort(int arr[], int len) {
        int i, j, temp;
        for (i = 0; i < len - 1; i++)
            for (j = 0; j < len - 1 - i; j++)
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
    }
    int main() {
        int arr[] = { 22, 34, 3, 32, 82, 55, 89, 50, 37, 5, 64, 35, 9, 70 };
        int len = (int) sizeof(arr) / sizeof(*arr);
        bubble_sort(arr, len);
        int i;
        for (i = 0; i < len; i++)
            printf("%d ", arr[i]);
        return 0;
    }
    

    以升序排序为例冒泡排序的思想:相邻元素两两比较,将较大的数字放在后面,直到将所有数字全部排序。就像小学排队时按大小个排一样,将一个同学拉出来和后面的比比,如果高就放后面,一直把队伍排好。

    #include <stdio.h>
    int main()
    {
        double arr[]={1.78, 1.77, 1.82, 1.79, 1.85, 1.75, 1.86, 1.77, 1.81, 1.80};
        int i,j;
        printf("\n************排队前*************\n");
        for(i=0;i<10;i++)
        {
            if(i != 9)   
                printf("%1.2f, ", arr[i]);  //%1.2f表示小数点前一位,小数点后精确到两位
            else
                printf("%1.2f", arr[i]);    //%1.2f表示小数点前一位,小数点后精确到两位
        }
        for(i=8; i>=0; i--)
        {
            for(j=0;j<=i;j++)
            {
                if( arr[j]>arr[j+1])      //当前面的数比后面的数大时
                {
                    double temp;    //定义临时变量temp
                    temp=arr[j];//将前面的数赋值给temp
                    arr[j]=arr[j+1];             //前后之数颠倒位置
                    arr[j+1]=temp;//将较大的数放在后面    
                }                 
            }                
        }
        printf("\n************排队后*************\n");
        for(i=0;i<10;i++)
        {
            if(i != 9)   
                printf("%1.2f, ", arr[i]);  //%1.2f表示小数点前一位,小数点后精确到两位     
            else
                printf("%1.2f", arr[i]);    //%1.2f表示小数点前一位,小数点后精确到两位
        }
        return 0;    
    }
    
    • 选择排序

    选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

    过程演示:

    void swap(int *a,int *b) //交换两个变数
    {
        int temp = *a;
        *a = *b;
        *b = temp;
    }
    void selection_sort(int arr[], int len) 
    {
        int i,j;
        for (i = 0 ; i < len - 1 ; i++) 
        {
            int min = i;
            for (j = i + 1; j < len; j++)     //走访未排序的元素
                if (arr[j] < arr[min])    //找到目前最小值
                    min = j;    //记录最小值
               swap(&arr[min], &arr[i]);    //做交换
        }
    }
    
    • 插入排序

    插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到 {\displaystyle O(1)} {\displaystyle O(1)}的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

    过程演示:

    void insertion_sort(int arr[], int len){
        int i,j,temp;
        for (i=1;i<len;i++){
                temp = arr[i];
                for (j=i;j>0 && arr[j-1]>temp;j--)
                        arr[j] = arr[j-1];
                arr[j] = temp;
        }
    }
    
    • 希尔排序

    希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。

    希尔排序是基于插入排序的以下两点性质而提出改进方法的:

    • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
    • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

    过程演示:

    void shell_sort(int arr[], int len) {
        int gap, i, j;
        int temp;
        for (gap = len >> 1; gap > 0; gap = gap >> 1)
            for (i = gap; i < len; i++) {
                temp = arr[i];
                for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
                    arr[j + gap] = arr[j];
                arr[j + gap] = temp;
            }
    }
    
    • 归并排序

    把数据分为两段,从两段中逐个选最小的元素移入新数据段的末尾,可从上到下或从下到上进行。

    过程演示:

    迭代法
    int min(int x, int y) {
        return x < y ? x : y;
    }
    void merge_sort(int arr[], int len) {
        int* a = arr;
        int* b = (int*) malloc(len * sizeof(int));
        int seg, start;
        for (seg = 1; seg < len; seg += seg) {
            for (start = 0; start < len; start += seg + seg) {
                int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);
                int k = low;
                int start1 = low, end1 = mid;
                int start2 = mid, end2 = high;
                while (start1 < end1 && start2 < end2)
                    b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
                while (start1 < end1)
                    b[k++] = a[start1++];
                while (start2 < end2)
                    b[k++] = a[start2++];
            }
            int* temp = a;
            a = b;
            b = temp;
        }
        if (a != arr) {
            int i;
            for (i = 0; i < len; i++)
                b[i] = a[i];
            b = a;
        }
        free(b);
    }
    
    递归法
    void merge_sort_recursive(int arr[], int reg[], int start, int end) {
        if (start >= end)
            return;
        int len = end - start, mid = (len >> 1) + start;
        int start1 = start, end1 = mid;
        int start2 = mid + 1, end2 = end;
        merge_sort_recursive(arr, reg, start1, end1);
        merge_sort_recursive(arr, reg, start2, end2);
        int k = start;
        while (start1 <= end1 && start2 <= end2)
            reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
        while (start1 <= end1)
            reg[k++] = arr[start1++];
        while (start2 <= end2)
            reg[k++] = arr[start2++];
        for (k = start; k <= end; k++)
            arr[k] = reg[k];
    }
    void merge_sort(int arr[], const int len) {
        int reg[len];
        merge_sort_recursive(arr, reg, 0, len - 1);
    }
    
    • 快速排序

    在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序。

    过程演示:

    迭代法
    typedef struct _Range {
        int start, end;
    } Range;
    Range new_Range(int s, int e) {
        Range r;
        r.start = s;
        r.end = e;
        return r;
    }
    void swap(int *x, int *y) {
        int t = *x;
        *x = *y;
        *y = t;
    }
    void quick_sort(int arr[], const int len) {
        if (len <= 0)
            return; // 避免len等于负值时引发段错误(Segment Fault)
        // r[]模拟列表,p为数量,r[p++]为push,r[--p]为 pop 且取得元素
        Range r[len];
        int p = 0;
        r[p++] = new_Range(0, len - 1);
        while (p) {
            Range range = r[--p];
            if (range.start >= range.end)
                continue;
            int mid = arr[(range.start + range.end) / 2]; // 选取中间点为基准点
            int left = range.start, right = range.end;
            do
            {
                while (arr[left] < mid) ++left;   // 检测基准点左侧是否符合要求
                while (arr[right] > mid) --right; //检测基准点右侧是否符合要求
     
                if (left <= right)
                {
                    swap(&arr[left],&arr[right]);
                    left++;right--;               // 移动指针以继续
                }
            } while (left <= right);
     
            if (range.start < right) r[p++] = new_Range(range.start, right);
            if (range.end > left) r[p++] = new_Range(left, range.end);
        }
    }
    
    递归法
    void swap(int *x, int *y) {
        int t = *x;
        *x = *y;
        *y = t;
    }
    void quick_sort_recursive(int arr[], int start, int end) {
        if (start >= end)
            return;
        int mid = arr[end];
        int left = start, right = end - 1;
        while (left < right) {
            while (arr[left] < mid && left < right)
                left++;
            while (arr[right] >= mid && left < right)
                right--;
            swap(&arr[left], &arr[right]);
        }
        if (arr[left] >= arr[end])
            swap(&arr[left], &arr[end]);
        else
            left++;
        if (left)
            quick_sort_recursive(arr, start, left - 1);
        quick_sort_recursive(arr, left + 1, end);
    }
    void quick_sort(int arr[], int len) {
        quick_sort_recursive(arr, 0, len - 1);
    }
    
    • 猴子排序(Bogo Monkey)

    如果数据稍多的话,几乎是不可能排序好的

    #include <time.h>
    #include <stdlib.h>
    #include <stdbool.h>
    
    void swap(int* x, int* y){
      //交换
      int temporary = *x;
      *x = *y;
      *y = temporary;
    }
    void randomize(int arr[], int length){
      //打乱数组
      for(int i = 0; i < length; i++){
        srand(time(NULL)+i); //引入i增加随机性
        if(rand()%2) swap(&arr[i],&arr[i+1]);
      }
      //printf("!"); //记录打乱次数
    }
    bool isSorted(int arr[], int length){
      for(int i = 0; i < length; i++) if(arr[i]>=arr[i+1]) return false;
      return true;
    }
    void bogoSort(int array[], int length){
      while(!isSorted(array,length)) randomize(array,length);
    }
    
    //Demo:
    #include <stdio.h>
    int main(){
      int numbers[] = {20,9,233,0,-23,7,1,666,4,345,63,45,2,45};
      bogoSort(numbers,14); //也可以改成更小
      for(int i = 0; i < 14; i++) printf("%d,",numbers[i]);
    }
    
    1. 希尔排序缩小递增量必须是要互质的。
    2. 快速排序可以不用交换中间值。
      以下代码仅供参考:
    void Array_Map_Sort_Quickly_Extrem(int* Array, int start, int end)
    {
        int i=start;
        int j=end;
        int Pivot = Array[end];
        if(start<end)
        {
            while(i<j)
            {
                while(i<j &&Array[i]<=Pivot) i++;//Note: i choose the end as parameter
                Array[j]=Array[i];
                while(i<j &&Array[j]>=Pivot) j--;
                Array[i]=Array[j];
            }
            Array[i]= Pivot;
        }
        else 
            return;
        Array_Map_Sort_Quickly_Extrem(Array,start,i-1);
        Array_Map_Sort_Quickly_Extrem(Array,i+1,end);
    }
    

    0x02 References

    《The C Primer Plus》
    C语言教程

    0x03 更多精彩

    C语言学习笔记之基础篇

    CTF系列 1 密码题解密网站总结(必收藏干货)

    CTF系列 0 Crypto-密码学汇总

    xcbyao's 黑色键盘 欢迎叨扰

    相关文章

      网友评论

        本文标题:C语言学习笔记之实战篇

        本文链接:https://www.haomeiwen.com/subject/snqzxhtx.html