加载中…
个人资料
liht1634
liht1634
  • 博客等级:
  • 博客积分:0
  • 博客访问:223,741
  • 关注人气:58
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
正文 字体大小:

C语言使用相关汇总1

(2014-11-24 11:09:45)
标签:

c语言使用相关汇总

分类: 编程类

内容包括sizeof操作符、memset和memcpy、srand()与rand()函数、continue与break、++i和i++的区别、指针、!~的区别、sprintf、printf、码制转换。紫色文字为超链接,点击自动跳转至其他相关博文。持续更新!

目录:

1、sizeof操作符

2、memset和memcpy

3、srand()与rand()函数

4、continue与break

5、++i和i++的区别

6、sizeof与strlen的区别与联系

7、指针

1)float (*p)(float)   2)理解指向指针的指针   3)指针直接访问结构体

8、!~的区别

9、sprintf字符串格式化命令

1)简述   2十进制打印   3十六进制打印   4注意一个符号扩展的问题   5控制浮点数打印格式   6实际应用

10、printf格式化字符串

1)Turbo C2.0提供的格式化规定符   2)一些特殊规定字符C语言之转义字符\

3)printf输出到串口死机(STM32关于printf重定向到串口)

11、原码、反码、补码的理解

12、十六进制与BCD码相互转换

---------------------------------------


-------------------------------------------------------------------------------------------------------------

1、sizeof操作符

sizeof(类型说明符,数组名或表达式)

sizeof 变量名

作用:返回一个对象或类型所占的内存字节数

size是尺寸,of是……的。比如sizeof(int)就是int型在内存中的尺寸,也就是大小了。


-------------------------------------------------------------------------------------------------------------

2、memset和memcpy

memset表达式:memset(a,0,sizeof(a))

memset解释:将a中前sizeof(a)个字节用0替换并返回a。

memset作用:在一段内存块中填充某个给定的值,它是对较大的结构体数组进行清零操作的一种最快方法。

一定要记住如果要把一个char a[20]清零,一定是 memset(a,0,20);

memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。

例子详见Zigbee学习/第三章基础实验/9.串口通讯-发送字符串:

void main(void)
{
    CLKCONCMD &= ~0x40;             //设置系统时钟源为32MHZ晶振
    while(CLKCONSTA & 0x40);          //等待晶振稳定为32M
    CLKCONCMD &= ~0x47;             //设置系统主时钟频率为32MHZ
     
    InitUart();                       //设置串口相关寄存器
    memset(TxData, 0, TX_SIZE);       //数据清0
    memcpy(TxData, TX_STRING, sizeof(TX_STRING)); //复制发送字符串到TxData
       
    while(1)
    {
        UartSendString(TxData, sizeof(TX_STRING)); //串口发送数据
        DelayMS(1000);         //延时
    }
}


-------------------------------------------------------------------------------------------------------------

3、srand()与rand()函数

高级语言中带的随机数产生函数是伪随机数,伪随机数的意思是并不是真正意义上的随机数,而是通过某种运算或者在某种程度上产生随机的效果。由于计算机是一种图灵机,相同的输入必定产生相同的输出。所以,我们必须在C语言随机数的基础上加上某种比较随机的条件,简称种子。这样产生的随机数才会看起来比较随机,而不会每次运行程序的时候是一样的了。比如光用rand,产生5个随机数,每次运行程序可能结果都是一样的可能都是4 9 3 8 7 但是使用了当前系统时间为种子的话,每次运行结果就可能不一样了 假设第一次运行是5 1 0 3 9 第二次运行可能就是8 7 1 3 4

srand 初始化随机种子,rand 产生随机数。

rand函数

头文件: 

定义函数:int rand(void)

函数功能:产生随机数

函数说明:因为rand的内部实现是用线性同余法做的,他不是真的随机数,只不过是因为其周期特别长,所以,在一定的范围里可看成是随机的。rand()会返回一随机数值,范围在0至RAND_MAX 间。在调用此函数产生随机数前,必须先利用srand()设好随机数种子,如果未设随机数种子,rand()在调用时会自动设随机数种子为1。

返回值: 返回0至RAND_MAX之间的随机整数值,RAND_MAX的范围最少是在32767之间(int),即双字节(16位数)。若用unsigned int 双字节是65535,四字节是4294967295的整数范围。且0~RAND_MAX每个数字被选中的机率是相同的。

