Linux及C基础学习总结

[1]进制转化,原反补码以及综合例题

1>进制加减法

进行加法运算时逢 几 进1,进行减法运算时借1当 几

2>二进制、八进制、十六进制转换为十进制

在这里插入图片描述

3>十进制转换为二进制、八进制、十六进制

整数部分:除 几 取余

在这里插入图片描述

小数部分:乘 几 取整

在这里插入图片描述

4>二进制和八进制,以及二进制和十六进制之间的转换
  • 二—>八:三合一,不够0补

  • 八—>二:一分三

  • 二—>十六:四合一,不够0补

  • 十六—>二:一分四

5>原码、反码、补码

符号位表示:0表示正 、1表示负

  • 正数:原反补码都相同

  • 负数:反码 = 原码符号位不变,其它位取反

    补码 = 反码 + 1

6>综合例题
0x12345678,将该数的第8-10位设置为Ox7,12-14位设置为0x6,其他位保持不变

0001 0010 0011 0100 0101 0110 0111 1000 //原码

0000 0000 0000 0000 0000 0111 0000 0000 //0x7<<8

0001 0010 0011 0100 0101 0111 0111 1000 //GPX2CON = GPX2CON | (0x7<<8)

0000 0000 0000 0000 0111 0000 0000 0000 //0x7<<12

1111 1111 1111 1111 1000 1111 1111 1111 //取反:~( 0x7<<12)

0001 0010 0011 0100 0000 0111 0111 1000 //清零:GPX2CON = GPX2CON & ~(0x7<<12)

0000 0000 0000 0000 0110 0000 0000 0000 //0x6<<12

0001 0010 0011 0100 0110 0111 0111 1000 //置位

GPX2CON = GPX2CON & ~(0x7<<12) | 0x6<<12 = 0x12346778

[2] gcc编译流程

GCC把源文件生成操作系统可以执行的文件需要经历4个过程

在这里插入图片描述

后缀名对应的语言
-c只编译不链接,生成目标文件".o"
-S只编译不汇编,生成汇编代码
-E只进行预编译,不做其他处理
-g在可执行程序种包含标准调试信息
-o file把输出文件输出到file里
-v打印出编译器内部编译各过程的命令行信息和编译器的版本
1> 预处理阶段

去掉注释,加载头文件,代替宏定义,条件编译

条件编译 需要文件:.c文件 生成产物:预处理文件(以.i结尾)

gcc -E [源文件] -o [目标文件]

实例:gcc -E hello.c -o hello.i

后缀名为”.i”的文件是经过预处理后的C原始程序。

2> 编译

​ 编译阶段的主要工作是把我们的源代码生成相应的汇编代码的过程。这个阶段花费的时间会比较长。它需要对我们的C语言进行语法和语义的分析。如果有语法错误,报错,并结束编译过程。如果没有语法错误,还需要优化我们的代码,把C的源程序转变为汇编代码。需要文件:.i文件 生成产物:汇编文件(以.s结尾)。

gcc -S [源文件] -o [目标文件]

实例:gcc -S hello.i -o hello.s

后缀名为”.s”的文件是汇编语言原始程序。

3> 汇编

​ 首先我们应该知道汇编代码(汇编指令)并不是机器能够执行的语言。我们还必须把汇编语言翻译

成计算机能识别的机器语言,这个翻译的过程是在汇编阶段完成的。所以在这个阶段把汇编源文件通过

汇编器生成目标文件(二进制机器语言)。

gcc -c [源文件] -o [目标文件]

实例:gcc -c hello.s -o hello.o

​ C 语言代码经过编译以后,并没有生成最终的可执行文件(.exe 文件),而是生成了一种叫做目标文件(Object File)的中间文件(或者说临时文件)。目标文件也是二进制形式的,它和可执行文件的格式是一样的。对于 Visual C++,目标文件的后缀是.obj;对于 GCC,目标文件的后缀是.o。

4> 链接

​ 把目标文件执行所依赖的所有二进制的其他目标文件及C的库文件都整合成一个可执行文件的过程需要文件:.o文件及各种动态库或静态库

生成产物:可执行程序。

gcc [源文件] -o [目标文件]

实例:gcc hello.o -o hello

​ 汇编只是将我们自己写的代码变成了二进制形式,它还需要和系统组件(比如标准库、动态链接库

等)结合起来, 链接(Link)其实就是一个“打包”的过程,它将所有二进制形式的目标文件和系统组件组合成

一个可执行文件。

[3]运算符优先级

