BUAA_23级_程设_暑假预习资料_第一讲

C语言作为一种广泛应用的编程语言有着重要的意义,它的出现和发展对计算机科学和软件工程产生了深远的影响,成为了现代编程语言的基础和起点。

C语言是计算机科学的基石,掌握C语言可以让我们更好地理解计算机底层原理,提高编程技能和算法设计能力,并且可以为我们提供更多的工作机会。希望同学们能认真对待C语言的学习,好好利用学长学姐发的资料。

在这里推荐两个视频课,大家可以看视频课,同时参考我们发的资料进行学习。

程序设计入门——C语言_浙江大学_中国大学MOOC(慕课)

【C语言】《带你学C带你飞》_哔哩哔哩_bilibili

在开始下面的学习之前,请先参考Dev-C++的安装与使用.pdf文档安装编译器,并学习程序的编译、运行。

建议大家多动手写程序,多跑代码,可以加深对知识点的理解,更快的掌握

我们在每部分知识点后面,都附上了考察这个知识点的习题(所有的习题都统一放到[第一讲预习赛]这个比赛里了),大家要认真完成噢!

C语言基本程序框架

/*
    This is program of a + b
    e.g. 2 + 6 =8
*/
// Copyright <#year> <author>
#include <stdio.h> //头文件
int main() // 主函数
{
    // 写代码
    int a, b, sum;
    scanf("%d%d", &a, &b);
    sum = a + b;
    printf("%d + %d = %d\n", a, b, sum);
    return 0;
}

在这里插入图片描述

Tips:

在C语言中,每行末尾都要以分号;结尾,表示一句话的结束

C语言的基本数据类型

1.有符号整型

有符号整型的数据类型通常包括intshortlonglong long 四种,因为是有符号类型,所以前面要加上 signed,但是通常省略,也就是说在代码中直接打出 int 类型就代表是有符号类型的。

(1)int类型