rand()产生的是假随机数字,每次执行时是相同的。若要不同,以不同的值来初始化它.初始化的函数就是srand()。

函数举例:

#include“stdlib.h”
#include “stdio.h”
int main(void)
{
  int i,j;
  for(i=0; i<10; i++)
  {
    j=1+(int)(10.0*rand()/(RAND_MAX+1.0));
    printf("%d ",j);
  }
  printf("\n");
  return 0;
}

第一次运行结果:  (每次运行结果都相同)

C语言使用相关汇总1

第二次运行结果:

C语言使用相关汇总1

srand函数

头文件:  “stdlib.h”//修改为<>

定义函数:void srand (unsigned int seed);

函数说明:srand()用来设置rand()产生随机数时的随机数种子。参数seed必须是个整数,通常可以利用getpid()或time(0)的返回值来当做seed。如果每次seed都设相同值,rand()所产生的随机数值每次就会一样。

函数举例:

#include “stdlib.h”
#include “stdio.h”
#include “time.h”
int main(void)
{
  int i,j;
  srand((int)time(NULL));
  for(i=0; i<10; i++)
  {
    j=1+(int)(10.0*rand()/(RAND_MAX+1.0));
    printf("%d ",j);
  }
  printf("\n");
  return 0;
}

第一次运行结果:(每次运行结果都不同)

C语言使用相关汇总1

第二次运行结果:

C语言使用相关汇总1

例子:

求做一个模拟产生学生成绩的程序,成绩要求在四十到一百分之间,保留一位小数,而且小数位要是零或五
程序的一部分是这样的
while((n=400+rand()601)%5);
score[i]=n/10.0;
printf("%5.1d",score[i]");

rand() 能产生0RAND MAX之间的随机数,rand() % 601生成一个 0~600的数,则 n 从4001000。
在后面的计算成绩处,把 score =n / 10, 即 n的个位,变成SCORE的小数部分;
又要求 小数 为0 或5, 所以 n 的个位必须是0或5,即能被5整除,n % 5 == 0;
如果 n 不能被5整除,即 n%5 > 0;
while((n=400+rand()601)%5);作用是产生一个400
1000的数,如果不能被5整除,则另产生一个数,直到该数能被5整除。
PS:rand()601 产生的数并不是随机的。如
0~RAND MAX = 700, 并假定产生RAND能产生0700间的随机数,那么 rand()601后,原来0600的数还是0600,601700的数变成 099,所以099 的机率要比其它数大一倍。


-------------------------------------------------------------------------------------------------------------

4、continue与break
1)break可用于switch语句,表示跳出整个switch块,而continue则不能用于switch语句

--------------------------------------
2
它们都可用于循环语句的循环体,所谓的区别也应该是它们对循环次数的影响不同。break用于立即退出当前循环,而continue仅跳过当次循环(本次循环体内不执行continue语句后的其它语句,但下次循环还会执行)。举例说明。
int i;
int s = 0;


for (int i = 1; i <= 10; i++)
{
  if (i == 6) break;
  s += i;
}
上面的循环会因为break语句而在i=6时提前终止,这样s的最终值就是1+2+3+4+5
如将break换成continue
int i;
int s = 0;


for (int i = 1; i <= 10; i++)
{
  if (i == 6) continue;
  s += i;
}
当i=6时就不会将i累加到s中,s的最终值是1+2+3+4+5+7+8+9+10,唯独少一个6。


-------------------------------------------------------------------------------------------------------------

5、++i和i++的区别

在游戏中就i++是“我加你”;++i是“求人家加”。区别在于“应答与请求”。

在编程中i++和++i最终是一样的,不过在循环体中,i++是先取出i,再加1,++i先加后取。


-------------------------------------------------------------------------------------------------------------

6、sizeof与strlen的区别与联系

1sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。
该类型保证能容纳实现所建立的最大对象的字节大小。

--------------------------------------
2
sizeof是算符,strlen是函数。

--------------------------------------
3
sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以''\0''结尾的。
sizeof还可以用函数做参数,比如:
short f();
printf("%d\n", sizeof(f()));
输出的结果是sizeof(short),即2。

--------------------------------------
4
数组做sizeof的参数不退化,传递给strlen就退化为指针了。

--------------------------------------
5
大部分编译程序在编译的时候就把sizeof计算过了是类型或是变量的长度这就是sizeof(x)可以用来定义数组维数的原因
char str[20]="0123456789";
int a=strlen(str); //a=10;
int b=sizeof(str); //而b=20;