优先级运算符名称或含义使用形式结合方向说明
1[ ]数组下标数组名[ ]左到右
( )圆括号(表达式) /函数名(形参表)
.成员选择(对象)对象.成员名
->成员选择(指针)对象指针->成员名
2-负号运算符-表达式右到左单目运算符
~按位取反~表达式
++自增++变量名、变量名++
自减–变量名、变量名–
*取值运算符*指针变量
&取地址&变量名
!逻辑非!(表达式)
(类型)强制转换(数据类型)表达式
sizeof长度运算符(字节)sizeof(表达式)
3/表达式/表达式左到右双目运算符
*表达式*表达式
%取余表达式%表达式
4+表达式+表达式
-表达式-表达式
5<<左移变量<<(表达式)
>>右移变量>>(表达式)
6>大于表达式>表达式左到右双目运算符
>=大于等于表达式>=表达式
<小于表达式<表达式
<=小于等于表达式<=表达式
7==等于表达式==表达式
!=不等于表达式!=表达式
8&按位与表达式&表达式左到右双目运算符
9^按位异或表达式^表达式
10|按位或表达式|表达式
11&&逻辑与表达式&&表达式
12||逻辑或表达式||表达式
13? :条件运算符表达式1 ? 表达式2 : 表达式3右到左三目运算符
14=赋值变量=表达式右到左
/=除后赋值变量/=表达式
*=乘后赋值变量*=表达式
%=取余后赋值变量%=表达式
+=加后赋值变量+=表达式
-=减后赋值变量-=表达式
<<=左移后赋值变量<<=表达式
>>=右移后赋值变量>>=表达式
&=按位与后赋值变量&=表达式
^=按位异或后赋值变量^=表达式
|=按位或后赋值变量|=表达式
15,逗号表达式1,表达式2,表达式3…左到右

[4]linux下使用sqrt函数问题

程序引用头文件math.h

gcc编译时需要在.c后面加上**-lm**

如:gcc test.c -lm -o test

[5]linux终端左右分屏

  • 1>安装tmux工具:sudo apt-get install tmux

  • 2>使用工具:tmux

  • 3>左右分屏:ctrl + b 再按 %

  • 4>切换屏幕:ctrl + b 再按 o

  • 5>关闭一个终端:ctrl + b 再按x

[6]while循环中printf打印两次问题

原因:按下回车键’\n’被视为字符,同样进入while循环里的语句判断,造成printf重复执行了两次

解决办法:在获取键盘输入后,加入以下代码,消耗内存中的’\n’

while(getchar()!='\n')
    continue;

[7]输出斐波那契数列前n项以及前n项和(函数+递归)

首先介绍一下斐波那契数列:1、1、2、3、5、8、13、21、34、55……

在数学上,斐波那契数列以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)

代码的实现:

/*斐波那契数列第n项*/
int fibonacci(int n){
	/*返回数列第n项,当n>2时,递归利用递推关系算出第n项*/
	return (n==1||n==2)?1:fibonacci(n-1)+fibonacci(n-2); 
}
int main(){
	int n,fn;
	while(1){
		int sum = 0;
		printf("输入项数/(q->exit):");
		scanf("%d",&n);
		if(getchar()=='q')
			break;
		for(int i = 1;i <= n;i++){
			fn = fibonacci(i);
			sum += fn;     /*不建议求和项数太多,因为数列越往后数值会变得非常大,求和会容易溢出(参考下图的代码测试)*/
			printf("%d ",fn);
		}
		printf("\n前%d项和 = %d\n",n,sum);
	}
	return 0;
}

Ubuntu20.04编译运行测试:

在这里插入图片描述

[8]输出二维数组中最大元素以及它的索引

思路:先遍历找出最大元素,再遍历每个元素与最大值比较等于就输出它的值以及索引。

代码的实现

#include <stdio.h>
#include <stdlib.h>
int main(){
    int arr[3][4];
    for(int i = 0;i < 3;i++){
        for(int j = 0;j < 4;j++){
            arr[i][j] = rand()%30 + 1;
        }
    }
    arr[0][3] =28;  /*测试有多个相同最大值*/
    for(int i = 0;i < 3;i++){
        for(int j = 0;j < 4;j++){
            printf("%d\t",arr[i][j]);
        }
        putchar('\n');
    }

    int maxVal = arr[0][0];
    int maxIndex1 = 0;
    int maxIndex2 = 0;
    /*找出最大的元素*/
    for(int i = 0;i < 3;i++){
        for(int j = 0;j < 4;j++){
            if(arr[i][j] > maxVal){
                maxIndex1 = i;
                maxIndex2 = j;
                maxVal = arr[i][j];
            }
        }
    }
    /*判断有多个相同最大值*/
    for(int i = 0;i < 3;i++){
        for(int j = 0;j < 4;j++){
            if(arr[i][j] == maxVal){
                maxIndex1 = i;
                maxIndex2 = j;
                maxVal = arr[i][j];
                printf("最大元素:arr[%d][%d] = %d\n",maxIndex1,maxIndex2,maxVal);
            }
        }
    }
    return 0;
}

[9]冒泡排序(bubble sort)

排序思路:每次循环把最大的元素放到数组的最后面(排序皆是从小到大)。

代码的实现