数据类型大小是 4 字节,能表示的数值范围是 − 2 31 ∼ 2 31 − 1 -2^{31}\sim 2^{31}-1 2312311 (即 − 2147483648 ∼ 2147483647 -2147483648 \sim 2147483647 21474836482147483647

(2)short类型

数据类型大小是 2 字节,能表示的数值范围是 − 2 15 ∼ 2 15 − 1 -2^{15} \sim 2^{15}-1 2152151 (即 − 32768 ∼ 32767 -32768 \sim 32767 3276832767

(3)long类型

数据类型大小是 4 字节,能表示的数值范围是$-2^{31} \sim 2^{31}-1 $(即 − 2147483648 ∼ 2147483647 -2147483648 \sim 2147483647 21474836482147483647

(4)long long类型

数据类型大小是 8 字节,能表示的数值范围是 − 2 63 ∼ 2 63 − 1 -2^{63} \sim 2^{63}-1 2632631 (这个数足够大了)

2、无符号整型

无符号数用 unsigned 表示 ,只表示数据量,而没有方向(没有正负,且无符号数最高位不是符号位,而就是数的一部分,无符号数不可能是负数。

(1)unsigned int 类型

数据类型大小是 4 字节,能表示的数值范围是 0 ∼ 2 32 − 1 0 \sim 2^{32}-1 02321(即 0 ∼ 4294967295 0\sim4294967295 04294967295

(2)unsigned short 类型

数据类型大小是 2 字节,能表示的数值范围是 0 ∼ 2 8 − 1 0 \sim 2^8 -1 0281 (即 0 ∼ 65535 0\sim65535 065535

(3)unsigned long 类型

数据类型大小是 4 字节,能表示的数值范围是 0 ∼ 2 32 − 1 0 \sim 2^{32}-1 02321 (即 0 ∼ 4294967295 0\sim4294967295 04294967295

(4)unsigned long long 类型

数据类型大小是 8 字节,能表示的数值范围是 0 ∼ 2 64 − 1 0\sim2^{64}-1 02641

2.浮点类型

浮点类型包含:单精度浮点型(float)、双精度浮点型(double)和复数浮点型

3.字符型(char)

字符型变量用于存储一个单一字符,在 C 语言中用 char 表示,其中每个字符变量都会占用 1 个字节。在给字符型变量赋值时,需要用一对英文半角格式的单引号(' ')把字符括起来。

常量

C语言中规定,把一些不可以被改变的值称为常量。只能被访问,被读取,不可以被改,被赋值

1.整型常量

如:018991000
0x2310x12(十六进制)

2.字符常量

字符常量用于表示一个字符,要用英文的单引号(' ')引起来。它可以是数字,英文字母,标点符号以及由转义序列来表示的特殊字符。
示例如下:'g' 'a' '1''\0'

3.实型常量(浮点数常量)

实型常量也称为浮点数常量,也就是小数,可分为float单精度浮点数和double双精度浮点数两种类型。其也可以用指数形式表示,如:3e3(代表3×10的三次方),-1.9e-10(代表-1.9×10的-10次方)

4.字符串常量

字符串常量用于表示一串字符,要用英文的双引号(" ")引起来。
示例如下:"l love BUAA!""Hello world"

5.符号常量

可以通过宏定义用标识符来表示一个常量

#include <stdio.h>
#define MAX 10 // 定义一个名为MAX的常量,使程序中的所有MAX都是值为10的常量
int main()
{
    printf("最大值是:%d", MAX); // 输出“最大值是:10”
    return 0;
}

变量

变量,顾名思义,就是可以改变的量,而每个常量都会有一个名字(即标识符),变量占据内存中一定的存储单元。

使用变量之前必须先定义变量。要注意区分变量名和变量值是两个不同的概念!

变量的定义

  • 一般形式为:变量类型 变量名;

  • 同时定义多个相同类型变量时:变量类型 变量名,变量名,变量名…;

  • 变量的赋值分为两种方式:

    • 1.先声明再赋值

    • 2.声明的同时赋值

    • 注意:变量的值可以随程序的执行进行改变,我们可以通过赋新值来修改变量的值,例如:

      int age = 10; // 声明一个age同时赋值为10
      age = 20;     // 修改age的值为20
      
      char ch = 'A'; // 声明一个ch同时赋值为'A'
      ch = 'B';      // 修改ch的值为字符'B'
      

下面的代码示例 展示了多种声明和赋值变量的形式:

#include <stdio.h>
int main()
{
    int num;     // 声明一个整形变量,名字为num
    num = 100;   // 给num赋值为100

    int a, b, c; // 同时声明多个变量
    a = 10;      // 为变量赋值
    b = 9;
    c = 8;

    int k = 7;         // 声明的同时给变量赋值
    double e = 9.9;    // 小数

    printf("%d", num); // 打印变量num的值,100

    char d = 'A';      // 声明并赋值一个字符
    printf("%c", d);   // 打印变量d的值,A
    
    return 0;
}

**注意:**在定义时不可以给变量合并赋值,如int a=b=c=1是错误的!大家要养成变量初始化的好习惯哦(在定义不确定值的变量时可先赋值为零)

IO格式控制

当我们在编写 C 语言程序时,通常使用 scanf()printf() 函数来实现输入和输出,而IO格式控制用于控制输入和输出的格式。

输入

1.scanf()

scanf() 输入函数,从用户那里接收数据。在程序中使用 scanf() 函数来读取不同类型的输入,如整数、浮点数、字符、字符串等。

2.格式控制符

为了正确读取输入的数据类型,我们需要使用与输入数据类型相匹配的格式控制符。

例如,%d 用于读取整数(int),%f 用于读取浮点数(float),%lf用于读取更精确的浮点数(double),%c 用于读取单个字符(char),%s 用于读取字符串(string)等。

根据需要,使用多个格式控制符来读取多个输入,并将这些输入数据存储到相应的变量中。

3.引用和取地址操作符

在使用 scanf() 函数读取输入时,需要使用变量的引用 & 来获取变量的地址,并将地址传递给 scanf() 函数。

例如,输入一个整数:

int a;
scanf("%d",&a);

这个代码片段的意义是等待用户输入一个整数值,并将输入的值存储在变量 a 中,以便后续在程序中使用。

4.输入多个数据

可以通过在 scanf() 函数中使用多个格式控制符来读取多个输入。

例如,scanf("%d %f", &num, &f) 将会读取一个整数和一个浮点数,并将它们分别存储在变量 numf 中。在格式控制符之间使用空格等分隔符来分开不同的输入。

5.其他格式读入

当输入存在数字又存在除空格和换行以外的字符(因为这些字符无法跳过,影响下一步读入),需在scanf里面添加字符

例如:分数的/,以及坐标的( ,)

scanf("(%d,%d)\n", &x0, &y0); // 输入(x,y),x,y是整数

scanf("%d/%d %d/%d", &a, &b, &c, &d); // 输入a/b c/d形式的坐标,a,b,c,d是整数

scanf("%d\n", &n); // 输入一个整数并换行

scanf("%d%d%d\n", &p, &q, &r); // 连续输出三个整数并换行

输出

1.printf()

printf() 是 输出函数,可以将数据显示在屏幕上或输出到终端

2.格式控制符

与输入一样(%d 表示要输出一个整数,%f 表示要输出一个浮点数,%lf表示输出更精确的浮点数,%c表示要输出一个字符,%s 表示要输出一个字符串)且不需要加取地址符

例如,输出一个整数,两个小数:

#include <stdio.h>
int main()
{
    int a = 3;
    float b = 3.12345678;
    double c = 3.12345678;

    printf("%d\n", a);
    printf("%f\n", b);
    printf("%lf\n", c);

    return 0;
}
3.输出类型
(1)输出变量的值

使用格式控制符来输出变量的值。例如:

#include <stdio.h>
int main()
{
    int num = 10;
    printf("The number is %d", num);
    return 0;
}

将会输出 “The number is 10”。在格式字符串中用 %d 表示整数,并在后面使用变量 num 来替换 %d

(2)输出多个值

我们可以在一个 printf() 函数中输出多个值。例如:

#include <stdio.h>
int main()
{
    int a = 5, b = 10;
    printf("The values is %d and %d", a, b);
    return 0;
}

将会输出 “The values are 5 and 10”。即在格式字符串中,我们可以使用多个格式控制符,并在后面按照顺序传入相应的变量。

(3)转义字符

转义字符是一种特殊的字符序列,用于表示非打印字符、控制字符或具有特殊含义的字符。这些转义字符以反斜杠(\)开头,后面跟着一个或多个字符。

常用转义字符有:

转义字符含义
\"双引号
\’单引号
\\反斜杠
%%百分号
\n换行
\f换页
\r回车
\t水平制表
\v垂直制表

几个小例子:

  • 输出i say :"i love buaa"
#include <stdio.h>
int main()
{
	printf("i say : \"i love buaa\"");
	return 0;
}
  • 输出两行内容
#include <stdio.h>
int main()
{
    printf("Line1\nLine2");
    return 0;
}

将会输出两行,第一行是 “Line 1”,第二行是 “Line 2”。

4.控制输出宽度和精度

可以在格式控制符中使用数字来控制输出的宽度和精度。

(1)控制宽度和精度

%nd 表示输出一个宽度为 n 的整数,如果不够宽,则会使用空格填充。

%.nf 表示输出一个浮点数并保留n位小数。

(2) 最小数字宽度

a. %x.yf , 表示这个浮点数的最小宽度为x,保留y位小数,当宽度不足时在前面补空格(右对齐)

b. %-x.yf , 表示最小宽度为x,保留y位小数,当宽度不足时在后面补上空格(左对齐)

c. %0x.yf , 表示最小宽度为x,保留y位小数,当宽度不足时在前面补上0

(3)不同的进制输出

%o 8进制输出

%x 16进制输出

%e 科学计数法输出

习题

T366301 懒懒大王的受苦日记

T366316 欢迎来到北京学院 II

基本运算

算术运算符

下表显示了 C 语言支持的所有算术运算符。
假设变量 A 的值为 10,变量 B 的值为 20,则:

运算符描述实例
+把两个操作数相加A + B 将得到 30
-从第一个操作数中减去第二个操作数A - B 将得到 -10
*把两个操作数相乘A * B 将得到 200
/分子除以分母B / A 将得到 2
%取模运算符,整除后的余数B % A 将得到 0
++自增运算符,整数值增加 1A++ 将得到 11
自减运算符,整数值减少 1A-- 将得到 9
+=A += B 等价于 A = A + BA += B 将得到 A = 30, B = 20
-=A -= B 等价于 A = A - BA -= B 将得到 A = -10, B = 20

注意:

  • 1、’ % ’ 只能进行整数间的运算,如果前后有浮点数会报错!
  • 2、区分++AA++的区别!
    ++A:先让A加一, 然后拿A去做关系运算等
    A++: 先拿A去做关系运算等,然后让A加一
  • 3、A = A + B 等价于 A += B; 减乘除取余同理

关系运算符

下表显示了 C 语言支持的所有关系运算符。
假设变量 A 的值为 10,变量 B 的值为 20,则:

运算符描述实例
==检查两个操作数的值是否相等,如果相等则条件为真(A == B) 为假
!=检查两个操作数的值是否相等,如果不相等则条件为真(A != B) 为真
>检查左操作数的值是否大于右操作数的值,如果是则条件为真(A > B) 为假
<检查左操作数的值是否小于右操作数的值,如果是则条件为真(A < B) 为真
>=检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真(A >= B) 为假
<=检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真(A <= B) 为真

在C语言中 真为1 假为0

逻辑运算符

下表显示了 C 语言支持的所有关系逻辑运算符。
假设变量 A 的值为 1(真),变量 B 的值为 0(假),则:

运算符描述实例
&&称为逻辑运算符。如果两个操作数都非零,则条件为真。(A && B) 为假
||称为逻辑运算符。如果两个操作数中有任意一个非零,则条件为真。(A|| B) 为真
!称为逻辑运算符。用来逆转操作数的逻辑状态。!(A && B) 为真

运算符优先级

在这里插入图片描述

习题

T366319 大肚的大度

T366379 爱玩游戏的夜阑学姐

ASCII码

当计算机需要处理文本信息时,常常使用ASCII码(美国标准信息交换码)来表示字符。ASCII码是一种字符编码标准,它将每个字符映射到一个唯一的数字值,范围从0到127。

ASCII码有以下的特点和属性

  • ASCII码将可打印字符(如字母、数字、标点符号等)映射到特定的整数值。

  • 表示数字的字符与数字值是不一样的,如 字符 '0' 的ASCII编码是0x30,或十进制的 48,而不是数字0

  • 字符实际上是一个整数(字符的ASCII编码,0~127)

  • 数字字符 0~9,字母a~zA~Z之间的所有字符都是连续编码的,如: '0'⭤ 48, '1'⭤ 49, … ; 'a'⭤ 97, 'b'⭤ 98, …

  • 字符常量可像其它整数一样参与数值运算, 如: '5' 的编码等于 '0'+5'a'+2 等于 'c''8' - '0'等于 8 ( 注意分辨是字符还是数字 )

附ASCII码表

img

试试运行这两段代码,可以加深理解

例一

#include <stdio.h>
int main(int argc, char *argv[])
{
    char x = '0';      // int x = '0';
    printf("%d\n", x); // 输出 48
    printf("%c\n", x); // 输出字符 0
    return 0;
}

在ASCII码中,十进制的48,对应字符0

例二

#include <stdio.h>
int main(int argc, char *argv[])
{
    int ch = 'a';
    printf("%c\n", ch); // 输出字符 a

    ch = 97;            //97是'a'的ASCII码值
    printf("%c\n", ch); // 输出字符 a

    ch = 'a'+1; 
    printf("%c\n", ch); // 输出字符 b

    ch = 'A'+32;
    printf("%c\n", ch); // 输出字符 a

    printf("%d\n", ch); // 输出97
    return 0;
}

第一行,常规输出'a'

第二行,给ch赋值为'a'的ASCII编码值,同样可以直接输出'a'

第三行,'a'+1进行数值运算,得到'b'

第四行,由于'A'的ASCII编码是65,'a'的ASCII编码是97,通过计算65-32=97可以直接将'A'转换成'a',(32是ASCII码中,大写字母和小写字母的差值)

第五行,输出'a'的ASCII码数值

习题

T366313 八卦的夜阑学姐

类型转换

运算对象的类型转换是C语言中十分常见的操作,它往往出现在表达式求值,变量的赋值,以及函数调用时参数的传递等场合。在了解数据类型转换之间的规则前,先来康康C语言中变量的数据类型罢。

一、变量的数据类型

先别被这么多类型吓到!看起来多,实际上加粗部分的char、int、long long、double就足以解决绝大多数的问题力

1、整型
类型数据范围字节数输入输出时的字段说明
char-128~127(对应ASCII码)1%c(以字符形式) / %d(以整型形式)
unsigned char0~2551%c(以字符形式) / %d(以整型形式)
short-32768~327672%hd
unsigned short0~655352%hu
int-2147483648~21474836474%d
unsigned int0~42949672954%u
long long-2的63次方~2的63次方-18%lld
unsigned long long0~2的64次方-18%llu
2、浮点型
类型有效数字字节数输入输出时的字段说明
float7位4%f
double15位8%lf

二、类型转换的方式

细说类型转换之前,先来看看以下几个例子罢

int a = 2;
double b = 3.5;
char c = 'a';           // tip:a的ASCII码值为97
double ans = a * b + c; // 先猜一猜为什么ans要定义为double类型?
#include <stdio.h>
int main()
{
    int a = 2;
    double b = 3.5;
    char c = 'a';
    printf("%lf\n", a * b + c);
    printf("%lf\n", a * (int)b + c);
    printf("%d", a * (int)b + c); // 不妨试运行下这段代码查看结果,想一想为什么输出结果会不一样?
    return 0;
}
#include <stdio.h>
int main()
{
    int a = 2;
    int b = 5;
    double c = b / a;
    printf("%lf", c); // 5/2,结果应是2.5,为什么输出的结果却是2?
    return 0;
}

有没有感觉到数据类型转换的神奇之处?我们在平时进行编程的时候,经常会处理不同数据类型之间的运算,这时我们就需要正确的类型转换来让我们得到正确的结果,减小后续出现bug的可能,真是太好用了!

而所有转换方式都遵从着同一个规则:**数据范围小的类型可以随便转换为数据范围更大的类型,反之则有可能产生数据损失。**就好像容量500ml瓶子中的水可以完美转移到1000ml的瓶子中,而将1000ml瓶子里的水倒入500ml瓶子中,就会损失掉500ml水!唔姆,就这样子记罢:小变大,随便换,大变小,要慎重!(常用的数据类型中,数据范围由小到大分别为,char<int<long long<double

另外,关于浮点型与整型之间的转换,比如double向int进行转换,会将小数点之后的部分抹去,留下整数部分。

下面是几种比较常用的转换方式↓

1、自动类型转换

在实际运算中,编译器会自动进行类型转换,比如我们的第一个例子

int a = 2;
double b = 3.5;
char c = 'a'; // tip:a的ASCII码值为97
double ans = a * b + c;

在运算式a*b+c中,类型转换的过程如下:

先判断a*b,a是int类型,b是double类型,a会自动临时转换为double类型,并计算a*b,结果仍为double类型
再判断a*b+c,a*b是double类型,c是char类型,c会自动临时转换为double类型,并计算a*b+c,结果最后为double类型,所以我们要给ans赋为double类型

可以看出,在自动类型转换过程中,始终遵循着那条规则,int的数据范围小于double,char的数据范围也小于double,故在运算过程中会进行自动类型转换,而这样的变换并不会产生数据损失。

2、赋值转换

我们也可以在给变量赋值的时候就进行类型转换,比如将第一个例子中的ans直接赋成int类型

int a = 2;
double b = 3.5;
char c = 'a'; // tip:a的ASCII码值为97
int ans = a * b + c;

此时,我们已知a*b+c是double类型,将其赋给int类型的ans时,会只将整数部分赋给ans。等下,回想一下口诀:小变大,随便换,大变小,要慎重,这是double的数据范围比int大,大变小,需要我们慎重地具体分析一手:首先我们此时不需要小数部分,可以被舍弃,其次这个数据并没有超过int可表示的最大大小。唔姆,看来可以进行转换口牙!

但是像下面这个赋值就是不可以的

int a = 256;
char b = a;

大变小,要慎重,int的数据范围比char大,还记得char最大能表示多大的数吗,事255,而a刚刚好比255大,所以此时进行赋值转换时就会出现bug!就会出现数据丢失!

3、强制类型转换

强制类型转换,俗称”强转“,算是最为常用的转换手段了,主打一个便捷!好用!爱用!至于为什么叫强制类型转换呢,它主打的就是强制,只管转换,不管后果,如果使用不够熟练,不够得当的话,只会适得其反,出现莫名其妙的新bug

强转的一般形式为:(类型名)(表达式)

还是借助我们的第二个例子

#include <stdio.h>
int main()
{
    int a = 2;
    double b = 3.5;
    char c = 'a';
    printf("%lf\n", a * b + c);
    printf("%lf\n", a * (int)b + c);
    printf("%d", a * (int)b + c); // 不妨试运行下这段代码查看结果,想一想为什么输出结果会不一样?
    return 0;
}

其中a*(int)b+c这个表达式中,我们就对b进行了强转,把b从double类型强制转换成了int类型,从而使得最终表达式的类型变为了int类型(char被自动转换为int)

需要注意的是,(int)(b*a)表示的是先计算a*b,再将其转换成int类型,在第二个例子中结果就是7;而(int)b*a表示的是先将b强转为int,再计算a*(int)b,最终结果为6。
4、关于第三个例子的解释
#include <stdio.h>
int main()
{
    int a = 2;
    int b = 5;
    double c = b / a;
    printf("%lf", c); // 5/2,结果应是2.5,为什么输出的结果却是2?
    return 0;
}

来看double c=b/a这一句,他的类型转换逻辑是这样的

判断b/a,a和b均为int类型,不会进行自动类型转换,于是会进行两个整数的除法运算,在C语言中,整数的除法是整除,即最后的结果是整型,所以b/a才会是2而不是2.5

要想得到正确的结果,就要进行刚刚提到的强转了,将double c=b/a改写为double c=(double)b/a或者double c=b/(double)a即可得到正确结果,不过要注意double c=(double)(b/a)是不行的,它相当于将b/a先变int型再变double型,是无法达到预期目的的。

上面的所有例子仅供参考,希望各位萌新们能从中理解类型转换的规则机制,更好的帮助自己实际编程时遇到的问题,以上!

习题

T366299 小栗帽的“节食”计划

位运算

此内容有些难,暂时理解不了不用急

首先,在讲到位运算之前,我们先要去了解一下二进制的概念:

​ 二进制(binary),是在数学和数字电路中以2为基数的记数系统,是以2为基数代表系统的二进位制。这一系统中,通常用两个不同的符号0(代表零)和1(代表一)来表示 。发现者是莱布尼茨。数字电子电路中,逻辑门的实现直接应用了二进制,现代的计算机和依赖计算机的设备里都使用二进制。每个数字称为一个bit。

​ 二进制是计算机最常用的进制,在前面的学习中我们应该已经了解到了int,unsigned int ,long long , unsigned long long等等数据格式,int的数据范围大小为-2^31 – 2^31-1,unsigned int的数据范围大小为 0 – 2^32 - 1,其中原因便是因为实际上计算机的每个数据都是以二进制来储存的,int数据类型对应4字节也就是32位。

那么下面对一些举个例子:

下面例子中,左边是二进制的形式,括号里是其对应的十进制大小

int类型:

0000 0000 0000 0000 0000 0000 0010 0000 (32)

1000 0000 0000 0000 0000 0000 0001 0000 (-16)

Unsigned int类型:

0000 0000 0000 0000 0000 0000 0010 0000 (32)

1000 0000 0000 0000 0000 0000 0001 0000 (2147483664)

long long类型:

0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010 0000 (32)

1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 (-16)

unsigned long long类型:

0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010 0000 (32)

1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 (9223372036854775824)

在这里我们可以看到同样的一块数据,在不同储存类型下,对应的数字也不尽相同,但这不是重点,接下来我们开始讲重点内容,所谓位运算,就是对一个bit位进行操作。C语言提供了六种位运算符(在下面的所有例子中,为了简化难度,统一数据类型为int。在这里我们一定要明确一个问题,数据在电脑中是以补码形式存储的,所以在下面的负数会从补码还原成原码所代表的数字,而上面的样例中并没有此处理):

运算符&^>><<
说明按位与按位或按位亦或取反右移左移

在这里,浅浅普及一下计算机原码,反码,补码:

​ 计算机以二进制补码的形式存储整数,最高位是符号位,0表示正数,1表示负数,其余位为数字位。
​ (1)原码:将最高位作为符号位(0表示正,1表示负),其它数字位代表数值本身的绝对值的数字表示方式。
​ (2)反码:如果是正数,则表示方法和原码一样;如果是负数,将其绝对值的原码各位取反,则得到这个数字的反码表示形式。
​ (3)补码:如果是正数,则表示方法和原码一样;如果是负数,则将其反码加上1(相当于将原码数值位取反,然后在最低位加1)。
​ 正数的原码、反码、补码完全一样,只有负数需要按照以上规则计算。
​ 0的反码、补码都为0。

&(按位与):

​ 只有参与&运算的两个位都为 1 时,结果才为 1,否则为 0。按位与运算通常用来对某些位清 0,或者保留某些位。

​ 0000 0000 0000 0000 0000 0000 0000 1001 (9)

& 0000 0000 0000 0000 0000 0000 0000 0101 (5)

​ 0000 0000 0000 0000 0000 0000 0000 0001 (1)

|(按位或):

​ 参与|运算的两个二进制位有一个为 1 时,结果就为 1,两个都为 0 时结果才为 0。按位或运算可以用来将某些位置 1,或者保留某些位。

​ 0000 0000 0000 0000 0000 0000 0000 1001 (9)

| 0000 0000 0000 0000 0000 0000 0000 0101 (5)

​ 0000 0000 0000 0000 0000 0000 0000 1101 (13)

^(按位异或):

​ 参与^运算两个二进制位不同时,结果为 1,相同时结果为 0。例如0^1为1,0^0为0,1^1为0。按位异或运算可以用来将某些二进制位反转。

​ 0000 0000 0000 0000 0000 0000 0000 1001 (9)

^ 0000 0000 0000 0000 0000 0000 0000 0101 (5)

​ 0000 0000 0000 0000 0000 0000 0000 1100 (12)

~(取反运算):
​ 取反运算符 ~为单目运算符,右结合性,作用是对参与运算的二进制位 取反。例如 ~1为0, ~0为1
0000 0000 0000 0000 0000 0000 0000 1001 (9)

​ 1111 1111 1111 1111 1111 1111 1111 0110 (-10)

<<(左移运算):

​ 左移运算符<<用来把操作数的各个二进制位全部左移若干位,高位丢弃,低位补0。如果数据较小,被丢弃的高位不包含 1,那么左移 n 位相当于乘以 2 的 n 次方。

​ 0000 0000 0000 0000 0000 0000 0000 1001 (9)

<< 3

​ 0000 0000 0000 0000 0000 0000 0100 1000 (72)

>>(右移运算):

​ 右移运算符>>用来把操作数的各个二进制位全部右移若干位,低位丢弃,高位补 0 或 1。如果数据的最高位是 0,那么就补 0;如果最高位是1,那么就补 1。如果被丢弃的低位不包含 1,那么右移 n 位相当于除以 2 的 n 次方(但被移除的位中经常会包含 1)

​ 0000 0000 0000 0000 0000 0000 0000 1001 (9)

>> 3

​ 0000 0000 0000 0000 0000 0000 0000 0001 (1)

代码实例:

#include <stdio.h>
int main()
{
    int a, b, c, d;
    scanf("%d%d%d%d", &a, &b, &c, &d); // 输入中a为第一个数字,b为第二个数字,c为左移位数,d为右移位数
    printf("and : %d\n", a & b);       // 按位与
    printf("or : %d\n", a | b);        // 按位或
    printf("xor : %d\n", a ^ b);       // 按位异或
    printf("inv : %d\n", ~a);          // 取反
    printf("shl : %d\n", a << c);      // 左移
    printf("shr : %d\n", a >> d);      // 右移
    return 0;
}

​ 在上述代码中按顺序输入9 5 3 3便可以得到与上述例子相同的答案。

位运算用途:

在这里我们将简单介绍一些位运算的最基本用途:

按位与:

​ 按位与运算通常用来对某些位清 0,或者保留某些位。例如要把 n 的高 16 位清 0 ,保留低 16 位,可以进行n & 0XFFFF运算(0XFFFF 在内存中的存储形式为 0000 0000 0000 0000 1111 1111 1111 1111)。

#include <stdio.h>

int main()
{
    int n;
    scanf("%d",&n); //这里要输入一个很大的数
    printf("%d\n",n & 0XFFFF);
    return 0;
}
按位或:

​ 按位或运算可以用来将某些位置 1,或者保留某些位。例如要把 n 的高 16 位置 1,保留低 16 位,可以进行n | 0XFFFF0000运算(0XFFFF0000 在内存中的存储形式为 1111 1111 1111 1111 0000 0000 0000 0000)。

#include <stdio.h>

int main()
{
    int n;
    scanf("%d",&n); //这里n可以是一个很小的数
    printf("%d\n",n | 0XFFFF0000);
    return 0;
}
按位异或:

​ 按位异或运算可以用来将某些二进制位反转。例如要把 n 的高 16 位反转,保留低 16 位,可以进行n ^ 0XFFFF0000运算(0XFFFF0000 在内存中的存储形式为 1111 1111 1111 1111 0000 0000 0000 0000)。

需要注意的是,数据经过两次异或后可以还原得到原始数据

#include <stdio.h>

int main()
{
    int n;
    scanf("%d", &n);
    printf("%d\n",n ^ 0XFFFF0000);
    n = n ^ 0XFFFF0000;
    printf("%d\n",n ^ 0XFFFF0000);
    return 0;
}

补充一点异或的性质:

  1. 交换律: p   ⊕   q = p   ⊕   q p \ {\oplus} \ q = p \ {\oplus} \ q p  q=p  q
  2. 结合律: p   ⊕   ( q   ⊕   r ) = ( p   ⊕   q )   ⊕   r p \ {\oplus} \ (q \ {\oplus} \ r) = (p \ {\oplus} \ q) \ {\oplus} \ r p  (q  r)=(p  q)  r
  3. 恒等率: p   ⊕   0 = p p \ {\oplus} \ 0 = p p  0=p
  4. 归零率: p   ⊕   q   ⊕   q = p   ⊕   0 = p p \ {\oplus} \ q \ {\oplus} \ q = p \ {\oplus} \ 0 = p p  q  q=p  0=p
左移/右移

​ 如果数据较小且不为负数,即被丢弃的高位不包含 1,那么左移 n 位相当于乘以 2 的 n 次方。如果被丢弃的低位不包含 1,那么右移 n 位相当于除以 2 的 n 次方(但实际上/运算也会抛弃余数)

#include <stdio.h>

int main()
{
    int n;
    scanf("%d",&n);//这里可以尝试输入各种数字,看看哪些数字符合要求
    if(((n << 2) != (n * 4)) || ((n >> 2) != (n/4))){
        printf("error!\n");
    }
    else{
        printf("right!\n");
    }
    return 0;
}

​ 位运算是一个很重要的操作,希望大家在了解位运算符号使用方式的过程中去理解并掌握对二进制数据的各种处理,这将对大家以后学习专业课有所帮助。

习题

T366314 位运算加密挑战

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值