--------------------------------------
6
strlen的结果要在运行的时候才能计算出来,时用来计算字符串的长度,不是类型占内存的大小。

--------------------------------------
7
sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof是个操作符不是个函数。

--------------------------------------
8)当适用了于一个结构类型时或变量, sizeof 返回实际的大小,
当适用一静态地空间数组, sizeof 归还全部数组的尺寸。
sizeof 操作符不能返回动态地被分派了的数组或外部的数组的尺寸

--------------------------------------
9
数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,如:
fun(char [8])
fun(char [])
都等价于 fun(char *)
在C++里参数传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小
如果想在函数内知道数组的大小, 需要这样做:
进入函数后用memcpy拷贝出来,长度由另一个形参传进去
fun(unsiged char *p1, int len)
{
  unsigned char* buf = new unsigned char[len+1]
  memcpy(buf, p1, len);
}
我们能常在用到 sizeof 和 strlen 的时候,通常是计算字符串数组的长度。
看了上面的详细解释,发现两者的使用还是有区别的,从这个例子可以看得很清楚:
char str[20]="0123456789";
int a=strlen(str); //a=10; >>>> strlen 计算字符串的长度,以结束符 0x00 为字符串结束。
int b=sizeof(str); //而b=20; >>>> sizeof 计算的则是分配的数组 str[20] 所占的内存空间的大小,不受里面存储的内容改变。
上面是对静态数组处理的结果,如果是对指针,结果就不一样了
char* ss = "0123456789";
sizeof(ss) 结果 4 ===》ss是指向字符串常量的字符指针,sizeof 获得的是一个指针的之所占的空间,应该是长整型的,所以是4
sizeof(*ss) 结果 1 ===》*ss是第一个字符 其实就是获得了字符串的第一位'0' 所占的内存空间,是char类型的,占了 1 位

strlen(ss)= 10 >>>> 如果要获得这个字符串的长度,则一定要使用 strlen。


-------------------------------------------------------------------------------------------------------------

7、指针

1)float (*p)(float)

声明了一个指针p,p指向一个具有一个float类型形参的函数,这个函数返回一个float型值。
或者说是:声明了一个指向具有一个float类型形参、返回类型为float的函数的指针p。

本人认为后一种说法正规些,但很绕口;第一种好理解,但略欠规范。

--------------------------------------

2)理解指向指针的指针

int i = 5, j = 6, k=7;

int *ip1 = &i, *ip2 = &j;

int **ipp = &ip1;

那么现在指针 ipp 指向了 ip1,ip1 指向了 i。*ipp 就是 ip1,**ipp 就是 i,或者说是 5。我们可以用我们熟悉的盒子箭头图来描述,像这样:


C语言使用相关汇总1


如果我们接着这么写:

*ipp = ip2;(或者*ipp = &j

我们就改变了 ipp 指向的那个指针(也就是 ip1),现在它指向 ip2 所指的地方。也就是说(ip1)现在它指向了 j:


C语言使用相关汇总1

我的疑问是:为什么在第二幅图里,ipp 还是指向 ip1 而不是 ip2?

让我们暂时忘掉那些关于指针的各种类比,指针实际上存放的是内存的地址。

& 符号的意思是取地址,也就是返回一个对象在内存中的地址。

*  符号的意思是取得一个指针所指向的对象。 也就是如果一个指针保存着一个内存地址,那么它就返回在那个地址的对象。

所以 *ipp = ip2;(*ipp就是 ip1)实际上是把 ipp 存的地址所对应的对象,也就是 ip1 取到,然后把 ip2 存的值赋值给 ip1,也就是 j 的地址。简单点就是: &取址*取值。

--------------------------------------

3)指针直接访问结构体

http://bbs.csdn.net/topics/390485528

有一个结构体typedef struct{

unsigned char month;
                      unsigned char day;
                      unsigned char hour;
                          }date;
date Time;现在想把Time结构体放在单片机的RAM区域,单片机在进入休眠模式下时间数据不丢失,NEC单片机是_no_init date Time @0x200位置上,可是现在我用松下单片机平台没有办法,不知道哪位大侠可以用指针直接#define LCD5W (*(volatile unsigned int*)0xfa40)的方式指导一下?现在主要是结构体怎么用指针直接访问?如下:

unsigned char array[3];
array=((array*)0xfa40);
struct date *p;
p=((struct date*)0xfa40);
p.day=11;  //给日期的赋值
p.month=11;  //给月份的赋值