int temp;
bool flag = false;
for (int i = 0;i < N-1;i++){ 
	/*每次内循环结束把最大的数放到后面N-i-1位置,保证数据从小到大排序*/
    for(int j = 0;j < N-i-1;j++){
    	/*前者大则与后者交换,之后再与后面的值比较*/
        if(arr[j] > arr[j+1]){
            temp = arr[j];
            arr[j] = arr[j+1];
            arr[j+1] = temp;
            /*发生交换则true值给flag*/
            flag = true;
        }
    }
    /*每次排序结束判断,flag值是否发生变化,无变化说明数组已经有序,提前结束循环,节省时间*/
    if(!flag){
        break;
    }else{
        flag = false;
    }
}  

flag的作用:flag是对冒泡排序算法的优化,每次内循环结束都会将长度为N-i-1数组中最大的元素交换到最后面,当内循环结束没有发生数据的交换,说明数组已经是有序的了,此时flag=false,退出循环。

[10]杨辉三角

思路
第一种:利用二维数组;
第二种:利用了它的排列组合的性质:第n行的第m个数可表示为 C(n-1,m-1),即为从n-1个不同元素中取m-1个元素的组合数。

二维数组方法的代码实现:

#include <stdio.h>
/*二维数组法*/
int main(){
    int n;
    printf("输入行数:");
    scanf("%d",&n);
    /*杨辉三角n行有n个元素*/
    int arr[n][n];
    /*三角形两条腰上元素都是1*/
    for(int i = 0;i < n;i++){
        arr[i][0] = arr[i][i] = 1;
    }   
    /*每行从第二个元素开始,都是上一行前一个元素和后一个元素之和*/
    for(int i = 2;i < n;i++){
        for(int j = 1;j < i;j++){
            arr[i][j] = arr[i-1][j-1] + arr[i-1][j];
        }
    }   
    /*打印三角形*/
    for(int i = 0;i < n;i++){
        /*三角形左边的空格*/
        for(int j = 0;j < n-i;j++){
            printf("   ");
        }
        /*跟在空格后打印每行的元素*/
        for(int k = 0;k < i+1;k++){
            printf("%6d",arr[i][k]);
        }
        putchar('\n');
    }   
    return 0;
}       

排列组合法代码的实现:

#include <stdio.h>
/*阶乘*/
long int factorial(int a){
long int p=1;
	for(int i = 1;i <= a;i++){
		p *= i; 
	}
	return p;
}
/*组合C(n,m),n为下标,m为上标*/
int Cn(int n,int m){
	if(m == 1){
		return 1;
	}
	else{
		return factorial(n-1)/(factorial(m-1)*factorial(n-m));
	}
}
/*杨辉三角*/
int main(){
	int n;
	printf("输入行数(1~20):");
	scanf("%d",&n);
	for(int i = 1;i <= n;i++){
		for(int j = 0;j < n-i;j++){
			printf("   ");
		}
		for(int k = 0;k < i;k++){
			printf("%6d",Cn(i,k+1));
		}
		putchar('\n');
	}
	return 0;
}

运行结果:
在这里插入图片描述

[11]约瑟夫环

使用数组法解决约瑟夫环问题
约瑟夫入狱,监狱内共有33个犯人。某日33名犯人围成一圈,从第一个犯人开始报数,报到数字7的犯人出列,被枪毙,
下一名犯人重新从1开始报数。依次类推,直至剩下最后1名犯人可被赦免。聪明的约瑟夫在心里稍加计算,算出了最后枪毙的位置,他站在这个位置,最终避免了自己被枪毙,逃出升天。
问:约瑟夫算出的是哪个位置?

  • 提示:对于约瑟夫环问题来说,需要解决3个问题
    1. 如何解决数组循环的问题?
    2. 如何解决“逢7一杀”这个逻辑?
    3. 如何处理“已死之人”?

代码的实现:

#include <stdio.h>
#define N 33
#define KILL 7
int main(){
    /*定义犯人数组,全部元素置零表示没被噶*/
    int prisoner[N] = {0};
    /*记录被噶了几个犯人*/
    int counter = 0;
    /*数组索引*/
    int index = 0;
    /*报数*/
    int num = 0;
    /*有一个辛存者,所以被噶了32个犯人时结束循环*/
    while(counter != N-1){
        /*如果数组索引=33,则置零再继续*/
        if(index == N){ 
            index = 0;
        }
        /*值为0 ,说明还没噶,要继续报数*/
        if(prisoner[index] == 0){ 
            num++;
            /*报到7的人,噶了*/
            if(num == KILL){
                prisoner[index] = 1;
                /*被嘎人数+1*/
                counter++;
                /*有人被噶,重新从1开始报数*/
                num = 0;
                printf("第%2d轮报数,%2d号位置的人被嘎了\n",counter,index+1);
            }
        }
        index++;
    }   
    /*循环结束,遍历数组,看看约瑟夫站哪了*/
    for(int i = 0;i < N;i++){
        if(prisoner[i] == 0){ 
            printf("约瑟夫在第%d个位置\n",i+1);
        }
    }   
    return 0;
}

运行结果:

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值