c语言基础语法
进制、原码、反码、补码、输入输出函数
进制
1、十进制转二进制
如:666 转成二进制,采用除二反向取余,每次除以二,若每次可以被2整除,则为0,若无法整除,则取余为1,余数继续除二,直到为0
则得到结果:1010011010, 代码实现如下:
#include
int main(void){
int i=0,n,a[32];
printf("请输入一个十进制整数.\n");
scanf("%d",&n);
// 除二反向取余
while (n>0)
{
a[i]=n%2;
i=i+1;
n=n/2;
}
printf("十进制整数转换为二进制数是:\n");
for(i--;i>=0;i--)
printf("%d",a[i]);
printf("\n");
return 0;
}
2、二进制转十进制
采用了当取幂,与之相乘的算法:如:110101
则从右往左为低为到高位,则该十进制数为:
代码实现如下:
#include
int main(){
char ch;
int a=0;
//遍历获取
while(ch=getchar(),ch!='\n')
a=a*2+(ch-'0');
printf("%d\n",a);
return 0;
}
3、八进制转二进制
方法:展开法
056:5 ==》 101, 6 ==》110 则:
101110
05326:5==》101, 3==》011,2==》010,6==》110
101011010110
4、八进制转十进制
采用类似二进制转十进制的方法。
如1507:
则
5、十六进制转十进制
16进制数的第0位的权值为16的0次方,第1位的权值为16的1次方,第2位的权值为16的2次方……
所以,在第N(N从0开始)位上,如果是数 X (X 大于等于0,并且X小于等于 15,即:F)表示的大小为 X * 16的N次方。
例:2AF5换算成10进制:
用竖式计算:
第0位:5 * 16^0 = 5
第1位:F * 16^1 = 240
第2位:A * 16^2= 2560
第3位:2 * 16^3 = 8192
直接计算就是:
5 * 16^0 + F * 16^1 + A * 16^2 + 2 * 16^3 = 10997
6、十六进制转二进制
由于在二进制的表示方法中,每四位所表示的数的最大值对应16进制的15,即16进制每一位上最大值,所以,我们可以得出简便的转换方法,将16进制上每一位分别对应二进制上四位进行转换,即得所求:
例:2AF5换算成2进制:
第0位:(5)16 = (0101) 2
第1位:(F)16 = (1111) 2
第2位:(A) 16 = (1010) 2
第3位:(2) 16 = (0010) 2
得:(2AF5)16=(0010.1010.1111.0101)2
代码输出方法
C语言的格式化输出能直接将十进制数转成8进制或者16进制:
#include
int main(void){
int a = 156;
printf("10进制显示:%d\n",a);
printf("8进制显示:%o\n",a);
printf("16进制显示:%x\n",a);
return 0;
}也可以使用十六进制数转成十进制或者八进制:
#include
int main(void){
int b = 0x4c;
printf("10进制显示:%d\n",b);
printf("8进制显示:%o\n",b);
printf("16进制显示:%x\n",b);
return 0;
}
内存数值存储方式
预备知识:
一个Bit位即为一个二进制位
一个字节1B = 8Bit
1KB = 1024B
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1、机器数
一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.
比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011 。
那么,这里的 00000011 和 10000011 就是机器数。
2、真值
因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1
一、原码
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:
原码是人可直接理解最简单的二进制码的理解方式。
[+1] ==》 原 = 0000 0001
[-1] ==》 原 = 1000 0001
区别在最开头的0和1的区别0表示正数,1表示复数
第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:
[1111 1111 , 0111 1111] == [-127 , 127]
二、反码
反码的表示方法是:
正数的反码是其本身
负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
[43] = [00101011]原 = [00101011]反
[-43] = [10101011]原 = [11010100]反
三、补码(重要)
补码是现今计算机采用的存储形式。
- 对于正数,原码、反码、补码相同。
- 对于负数,其补码为它的反码加1。
- 补码符号位不动,其他其他位求反,最后整个数加1,得到原码。
如下例子:
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
补码的意义
我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.
于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:
计算十进制的表达式: 1-1=0
但是计算由原码如下,用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.
1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
为了解决原码做减法的问题, 出现了反码:
计算十进制的表达式: 1-1=0
1 - 1 = 1 + (-1)
= [0000 0001]原 + [1000 0001]原
= [0000 0001]反 + [1111 1110]反
= [1111 1111]反 = [1000 0000]原
= -0
发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.
于是补码的出现, 解决了0的符号以及两个编码的问题:
1-1 = 1 + (-1)
= [0000 0001]原 + [1000 0001]原
= [0000 0001]补 + [1111 1111]补
= [0000 0000]补
=[0000 0000]原
-1-127的结果应该是-128, 在用补码运算的结果中, [1000 0000]补 就是-128. 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的)
使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].
因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-231, 231-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值。
补码计算例子
43 - 27 = 43 + (-27)
= [00011011]原 + [10011011]原
= [00011011]补 + [11100101]补
= [00010000]补 = [00010000]原
= 15
四、数据溢出
当出现计算溢出的时候:
两正数加,变负数,上溢(大于机器所能表示的最大数)
两负数加,变正数,下溢(小于机器所能表示的最小数)
一般是char类型的溢出:
#include
int main(void){
char ch = 127;
printf("ch = %d\n",ch);
ch = 127 + 1;
printf("ch + 1 = %d\n",ch);
ch = 127 + 2;
printf("ch + 2 = %d\n",ch);
return 0;
}
因为char类型本身的数据容量很少,只有8Bit的长度,所以很容易出现数据溢出的现象。
五、类型限定符
extern
表示声明,没有内存空间,无法提升。
const
限定一个变量变为只读变量。对象的类型如果采用了限定符 const,则该对象就是常量。在定义该对象之后,程序无法修改它。
volatile
防止编译器优化代码。对象的类型如果采用了限定符 volatile,则该对象可以被其他进程或事件修改。关键字 volatile 告诉编译器在每次使用该对象值时,都要重新读取它,即便程序本身自上一次获取后再没有修改过它的值。这种限定符通常用于硬件接口编程,以防止变量值被外部事件修改后,未能及时更新。
register
定义一个寄存器变量。
输入输出的一些知识点
一、putchar与getchar函数
getchar() 用于读取用户从键盘输入的单个字符,它有一个整型的返回值,当发生读取错误的时候,返回整型值-1,当读取正确的时候,它会返回用户从键盘输的第一个字符的ASCII码,当程序调用getchar时,运行程序时就等着用户从按键输入,用户输入的字符被存放在键盘缓冲区中.
直到用户按回车为止(回车字符也放在缓冲区中),当用户键入回车之后,getchar才开始从输入流中每次读入一个字符,输入的字符不只一个的时候,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完之后,才等待用户按键,getchar函数输入数字也按字符处理,单个的getchar函数输入多于一个字符时,只接收第一个字符。
putchar()向终端输出一个字符。其格式为putchar(ch),其中ch可以是被单引号(英文状态下)引起来的一个字符,可以是介于0~127之间的一个十进制整型数(包含0和127)(超过127就不是ASCII码了),也可以是事先用char定义好的一个字符型变量当c为一个被单引号(英文状态下)引起来的字符时,输出该字符(注:该字符也可为转义字符),当c为一个介于0~127(包括0及127)之间的十进制整型数时,它会被视为对应字符的ASCII代码,输出该ASCII代码对应的字符;
当c为一个事先用char定义好的字符型变量时,输出该变量所指向的字符。当整型变量ch超出8位变量的范围时,ch则会变强制转化为8位变量(即取其低八位传过去输出),当为负数的时候,由于计算机存储负数是用补码表示的,所以传过去的二进制补码也被当做正数处理,也是取其低八位
getchar()和putchar()函数包含在头文件stdio.h中,使用时需包含此头文件
#include
int main(void){
char ch;
ch = getchar();
printf("输出getchar()所输入的数据:\n");
putchar(ch);
return 0;
}
2、scanf函数
scanf 的功能用一句话来概括就是“通过键盘给程序中的变量赋值”。
scanf("输入控制符", 多个参数);
功能:将从键盘输入的字符转化为“输入控制符”所规定格式的数据,然后存入以输入参数的值为地址的变量中。
#include
int main(void){
int a;
printf("输入a =");
scanf("%d",&a);
/* &表示取出变量a的地址,描述a的空间在哪
%d 则是表示该a是以该格式存入,输入浮点数也将以整数形式
*/
printf("a = %d\n",a);
// printf("输入ch =\n");
// char ch1,ch2,ch3;
// scanf("%c%c%c",&ch1,&ch2,&ch3);
// printf("ch = %c\n",ch1);
// printf("ch = %c\n",ch2);
// printf("ch = %c\n",ch3);
return 0;
}多次输入的话,可以同时多数据:但是只能接受指定的数据量
scanf("输入控制符", 数组参数);
允许数组格式接受整串
#include
int main(void){
char str[10];
/*定义一个数组,用来接收用户输入的字符串。
但是注意最多只能10字节,超出会无法接受多余的数据
但是一般情况c程序会补充栈*/
scanf("%s", str); //变量名要取地址传递scanf,数组名本身表示地址,不用 &
printf("%s",str);
return 0;
}如上helloguojia中,虽然全部打印出来了,但是只有“helloguoji”受到保护,而“a”不受保护,具有安全隐患。若存在存储空间不足,就会被弹出,出现数据的异常。
注意:scanf 函数碰到字符串时,碰到空格和换行会自动终止。
往期文章
2020-8-19
C++语法1、类与对象
c++语法2、c执行-命名空间-输入输出
c++ 3、语法 数据类型
c++ 4、语法 数组
c++ c语言- 概述
c++ c语言 - 控件及概述补充
c++ c语言 - 数据类型
图 | 郭嘉
文 | 郭嘉

扫码关注桔梗与少年
微信号 : if-u-remembers
新浪微博:桔梗与少年