struct date *p;
对这个类型进行声明的时候,要加入防止编译器自动对齐的编译选项


-------------------------------------------------------------------------------------------------------------

8、!~的区别

! 这个是“逻辑反”,凡是a的值不为0的,!a 就等于0;如果a的值为0,则 !a 的值为1;

~ 这个是“按位取反”。

比如:

int a = 2 ; 用二进制表示为00 00 00 10; 则!a = 0;

  ~a = 11 11 11 01 (按位取反后的二进制结果,此结果为补码形式)

求其源码为 :10 00 00 11  即为-3,所以~a 的结果为-3。


-------------------------------------------------------------------------------------------------------------

9、sprintf字符串格式化命令

1)简述

(1)函数原型:int sprintf( char *buffer, const char *format [, argument] … );

主要功能是把格式化的数据写入某个字符串中。sprintf 是个变参函数,最常见的应用之一莫过于把整数打印到字符串中。

(2)转换说明及作为结果的打印输出说明:

%a           浮点数、十六进制数字和p-记数法(C99)

%A     浮点数、十六进制数字和p-记法(C99)

%c      一个字符 

%d     有符号十进制整数 

%e    浮点数、e-记数法

%E    浮点数、E-记数法

%f    浮点数、十进制记数法  

%g     根据数值不同自动选择%f或%e.

%G     根据数值不同自动选择%f或%e.

%i             有符号十进制数(与%d相同)

%o    无符号八进制整数

%p    指针    

%s    字符串

%u    无符号十进制整数

%x    使用十六进制数字0f的无符号十六进制整数 

%X    使用十六进制数字0f的无符号十六进制整数

 

%%    打印一个百分号

--------------------------------------

2十进制打印

//把整数123 打印成一个字符串保存在s 中。

sprintf(s, "%d", 123); //产生"123"

可以指定宽度,不足的左边补空格:

sprintf(s, "MM", 123, 4567); //产生" 1234567"

当然也可以左对齐:

sprintf(s, "%-4dM", 123, 4567); //产生"123 4567"

--------------------------------------

3十六进制打印

sprintf(s, "%8x", 4567); //小写16 进制,宽度占8 个位置,右对齐

sprintf(s, "%-8X", 4568); //大写16 进制,宽度占8 个位置,左对齐

这样,一个整数的十六进制字符串就很容易得到,但我们在打印十六进制内容时,通常想要一种左边补0 的等宽格式,那该怎么做呢?很简单,在表示宽度的数字前面加个0 就可以了。

sprintf(s, "X", 4567); //产生"000011D7"

上面以”%d”进行的10 进制打印同样也可以使用这种左边补0 的方式。

--------------------------------------

4注意一个符号扩展的问题

比如,假如我们想打印短整数(short)-1 的内存十六进制表示形式,在Win32 平台上,一个short 型占2 个字节,所以我们自然希望用4 个十六进制数字来打印它:

short si = -1;

sprintf(s, "X", si);

产生“FFFFFFFF”,怎么回事?因为sprintf 是个变参函数,除了前面两个参数之外,后面的参数都不是类型安全的,函数更没有办法仅仅通过一个“%X”就能得知当初函数调用前参数压栈时被压进来的到底是个4 字节的整数还是个2 字节的短整数,所以采取了统一4 字节的处理方式,导致参数压栈时做了符号扩展,扩展成了32 位的整数-1,打印时4 个位置不够了,就把32 位整数-1 的8 位十六进制都打印出来了。

如果你想看si 的本来面目,那么就应该让编译器做0 扩展而不是符号扩展(扩展时二进制左边补0 而不是补符号位):

sprintf(s, "X", (unsigned short)si);

就可以了。或者:

unsigned short si = -1;

sprintf(s, "X", si);

sprintf 和printf 还可以按8 进制打印整数字符串,使用”%o”。注意8 进制和16 进制都不会打印出负数,都是无符号的,实际上也就是变量的内部编码的直接的16 进制或8 进制表示。

--------------------------------------

5控制浮点数打印格式

浮点数的打印和格式控制是sprintf 的又一大常用功能,浮点数使用格式符”%f”控制,默认保留小数点后6 位数字,比如:

sprintf(s, "%f", 3.1415926); //产生"3.141593"

但有时我们希望自己控制打印的宽度和小数位数,这时就应该使用:”%m.nf”格式,其中m 表示打印的宽度,n 表示小数点后的位数。比如:

sprintf(s, ".3f", 3.1415626); //产生" 3.142"

sprintf(s, "%-10.3f", 3.1415626); //产生"3.142 "

sprintf(s, "%.3f", 3.1415626); //不指定总宽度,产生"3.142"

注意一个问题,你猜

int i = 100;

sprintf(s, "%.2f", i);

会打出什么东东来?“100.00”?对吗?自己试试就知道了,同时也试试下面这个:

sprintf(s, "%.2f", (double)i);

--------------------------------------

6实际应用

(1) 该函数包含在stdio.h的头文件中。

#include "stdio.h"

--------------------

(2) sprintf与printf函数的区别

sprintf和平时我们常用的printf函数的功能很相似。

sprintf函数打印到字符串中。

printf函数打印输出到屏幕上。

sprintf函数在我们完成其他数据类型转换成字符串类型的操作中应用广泛。

--------------------

(3) sprintf函数的格式:
int sprintf( char *buffer, const char *format [, argument,...] );
除了前两个参数固定外,可选参数可以是任意个。buffer是字符数组名;format是格式化字符串(像:"=%6.2f%#x%o",%与#合用时,自动在十六进制数前面加上0x)。只要在printf中可以使用的格式化字符串,在sprintf都可以使用。其中的格式化字符串是此函数的精华。

--------------------

(4) 可以控制精度

char str[20];
double f=14.309948;
sprintf(str,"%6.2f",f);
--------------------

(5) 可以将多个数值数据连接起来。

char str[20];

int a=20984,b=48090;
sprintf(str,"=m",a,b);
str[]="20984 48090"
--------------------

(6) 可以将多个字符串连接成字符串

char str[20];
char s1={'A','B','C'};
char s2={'T','Y','x'};
sprintf(str,"%.3s%.3s",s1,s2);

%m.n在字符串的输出中,m表示宽度,字符串共占的列数;n表示实际的字符数。%m.n在浮点数中,m也表示宽度;n表示小数的位数。

--------------------

(7) 可以动态指定,需要截取的字符数
char s1={'A','B','C'};
char s2={'T','Y','x'};
sprintf(str,"%.*s%.*s",2,s1,3,s2);
sprintf(s, "%*.*f", 10, 2, 3.1415926); 

--------------------

(8) 可以打印出i的地址sprintf(s, "%p", &i);

上面的语句相当于
sprintf(s, "%0*x", sizeof(void *), &i);

--------------------

(9) sprintf的返回值是字符数组中字符的个数,即字符串的长度,不用在调用strlen(s)求字符串的长度。

--------------------

(10) VC++测试

#include "stdio.h"


int main(void) 

  char s[40];

  sprintf(s,"%s%d%c","test",1,'2'); //把结果输出到指定的字符串中

  printf("%s\n",s);

}C语言使用相关汇总1


-------------------------------------------------------------------------------------------------------------

10、printf格式化字符串

1)Turbo C2.0提供的格式化规定符

符号  作用
%d 十进制有符号整数
%u 十进制无符号整数
%f  浮点数
%s 字符串
%c 单个字符
%p  指针的值
%e  指数形式的浮点数
%x, %X  无符号以十六进制表示的整数
%o  无符号以八进制表示的整数
%g  自动选择合适的表示法

说明:

(1) 可以在"%"和字母之间插进数字表示最大场宽。

例如: = 表示输出3位整型数, 不够3位右对齐。

%9.2f 表示输出场宽为9的浮点数, 其中小数位为2, 整数位为6,小数点占一位, 不够9位右对齐。

%8s 表示输出8个字符的字符串, 不够8个字符右对齐。如果字符串的长度、或整型数位数超过说明的场宽, 将按其实际长度输出。但对浮点数, 若整数部分位数超过了说明的整数位宽度, 将按实际整数位输出;若小数部分位数超过了说明的小数位宽度, 则按说明的宽度以四舍五入输出。

另外, 若想在输出值前加一些0, 就应在场宽项前加个0。

例如: d 表示在输出一个小于4位的数值时, 将在前面补0使其总宽度为4位。

如果用浮点数表示字符或整型量的输出格式, 小数点后的数字代表最大宽度,小数点前的数字代表最小宽度。

例如: %6.9s 表示显示一个长度不小于6且不大于9的字符串。若大于9, 则第9个字符以后的内容将被删除。

(2) 可以在"%"和字母之间加小写字母l, 表示输出的是长型数。

--------------------

例如: %ld 表示输出long整数

      %lf 表示输出double浮点数

--------------------

(3) 可以控制输出左对齐或右对齐, 即在"%"和字母之间加入一个"-" 号可说明输出为左对齐, 否则为右对齐。

例如: %-7d 表示输出7位整数左对齐

        %-10s 表示输出10个字符左对齐

--------------------------------------

2)一些特殊规定字符(C语言之转义字符\

 字符  作用
 \n  换行
 \f  清屏并换页
 \r  回车
 \t  Tab符


讨论:例如串口接收单片机发送的16进制的数据,则我们要这样接收:

byte buf[56];

recv(comm->handle,buf);

buf[strlen(buf)]=0;//打上结束标志

//将接收的数字转化为16进制串

char *pstr=new char[256];

byte *pb=&buf[0];

while(!pb) 

sprintf(pstr++,"x",*pb++);

--------------------------------------

3)printf输出到串口死机(STM32关于printf重定向到串口)


-------------------------------------------------------------------------------------------------------------

11、原码、反码、补码的理解

1)讨论原码之前首先需要了解两个概念:机器数和真值。

a.一个数值在计算机中的二进制表示形式,就称为这个数值的机器数。机器数是带符号的,其中最高位是符号位,1表示负数,0表示正数。比如,

1100 0000就是-64的机器数,即在计算机中的二进制表示形式。同样的,0100 0000就是64的机器数。

b.真值:因为第一位是符号位,所以机器数的形式值并不是真正的数值。机器数中除符号位之外的二进制串表示的才是真值(当然要加上符号才是真正的值)。

2)对于一个数,在计算机中如何表示呢?这就涉及到对其的编码问题,原码、反码、补码就是计算机中对数的三种编码方式。

a.原码就类似于上述机器数的定义方式,一个数的原码就是符号位加上真值的绝对值,即第一位表示符号位,其余位表示值。

-64的原码就是:1100 0000

+64的原码就是:0100 0000

计算机中只有加法器没有减法器,减法运算是转化为加法来进行的。以下举例说明使用原码进行加减法的过程:

64+64=0100 0000 + 0100 0000=0 1000 0000=128

64-64=64+(-64)=0100 0000+ 1100 0000=1 0000 0000=-0

1-1=1+(-1)=0000 0001 + 1000 0001=1000 0010=-2  计算错误

原码的表示方式存在的问题:

存在+0、-0的情况(0000 1000)

进行减法运算的时候,有些情况会计算出错(1-1=-2)


b.为了解决减法运算会出错的情况,引入了反码的编码形式。对于正数,其反码与原码完全相同(原码表示下,加法是正确的,所以正数的反码保持与原码相同即可),而负数的反码是保持原码的符号位不变,所有位依次取反,就得到反码。以下分别给出+1,-1的反码形式,并使用反码进行减法运算:

+1=[0000 0001]

-1=[1111 1110]

1-1=1+(-1)=0000 0001+1111 1110=1111 1111,结果是反码形式,需要将其转化为原码形式才能得到确切值,原码是1000 0000=-0。

但是反码对于0的表示方法任然是有两种,这里以4 bit为例:

0000 +0

1111 -0


c.采用了反码的形式之后,减法运算的结果是正确了的。不过对于0还是存在+0、-0的情况。为了解决这个问题,补码出现了。正数的补码就是其原码自身,负数的补码是在其反码的基础上+1。还是以+1、-1的补码形式说明:

+1=0000 0001

-1=1111 1111

1-1=1+(-1)=1 0000 0000 丢掉最高位=0000 0000=0(正数的补码和原码相同,所以是0)。

使用补码不仅解决了+0、-0的情况,而且还可以多表示一个最低数。下图以4 bit为例,说明了原码、反码、补码的”进化过程“:

以下讨论原码和补码两种编码形式下,表示的取值范围。从上图易知,分别使用原码和补码,4 bits的取值范围是:

原码:[-7,7](包含+0、-0)

补码:[-8,7](只有一个0)

两种编码方式都表示了16个数值,只是原码有+0、-0之分,而补码只有一个0(+0),没有-0,因此可多表示一个最小值-8。-8的补码推倒过程如下:

-1-7=1111+1001=1000=-8,所以-8的补码是1000,其没有对应的反码和原码。

而8 bits的范围是:

补码:[-128,127] 1000 0000=-128

因此对于n bits分别以原码、补表示的数值取值范围是:

原码:[-(2^(n-1)-1),2^(n-1)-1]   有1个符号位,所以是n-1

补码:[-(2^(n-1)),2^(n-1)-1]

3)计算机中是以补码的形式表示数值的(起码是整数),编程语言也是以补码的形式表示数值的。

根据负数的原码求补码有一个比较简单的计算方法:符号位保持不变。从最低位开始,直至遇到第一个1之前,之前的所有位保持不变。遇到第一个1之后,保留这个1,以后按位取反直到符号位后一位。

根据负数的补码求其原码的方法:符号位保持不变,其余各位取反,然后再整个数加1。

另外,两个二进制数相减,等于加上减数保持符号位不变,取反 +1,即使加上其补码。

4)计算机只有加法器,将减法转化成加法,是利用了数学中的同余概念。


-------------------------------------------------------------------------------------------------------------

12、十六进制与BCD码相互转换

十六进制转为BCD码好理解,有问题的是BCD码转为十六进制数;
Hex2Bcd:
0x3F的BCD码是多少?正确答案:99,或者0x63
0x3F=63,再加0x,->0x63, 再把0x63转为99(0x63=99)
1、如果要表示为十六进制,则转换为10进制数,前面加0x符号;(十六进制表示)
2、如果要表示为十进制,则先转换为10进制数,前面加0x符号,再把加上符号的。

十六进制数转为十进制数;(十进制表示) 如果一个数为0x38,则BCD码为86。

Bcd2Hex:(注:)
1、如果这个数是16进制表示的,把0x符号去掉,当成10进制数直接转换为16进制数即可;(前提是这个16进制表示的数的任何一位不能大于9,也就是说不能出现A、B、C、D、E、F)如:0x999,去掉0x,->999,再转为十六进制数为0x3E7
2、如果这个数是十进制表示的,先把这个数转换为16进制数,再把0x符号去掉,当成十进制数,再转化为十六进制数即可;(注:这个数要在一定范围内,才行,如999转换为十六进制为0x3E7,把0x去掉,3E7不在10进制表示范围内,)

    上面说的是在一定范围内的数,对于不在一定范围内的数怎么转?刚开始还真不好理解,是因为学校教给我们的数学规则限制了我们的思维,对于一个数3E7,当成10进制怎么理解?还是按十进制来,个位、十位、百位...,只不过现在十位上的数是14,BCD码999转换为十六进制是多少?正确答案:447,或者0x1BF,为什么?想不通...
转换过程:999转为十六进制,->0x3E7,33*10+E=44 (3*10+E)*10+7=447

也就相当于0x3E7当成10进制理解,则是3*100+E*10+7,简单不?
注:一个十六进制数转为BCD码,但这个BCD码再转为十六进制数,不等于这个数;
如:如果一个数为0x999,Hex2Bcd(0x999)=0x2547(9543),Bcd2Hex(0x2547)=0x9F3(2547),而一个BCD码的数可以转为十六进制数,这个十六进制数可以反转为BCD码,可能等于这个数(这个数是十六进制表示,且每一位的数字在0~9之间);也可能不等于这个数。
如:Bcd2Hex(0x999)=0x3E7(999),Hex2Bcd(0x3E7)=2457(0x999)(等于)
   Bcd2Hex(0x9F3)=0x41D(1053),Hex2Bcd(0x41D)=0x1053(4179)
   Bcd2Hex(999)=0x1BF(447),Hex2Bcd(0x1BF)=0x447(1095)(不等于)
   Bcd2Hex(0x447)=0x1BF(447),Hex2Bcd(0x1BF)=0x447(1095)(等于)
结论:16进制转BCD不可逆,BCD转16进制有可能可逆。

  1. unsigned char BCD2HEX(unsigned char bcd_data)    //BCD转为HEX子程序
  2.   
  3.     unsigned char temp;
  4.     temp=(bcd_data/16*10 bcd_data);   
  5.     return temp;   
  6.   
  7. unsigned char HEX2BCD(unsigned char hex_data)    //HEX转为BCD子程序
  8.   
  9.     unsigned char temp;   
  10.     temp=(hex_data/10*16 hex_data);   
  11.     return temp;   
  12. }


-------------------------------------------------------------------------------------------------------------

我的更多文章:

0

阅读 评论 收藏 转载 喜欢 打印举报/Report
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有