标准的LZW压缩原理:
~~~~~~~~~~~~~~~~~~
先来解释一下几个基本概念:
LZW压缩有三个重要的对象:数据流(CharStream)、编码流(CodeStream)和编译
表(String Table)。在编码时,数据流是输入对象(图象的光栅数据序列),编码流
就是输出对象(经过压缩运算的编码数据);在解码时,编码流则是输入对象,数据
流是输出对象;而编译表是在编码和解码时都须要用借助的对象。
字符(Character):最基础的数据元素,在文本文件中就是一个字节,在光栅数据中就
是一个像素的颜色在指定的颜色列表中的索引值;
字符串(String):由几个连续的字符组成;
前缀(Prefix):也是一个字符串,不过通常用在另一个字符的前面,而且它的长度可以为0;
根(Root):单个长度的字符串;
编码(Code):一个数字,按照固定长度(编码长度)从编码流中取出,编译表的映射值;
图案:一个字符串,按不定长度从数据流中读出,映射到编译表条目.
LZW压缩的原理:提取原始图象数据中的不同图案,基于这些图案创建一个编译表,
然后用编译表中的图案索引来替代原始光栅数据中的相应图案,减少原始数据大小。看
起来和调色板图象的实现原理差不多,但是应该注意到的是,我们这里的编译表不是事
先创建好的,而是根据原始图象数据动态创建的,解码时还要从已编码的数据中还原出
原来的编译表(GIF文件中是不携带编译表信息的),为了更好理解编解码原理,我们
来看看具体的处理过程:
编码器(Compressor)
~~~~~~~~~~~~~~~~
编码数据,第一步,初始化一个编译表,假设这个编译表的大小是12位的,也就是最
多有4096个单位,另外假设我们有32个不同的字符(也可以认为图象的每个像素最多有32
种颜色),表示为a,b,c,d,e...,初始化编译表:第0项为a,第1项为b,第2项为c...
一直到第31项,我们把这32项就称为根。
开始编译,先定义一个前缀对象Current Prefix,记为[.c.],现在它是空的,然后
定义一个当前字符串Current String,标记为[.c.]k,[.c.]就为Current Prefix,k就
为当前读取字符。现在来读取数据流的第一个字符,假如为p,那么Current String就等
于[.c.]p(由于[.c.]为空,实际上值就等于p),现在在编译表中查找有没有Current
String的值,由于p就是一个根字符,我们已经初始了32个根索引,当然可以找到,把p
设为Current Prefix的值,不做任何事继续读取下一个字符,假设为q,Current String
就等于[.c.]q(也就是pq),看看在编译表中有没有该值,当然。没有,这时我们要做下
面的事情:将Current String的值(也就是pq)添加到编译表的第32项,把Current Prefix 的值(也就是p)在编译表中的索引输出到编码流,修改Current Prefix为当前读取的字符(也就是q)。继续往下读,如果在编译表中可以查找到Current String的值([.c.]k),
则把Current String的值([.c.]k)赋予Current Prefix;如果查找不到,则添加Current String的值([.c.]k)到编译表,把Current Prefix的值([.c.])在编译表中所对应的索引
输出到编码流,同时修改Current Prefix为k ,这样一直循环下去直到数据流结束。伪代
码看起来就像下面这样:
编码器伪代码
Initialize String Table;
[.c.] = Empty;
[.c.]k = First Character in CharStream;
while ([.c.]k != EOF )
{
if ( [.c.]k is in the StringTable)
{
[.c.] = [.c.]k;
}
else
{
add [.c.]k to the StringTable;
Output the Index of [.c.] in the StringTable to the CodeStream;
[.c.] = k;
}
[.c.]k = Next Character in CharStream;
}
Output the Index of [.c.] in the StringTable to the CodeStream;
来看一个具体的例子,我们有一个字母表a,b,c,d.有一个输入的字符流abacaba。
现在来初始化编译表:#0=a,#1=b,#2=c,#3=d.现在开始读取第一个字符a,[.c.]a=a,
可以在在编译表中找到,修改[.c.]=a;不做任何事继续读取第二个字符b,[.c.]b=ab,
在编译表中不能找,那么添加[.c.]b到编译表:#4=ab,同时输出[.c.](也就是a)的
索引#0到编码流,修改[.c.]=b;读下一个字符a,[.c.]a=ba,在编译表中不能找到:
添加编译表#5=ba,输出[.c.]的索引#1到编码流,修改[.c.]=a;读下一个字符c,
[.c.]c=ac,在编译表中不能找到:添加编译表#6=ac,输出[.c.]的索引#0到编码流,
修改[.c.]=c;读下一个字符a,[.c.]c=ca,在编译表中不能找到:添加编译表#7=ca,
输出[.c.]的索引#2到编码流,修改[.c.]=a;读下一个字符b,[.c.]b=ab,编译表的
#4=ab,修改[.c.]=ab;读取最后一个字符a,[.c.]a=aba,在编译表中不能找到:
添加编译表#8=aba,输出[.c.]的索引#4到编码流,修改[.c.]=a;好了,现在没有数
据了,输出[.c.]的值a的索引#0到编码流,这样最后的输出结果就是:#0#1#0#2#4#0.
解码器(Decompressor)
~~~~~~~~~~~~~~~~~~
好了,现在来看看解码数据。数据的解码,其实就是数据编码的逆向过程,要从
已经编译的数据(编码流)中找出编译表,然后对照编译表还原图象的光栅数据。
首先,还是要初始化编译表。GIF文件的图象数据的第一个字节存储的就是LZW编
码的编码大小(一般等于图象的位数),根据编码大小,初始化编译表的根条目(从
0到2的编码大小次方),然后定义一个当前编码Current Code,记作[code],定义一
个Old Code,记作[old]。读取第一个编码到[code],这是一个根编码,在编译表中可
以找到,把该编码所对应的字符输出到数据流,[old]=[code];读取下一个编码到
[code],这就有两种情况:在编译表中有或没有该编码,我们先来看第一种情况:先
输出当前编码[code]所对应的字符串到数据流,然后把[old]所对应的字符(串)当成
前缀prefix [...],当前编码[code]所对应的字符串的第一个字符当成k,组合起来当
前字符串Current String就为[...]k,把[...]k添加到编译表,修改[old]=[code],
读下一个编码;我们来看看在编译表中找不到该编码的情况,回想一下编码情况:如
果数据流中有一个p[...]p[...]pq这样的字符串,p[...]在编译表中而p[...]p不在,
编译器将输出p[...]的索引而添加p[...]p到编译表,下一个字符串p[...]p就可以在
编译表中找到了,而p[...]pq不在编译表中,同样将输出p[...]p的索引值而添加
p[...]pq到编译表,这样看来,解码器总比编码器『慢一步』,当我们遇到p[...]p所
对应的索引时,我们不知到该索引对应的字符串(在解码器的编译表中还没有该索引,
事实上,这个索引将在下一步添加),这时需要用猜测法:现在假设上面的p[...]所
对应的索引值是#58,那么上面的字符串经过编译之后是#58#59,我们在解码器中读
到#59时,编译表的最大索引只有#58,#59所对应的字符串就等于#58所对应的字符串
(也就是p[...])加上这个字符串的第一个字符(也就是p),也就是p[...]p。事实上,
这种猜测法是很准确(有点不好理解,仔细想一想吧)。上面的解码过程用伪代码表
示就像下面这样:
解码器伪代码
Initialize String Table;
[code] = First Code in the CodeStream;
Output the String for [code] to the CharStream;
[old] = [code];
[code] = Next Code in the CodeStream;
while ([code] != EOF )
{
if ( [code] is in the StringTable)
{
Output the String for [code] to the CharStream; // 输出[code]所对应的字符串
[...] = translation for [old]; // [old]所对应的字符串
k = first character of translation for [code]; // [code]所对应的字符串的第一个字符
add [...]k to the StringTable;
[old] = [code];
}
else
{
[...] = translation for [old];
k = first character of [...];
Output [...]k to CharStream;
add [...]k to the StringTable;
[old] = [code];
}
[code] = Next Code in the CodeStream;
}
词典编码词典编码主要利用数据本身包含许多重复的字符串的特性.例如:吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮. 我们如果用一些简单的代号代替这些字符串,就可以实现压缩,实际上就是利用了信源符号之间的相关性.字符串与代号的对应表就是词典. 实用的词典编码算法的核心就是如何动态地形成词典,以及如何选择输出格式以减小冗余. 第一类词典编码第一类词典法的想法是企图查找正在压缩的字符序列是否在以前输入的数据中出现过,然后用已经出现过的字符串替代重复的部分,它的输出仅仅是指向早期出现过的字符串的"指针". LZ77算法LZ77 算法在某种意义上又可以称为"滑动窗口压缩",该算法将一个虚拟的,可以跟随压缩进程滑动的窗口作为词典,要压缩的字符串如果在该窗口中出现,则输出其出现位置和长度.使用固定大小窗口进行词语匹配,而不是在所有已经编码的信息中匹配,是因为匹配算法的时间消耗往往很多,必须限制词典的大小才能保证算法的效率;随着压缩的进程滑动词典窗口,使其中总包含最近编码过的信息,是因为对大多数信息而言,要编码的字符串往往在最近的上下文中更容易找到匹配串. LZ77编码的基本流程1,从当前压缩位置开始,考察未编码的数据,并试图在滑动窗口中找出最长的匹配字符串,如果找到,则进行步骤2,否则进行步骤 3. 2,输出三元符号组( off, len, c ).其中off 为窗口中匹配字符串相对窗口边界的偏移,len 为可匹配的长度,c 为下一个字符,即不匹配的第一个字符.然后将窗口向后滑动len + 1 个字符,继续步骤1. 3,输出三元符号组( 0, 0, c ).其中c 为下一个字符.然后将窗口向后滑动 1 个字符,继续步骤1. LZ77算法LZ77编码举例 C A B A B B C B A A 5, 3, A ABC 7 5 2, 1, B B 5 4 0, 0, C -- 4 3 1, 1, B A 2 2 0, 0, A -- 1 1 输出匹配串位置步骤LZSS算法LZ77通过输出真实字符解决了在窗口中出现没有匹配串的问题,但这个解决方案包含有冗余信息.冗余信息表现在两个方面,一是空指针,二是编码器可能输出额外的字符,这种字符是指可能包含在下一个匹配串中的字符. LZSS算法的思想是如果匹配串的长度比指针本身的长度长就输出指针(匹配串长度大于等于MIN_LENGTH),否则就输出真实字符.另外要输出额外的标志位区分是指针还是字符. LZSS编码的基本流程1,从当前压缩位置开始,考察未编码的字符,并试图在滑动窗口中找出最长的匹配字符串,如果匹配串长度len大于等于最小匹配串长度(len >= MIN_LENGTH),则进行步骤2,否则进行步骤3. 2,输出指针二元组( off, len).其中off 为窗口中匹配字符串相对窗口边界的偏移,len 为匹配串的长度,然后将窗口向后滑动len 个字符,继续步骤 1. 3,输出当前字符c,然后将窗口向后滑动 1 个字符,继续步骤 1. LZSS编码举例 C B A A B B C B B A A字符11 10 9 8 7 6 5 4 3 2 1 位置C C 11 8 (7,3) AAB 8 7 (3,2) BB 6 6 C -- 5 5 B B 4 4 B -- 3 3 A A 2 2 A -- 1 1 输出匹配串位置步骤输入数据流: 编码过程MIN_LEN =2 LZSS算法在相同的计算机环境下,LZSS算法比LZ77可获得比较高的压缩比,而译码同样简单.这也就是为什么这种算法成为开发新算法的基础,许多后来开发的文档压缩程序都使用了LZSS的思想.例如,PKZip, GZip, ARJ, LHArc和ZOO等等,其差别仅仅是指针的长短和窗口的大小等有所不同. LZSS同样可以和熵编码联合使用,例如ARJ就与霍夫曼编码联用,而PKZip则与Shannon-Fano联用,它的后续版本也采用霍夫曼编码. 第二类词典编码第二类算法的想法是企图从输入的数据中创建一个"短语词典(dictionary of the phrases)",这种短语可以是任意字符的组合.编码数据过程中当遇到已经在词典中出现的"短语"时,编码器就输出这个词典中的短语的"索引号",而不是短语本身. LZ78算法LZ78的编码思想是不断地从字符流中提取新的字符串(String),通俗地理解为新"词条",然后用"代号"也就是码字(Code word)表示这个"词条".这样一来,对字符流的编码就变成了用码字(Code word)去替换字符流(Char stream),生成码字流(Code stream),从而达到压缩数据的目的. LZ78编码器的输出是码字-字符(W,C)对,每次输出一对到码字流中,与码字W相对应的字符串(String)用字符C进行扩展生成新的字符串(String),然后添加到词典中. LZ78编码算法步骤1:将词典
和当前前缀P都初始化为空. 步骤2:当前字符C:=字符流中的下一个字符. 步骤3:判断P+C 是否在词典中(1)如果"是",则用C扩展P,即让P:=P+C,返回到步骤2. (2)如果"否",则输出与当前前缀P相对应的码字W和当前字符C, 即(W,C); 将P+C添加到词典中; 令P:=空值,并返回到步骤2 LZ78编码举例A B A C B C B B A字符9 8 7 6 5 4 3 2 1 位置(2, A) BA 8 5 (3, A) BCA 5 4 (2, C) BC 3 3 (0, B) B 2 2 (0, A) A 1 1 输出词典位置步骤输入数据流: 编码过程: LZW算法J.Ziv和A.Lempel在1978年首次发表了介绍第二类词典编码算法的文章.在他们的研究基础上,Terry A.Welch在1984年发表了改进这种编码算法的文章,因此把这种编码方法称为LZW(Lempel-Ziv Walch)压缩编码. 在编码原理上,LZW与LZ78相比有如下差别: LZW只输出代表词典中的字符串(String)的码字(code word).这就意味在开始时词典不能是空的,它必须包含可能在字符流出现中的所有单个字符.即在编码匹配时,至少可以在词典中找到长度为1的匹配串. LZW编码是围绕称为词典的转换表来完成的. LZW算法的词典LZW 编码器(软件编码器或硬件编码器)就是通过管理这个词典完成输入与输出之间的转换.LZW 编码器的输入是字符流(Char stream),字符流可以是用8位ASCII字符组成的字符串,而输出是用n位(例如12位)表示的码字流(Code stream),码字代表单个字符或多个字符组成的字符串(String). LZW编码算法步骤1:将词典初始化为包含所有可能的单字符,当前前缀P初始化为空. 步骤2:当前字符C:=字符流中的下一个字符. 步骤3:判断P+C是否在词典中(1)如果"是",则用C扩展P,即让P:=P+C,返回到步骤2. (2)如果"否",则输出与当前前缀P相对应的码字W; 将P+C添加到词典中; 令P:=C,并返回到步骤2 LZW编码举例C A B A B A B B A字符
9 8 7 6 5 4 3 2 1 位置2 BB 5 2 2 2 BA 6 3 3 4 ABA 7 4 4 7 ABAC 8 6 5 4 3 2 1 码字1 AB 1 1
C B A输出词典位置步骤输入数据流: 编码过程: LZW算法LZW算法得到普遍采用,它的速度比使用LZ77算法的速度快,因为它不需要执行那么多的缀-符串比较操作.对LZW算法进一步的改进是增加可变的码字长度,以及在词典中删除老的缀-符串.在GIF图像格式和UNIX的压缩程序中已经采用了这些改进措施之后的LZW算法. LZW算法取得了专利,专利权的所有者是美国的一个大型计算机公司—Unisys(优利系统公司),除了商业软件生产公司之外,可以免费使用LZW算法. 预测编码预测编码是数据压缩理论的一个重要分支.它根据离散信号之间存在一定相关性的特点,利用前面的一个或多个信号对下一个信号进行预测,然后对实际值和预测值的差(预测误差)进行编码.如果预测比较准确,那么误差信号就会很小,就可以用较少的码位进行编码,以达到数据压缩的目的. 第n个符号Xn的熵满足: 所以参与预测的符号越多,预测就越准确,该信源的不确定性就越小,数码率就可以降低.
lzw压缩算法的c语言实现
1 程序由五个模块组成。
(1) lzw.h 定义了一些基本的数据结构,常量,还有变量的初始化等。
#ifndef __LZW_H__
#define __LZW_H__
//------------------------------------------------------------------------------
#include
#include
#include
#include
//------------------------------------------------------------------------------
#define LZW_BASE 0x102// The code base
#define CODE_LEN 12 // Max code length
#define TABLE_LEN 4099 // It must be prime number and bigger than 2^CODE_LEN=4096. // Such as 5051 is also ok.
#define BUFFERSIZE 1024
//------------------------------------------------------------------------------
typedef struct
{
HANDLE h_sour; // Source file handle.
HANDLE h_dest; // Destination file handle.
HANDLE h_suffix; // Suffix table handle.
HANDLE h_prefix; // Prefix table handle.
HANDLE h_code; // Code table handle.
LPWORD lp_prefix; // Prefix table head pointer.
LPBYTE lp_suffix; // Suffix table head pointer.
LPWORD lp_code; // Code table head pointer.
WORD code;
WORD prefix;
BYTE suffix;
BYTE cur_code_len; // Current code length.[ used in Dynamic-Code-Length mode ]
}LZW_DA TA,*PLZW_DA TA;
typedef struct
{
WORD top;
WORD index;
LPBYTE lp_buffer;
HANDLE h_buffer;
BYTE by_left;
DWORD dw_buffer;
BOOL end_flag;
}BUFFER_DATA,*PBUFFER_DATA;
typedef struct //Stack used in decode
{
WORD index;
HANDLE h_stack;
LPBYTE lp_stack;
}STACK_DATA,*PSTACK_DATA;
//------------------------------------------------------------------------------
VOID stack_create( PSTACK_DATA stack )
{
stack->h_stack = GlobalAlloc( GHND , TABLE_LEN*sizeof(BYTE) );
stack->lp_stack = GlobalLock( stack->h_stack );
stack->index = 0;
}
//------------------------------------------------------------------------------
VOID stack_destory( PSTACK_DA TA stack )
{
GlobalUnlock( stack->h_stack );
GlobalFree ( stack->h_stack );
}
//------------------------------------------------------------------------------
VOID buffer_create( PBUFFER_DA TA buffer )
{
buffer->h_buffer = GlobalAlloc( GHND, BUFFERSIZE*sizeof(BYTE) );
buffer->lp_buffer = GlobalLock( buffer->h_buffer );
buffer->top = 0;
buffer->index = 0;
buffer->by_left = 0;
buffer->dw_buffer = 0;
buffer->end_flag = FALSE;
}
//------------------------------------------------------------------------------
VOID buffer_destory( PBUFFER_DA TA buffer )
{
GlobalUnlock( buffer->h_buffer );
GlobalFree ( buffer->h_buffer );
}
//------------------------------------------------------------------------------
VOID re_init_lzw( PLZW_DATA lzw ) //When code table reached its top it should { //be reinitialized.
memset( lzw->lp_code, 0xFFFF, TABLE_LEN*sizeof(WORD) );
lzw->code = LZW_BASE;
lzw->cur_code_len = 9;
}
//------------------------------------------------------------------------------
VOID lzw_create(PLZW_DA TA lzw, HANDLE h_sour, HANDLE h_dest) {
WORD i;
lzw->h_code = GlobalAlloc( GHND, TABLE_LEN*sizeof(WORD) );
lzw->h_prefix = GlobalAlloc( GHND, TABLE_LEN*sizeof(WORD) );
lzw->h_suffix = GlobalAlloc( GHND, TABLE_LEN*sizeof(BYTE) );
lzw->lp_code = GlobalLock( lzw->h_code );
lzw->lp_prefix = GlobalLock( lzw->h_prefix );
lzw->lp_suffix = GlobalLock( lzw->h_suffix );
lzw->code = LZW_BASE;
lzw->cur_code_len = 9;
lzw->h_sour = h_sour;
lzw->h_dest = h_dest;
memset( lzw->lp_code, 0xFFFF, TABLE_LEN*sizeof(WORD) );
}
//------------------------------------------------------------------------------
VOID lzw_destory(PLZW_DATA lzw)
{
GlobalUnlock( lzw->h_code );
GlobalUnlock( lzw->h_prefix );
GlobalUnlock( lzw->h_suffix );
GlobalFree( lzw->h_code );
GlobalFree( lzw->h_prefix );
GlobalFree( lzw->h_suffix );
}
//------------------------------------------------------------------------------
#endif
(2) fileio.h 定义了一些文件操作
#ifndef __FILEIO_H__
#define __FILEIO_H__
//------------------------------------------------------------------------------
#include
#include
#include
//------------------------------------------------------------------------------
HANDLE file_handle(CHAR* file_name)
{
HANDLE h_file;
h_file = CreateFile(file_name,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
0,
NULL
);
return h_file;
}
//------------------------------------------------------------------------------
WORD load_buffer(HANDLE h_sour, PBUFFER_DA TA buffer) // Load file to buffer
{
DWORD ret;
ReadFile(h_sour,buffer->lp_buffer,BUFFERSIZE,&ret,NULL);
buffer->index = 0;
buffer->top = (WORD)ret;
return (WORD)ret;
}
//------------------------------------------------------------------------------
WORD empty_buffer( PLZW_DATA lzw, PBUFFER_DATA buffer)// Output buffer to file
{
DWORD ret;
if(buffer->end_flag) // The flag mark the end of decode
{
if( buffer->by_left )
{
buffer->lp_buffer[ buffer->index++ ] = (BYTE)( buffer->dw_buffer >> 32-buffer->by_left )<<(8-buffer->by_left);
}
}
WriteFile(lzw->h_dest, buffer->lp_buffer,buffer->index,&ret,NULL);
buffer->index = 0;
buffer->top = ret;
return (WORD)ret;
}
//------------------------------------------------------------------------------
#endif
(3) hash.h 定义了压缩时所用的码表操作函数,为了快速查找使用了hash算法,还有处理hash冲突的函数
#ifndef __HASH_H__
#define __HASH_H__
//------------------------------------------------------------------------------
#include
#include
#include
//------------------------------------------------------------------------------
#define DIV TABLE_LEN
#define HASHSTEP 13 // It should bigger than 0.
//------------------------------------------------------------------------------
WORD get_hash_index( PLZW_DATA lzw )
{
DWORD tmp;
WORD result;
DWORD prefix;
DWORD suffix;
prefix = lzw->prefix;
suffix = lzw->suffix;
tmp = prefix<<8 | suffix;
result = tmp % DIV;
return result;
}
//------------------------------------------------------------------------------
WORD re_hash_index( WORD hash ) // If hash conflict occured we must recalculate { // hash index .
WORD result;
result = hash + HASHSTEP;
result = result % DIV;
return result;
}
//------------------------------------------------------------------------------
BOOL in_table( PLZW_DA TA lzw ) // To find whether current code is already in table. {
BOOL result;
WORD hash;
hash = get_hash_index( lzw );
if( lzw->lp_code[ hash ] == 0xFFFF )
{
result = FALSE;
}
else
{
if( lzw->lp_prefix[ hash ] == lzw->prefix &&
lzw->lp_suffix[ hash ] == lzw->suffix )
{
result = TRUE;
}
else
{
result = FALSE;
while( lzw->lp_code[ hash ] != 0xFFFF )
{
if( lzw->lp_prefix[ hash ] == lzw->prefix &&
lzw->lp_suffix[ hash ] == lzw->suffix )
{
result = TRUE;
break;
}
hash = re_hash_index( hash );
}
}
}
return result;
}
//------------------------------------------------------------------------------ WORD get_code( PLZW_DATA lzw )
{
WORD hash;
WORD code;
hash = get_hash_index( lzw );
if( lzw->lp_prefix[ hash ] == lzw->prefix &&
lzw->lp_suffix[ hash ] == lzw->suffix )
{
code = lzw->lp_code[ hash ];
}
else
{
while( lzw->lp_prefix[ hash ] != lzw->prefix ||
lzw->lp_suffix[ hash ] != lzw->suffix )
{
hash = re_hash_index( hash );
}
code = lzw->lp_code[ hash ];
}
return code;
}
//------------------------------------------------------------------------------
VOID insert_table( PLZW_DA TA lzw )
{
WORD hash;
hash = get_hash_index( lzw );
if( lzw->lp_code[ hash ] == 0xFFFF )
{
lzw->lp_prefix[ hash ] = lzw->prefix;
lzw->lp_suffix[ hash ] = lzw->suffix;
lzw->lp_code[ hash ] = lzw->code;
}
else
{
while( lzw->lp_code[ hash ] != 0xFFFF )
{
hash = re_hash_index( hash );
}
lzw->lp_prefix[ hash ] = lzw->prefix;
lzw->lp_suffix[ hash ] = lzw->suffix;
lzw->lp_code[ hash ] = lzw->code;
}
}
//------------------------------------------------------------------------------
#endif
(4) encode.h 压缩程序主函数
#ifndef __ENCODE_H__
#define __ENCODE_H__
//------------------------------------------------------------------------------
#include
#include
#include
//------------------------------------------------------------------------------
VOID output_code( DWORD code ,PBUFFER_DATA out, PLZW_DA TA lzw) {
out->dw_buffer |= code << ( 32 - out->by_left - lzw->cur_code_len );
out->by_left += lzw->cur_code_len;
while( out->by_left >= 8 )
{
if( out->index == BUFFERSIZE )
{
empty_buffer( lzw,out);
}
out->lp_buffer[ out->index++ ] = (BYTE)( out->dw_buffer >> 24 );
out->dw_buffer <<= 8;
out->by_left -= 8;
}
}
//------------------------------------------------------------------------------
VOID do_encode( PBUFFER_DA TA in, PBUFFER_DA TA out, PLZW_DATA lzw) {
WORD prefix;
while( in->index != in->top )
{
if( !in_table(lzw) )
{
// current code not in code table
// then add it to table and output prefix
insert_table(lzw);
prefix = lzw->suffix;
output_code( lzw->prefix ,out ,lzw );
lzw->code++;
if( lzw->code == (WORD)1<< lzw->cur_code_len )
{
// code reached current code top(1< // then current code length add one lzw->cur_code_len++; if( lzw->cur_code_len == CODE_LEN + 1 ) { re_init_lzw( lzw ); } } } else { // current code already in code table // then output nothing prefix = get_code(lzw); } lzw->prefix = prefix; lzw->suffix = in->lp_buffer[ in->index++ ]; } } //------------------------------------------------------------------------------ VOID encode(HANDLE h_sour,HANDLE h_dest) { LZW_DA TA lzw; BUFFER_DATA in ; BUFFER_DATA out; BOOL first_run = TRUE; lzw_create( &lzw ,h_sour,h_dest ); buffer_create( &in ); buffer_create( &out ); while( load_buffer( h_sour, &in ) ) { if( first_run ) {// File length should be considered but here we simply // believe file length bigger than 2 bytes. lzw.prefix = in.lp_buffer[ in.index++ ]; lzw.suffix = in.lp_buffer[ in.index++ ]; first_run = FALSE; } do_encode(&in , &out, &lzw); } output_code(lzw.prefix, &out , &lzw); output_code(lzw.suffix, &out , &lzw); out.end_flag = TRUE; empty_buffer( &lzw,&out); lzw_destory( &lzw ); buffer_destory( &in ); buffer_destory( &out ); } //------------------------------------------------------------------------------ #endif (5) decode.h 解压函数主函数 #ifndef __DECODE_H__ #define __DECODE_H__ //------------------------------------------------------------------------------ #include #include #include //------------------------------------------------------------------------------ VOID out_code( WORD code ,PBUFFER_DATA buffer,PLZW_DA TA lzw,PSTACK_DA TA stack) { WORD tmp; if( code < 0x100 ) { stack->lp_stack[ stack->index++ ] = code; } else { stack->lp_stack[ stack->index++ ] = lzw->lp_suffix[ code ]; tmp = lzw->lp_prefix[ code ]; while( tmp > 0x100 ) { stack->lp_stack[ stack->index++ ] = lzw->lp_suffix[ tmp ]; tmp = lzw->lp_prefix[ tmp ]; } stack->lp_stack[ stack->index++ ] = (BYTE)tmp; } while( stack->index ) { if( buffer->index == BUFFERSIZE ) { empty_buffer(lzw,buffer); } buffer->lp_buffer[ buffer->index++ ] = stack->lp_stack[ --stack->index ] ; } } //------------------------------------------------------------------------------ VOID insert_2_table(PLZW_DA TA lzw ) { lzw->lp_code[ lzw->code ] = lzw->code; lzw->lp_prefix[ lzw->code ] = lzw->prefix; lzw->lp_suffix[ lzw->code ] = lzw->suffix; lzw->code++; if( lzw->code == ((WORD)1< { lzw->cur_code_len++; if( lzw->cur_code_len == CODE_LEN+1 ) lzw->cur_code_len = 9; } if(lzw->code >= 1< { re_init_lzw(lzw); } } //------------------------------------------------------------------------------ WORD get_next_code( PBUFFER_DA TA buffer , PLZW_DA TA lzw ) { BYTE next; WORD code; while( buffer->by_left < lzw->cur_code_len ) { if( buffer->index == BUFFERSIZE ) { load_buffer( lzw->h_sour, buffer ); } next = buffer->lp_buffer[ buffer->index++ ]; buffer->dw_buffer |= (DWORD)next << (24-buffer->by_left); buffer->by_left += 8; } code = buffer->dw_buffer >> ( 32 - lzw->cur_code_len ); buffer->dw_buffer <<= lzw->cur_code_len; buffer->by_left -= lzw->cur_code_len; return code; } //------------------------------------------------------------------------------ VOID do_decode( PBUFFER_DA TA in, PBUFFER_DA TA out, PLZW_DATA lzw, PSTACK_DATA stack) { WORD code; WORD tmp; while( in->index != in->top ) { code = get_next_code( in ,lzw ); if( code < 0x100 ) { // code already in table // then simply output the code lzw->suffix = (BYTE)code; } else { if( code < lzw->code ) { // code also in table // then output code chain tmp = lzw->lp_prefix[ code ]; while( tmp > 0x100 ) { tmp = lzw->lp_prefix[ tmp ]; } lzw->suffix = (BYTE)tmp; } else { // code == lzw->code // code not in table // add code into table // and out put code tmp = lzw->prefix; while( tmp > 0x100 ) { tmp = lzw->lp_prefix[ tmp ]; } lzw->suffix = (BYTE)tmp; } } insert_2_table( lzw ); out_code(code,out,lzw,stack); lzw->prefix = code; } } //------------------------------------------------------------------------------ VOID decode( HANDLE h_sour, HANDLE h_dest ) { LZW_DA TA lzw; BUFFER_DATA in ; BUFFER_DATA out; STACK_DATA stack; BOOL first_run; first_run = TRUE; lzw_create( &lzw ,h_sour,h_dest ); buffer_create( &in ); buffer_create( &out ); stack_create(&stack ); while( load_buffer( h_sour, &in ) ) { if( first_run ) { lzw.prefix = get_next_code( &in, &lzw ); lzw.suffix = lzw.prefix; out_code(lzw.prefix, &out, &lzw , &stack); first_run = FALSE; } do_decode(&in , &out, &lzw, &stack); } empty_buffer( &lzw,&out); lzw_destory( &lzw ); buffer_destory( &in ); buffer_destory( &out ); stack_destory( &stack); } #endif 2 下面给出一个应用上面模块的简单例子 #include #include //------------------------------------------------------------------------------ #include "lzw.h" #include "hash.h" #include "fileio.h" #include "encode.h" #include "decode.h" //------------------------------------------------------------------------------ HANDLE h_file_sour; HANDLE h_file_dest; HANDLE h_file; CHAR* file_name_in = "d:\\code.c"; CHAR* file_name_out= "d:\\encode.e"; CHAR* file_name = "d:\\decode.d"; //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { h_file_sour = file_handle(file_name_in); h_file_dest = file_handle(file_name_out); h_file = file_handle(file_name); encode(h_file_sour, h_file_dest); // decode(h_file_dest,h_file); CloseHandle(h_file_sour); CloseHandle(h_file_dest); CloseHandle(h_file); return 0; } 3 后语 之前研究gif文件格式时偶然接触了lzw压缩算法,于是就想自己动手实现。从一开始看人家的原码,然 后跟着模仿,到现在用自己的语言表达出来,从理解原理到代码的实现花费了不少时间与精力,但是真正的快乐也就在这里,现在把她拿出来跟大家分享也就是分享快乐。 二级C语言-运算符与表达式 (总分:60.00,做题时间:90分钟) 一、{{B}}选择题{{/B}}(总题数:38,分数:38.00) 1.设变量已正确定义并赋值,以下合法的C语言赋值语句是______。 ? A.x=y==5 ? B.x=n%2.5 ? C.x+n=i ? D.x=5=4+1 (分数:1.00) A. √ B. C. D. 解析:[解析] 赋值运算符左侧的操作数必须是一个变量,而不能是表达式或者常量,选项C和D错误。“%”运算符两侧都应当是整型数据,选项B错误。 2.在x值处于-2~2、4~8时值为“真”,否则为“假”的表达式是______。 ? A.(2>x>-2)||(4>x>8) ? B.!(((x<-2)||(x>2))&&((x<=4)||(x>8))) ? C.(x<2)&&(x>=-2)&&(x>4)&&(x<8) ? D.(x>-2)&&(x>4)||(x<8)&&(x<2) (分数:1.00) A. B. √ C. D. 解析:[解析] 本题是考查关系运算和逻辑运算的混合运算。要给出此题的正确答案,首先需要了解数学上的区间在C语言中的表示方法,如x在[a, b]区间,其含义是x既大于等于a又小于等于b,相应的C语言表达式是x>=a&&x<=b。本例中给出了两个区间,一个数只要属于其中一个区间即可,这是“逻辑或”的关系。在选项A中,区间的描述不正确。选项B把“!”去掉,剩下的表达式描述的是原题中给定的两个区间之外的部分,加上“!”否定正好是题中的两个区间的部分,是正确的。选项C是恒假的,因为它的含义是x同时处于两个不同的区间内。选项D所表达的也不是题中的区间。 3.sizeof(double)是______。 ? A.一种函数调用 ? B.一个整型表达式 ? C.一个双精度表达式 ? D.一个不合法的表达式 (分数:1.00) C语言运算符与表达式的练习题 单项选择题 (1)以下选项中,正确的 C 语言整型常量是(D)。 A. 32L B. 510000 C. -1.00 D. 567 (2)以下选项中,(D)是不正确的 C 语言字符型常量。 A. 'a' B. '\x41' C. '\101' D. "a" (3)字符串的结束标志是(C)。 A. 0 B. '0' C. '\0' D. "0" (4)算术运算符、赋值运算符和关系运算符的运算优先级按从高到低依次为(B)。 A. 算术运算、赋值运算、关系运算 B. 算术运算、关系运算、赋值运算 C. 关系运算、赋值运算、算术运算 D. 关系运算、算术运算、赋值运算 (5)逻辑运算符中,运算优先级按从高到低依次为(D)。 A. && ! || B. || && ! C. && || ! D. ! && || (6)表达式!x||a==b 等效于(D)。 A. !((x||a)==b) B. !(x||y)==b C. !(x||(a==b)) D. (!x)||(a==b) (7)设整型变量 m,n,a,b,c,d 均为1,执行 (m=a>b)&&(n=c>d)后, m,n 的值是(A)。 A. 0,0 B. 0,1 C. 1,0 D. 1,1 *(8)设有语句 int a=3;,则执行了语句 a+=a-=a*=a; 后,变量 a 的值是(B)。 A. 3 B. 0 C. 9 D. -12 (9)在以下一组运算符中,优先级最低的运算符是(D)。 A. * B. != C. + D. = (10)设整型变量 i 值为2,表达式(++i)+(++i)+(++i)的结果是(B,上机13)。 A. 6 B. 12 C. 15 D. 表达式出错 (11)若已定义 x 和 y为double 类型,则表达式的值是(D)。 条件运算符的语法为: (条件表达式)?(条件为真时的表达式):(条件为假时的表达式) 例如: x=ay?"greater than":x==y?"equal to":"less than" 它等价于: (x>y)?"greater than":((x==y)?"equal to":"less than") 当x>y时,值为"greater than",x==y时,值为"equal to",否则,值为"less than"。条件运算符的嵌套可读性不够好。 在一个条件运算符的表达式中,如果后面两个表达式的值类型相同,均为左值,则该条件运算符表达式的值为左值表达式。例如: int x=5; long a,b; (x?a:b)=1;//ok:因为a和b都是左值 (x?x:a)=2;//error:x和a不同类型。编译器将其解释为(1ong)x和a (x==271:a)=3;//error:1非左值 "(x?a:b)=1"表示当x为0时,b=1,否则a=1。这里的括号是必须的,否则将被看作x?a:(b=1)。"(x?x:a)=2”中,尽管x是左值,a也是左值,但x与a不同类型,条件运算符要对其进行操作数的隐式转换,使之成为相同的类型。任何被转换的变量都不是左值。 ->在C中,条件运算符是不能作左值的,所以"(x?a:b)=1;"将通不过编译。 getchar 函数名: getchar 功能: 从stdio流中读字符 用法: int getchar(void); 注解: 数据类型、运算符和表达式复习题.选择题 1. 以下不合法的用户标识符是: ( ) a) f2_G3 b) If c) 4d d) _8 2. 以下选项中合法的用户标识符是: ( ) a) long b) _2Test c) 3Dmax d) A.dat 3. 以下可用作用户标识符的是: ( ) a) 12_a b) signed c) t-o d) _if 4. 以下不是关键字的标识符是: ( ) a) continue b) char c) If d) default 5. C 语言提供的合法的关键字是: ( ) a) swicth b) cher c) Case d) void 6. 以下选项中不合法的int 整型常数是( ) a) 32768 b) -56 c) 03 d) 0xAF 7. 以下合法的长整型常量是( ) a) 2L b) 49627 c) d) 213& 8. 以下正确的实型常量是( ) a) 1.2E b) . c) 1.2e0.6 d) 8 9. 以下选项中合法的实型常数是( ) a) 5E2.0 b) E-3 c) .2E0 d) 1.3E 10. 以下合法的八进制数是( ) a) 0135 b) 068 c) 013.54 d) o7 11. 以下合法的十六进制数是( ) a) 0x b) 0x4de c) 0x1h d) ox77 12. 以下选项中非法的数值常量是( ) a) 019 b) 0L c) 0xff d) 1e1 13. 若变量已正确定 以下合法的赋值表达式是( ) 义, a) a=1/b=2 b) ++(a+b) c) a=a/(b=5) d) y=int(a)+b 14. 若变量已正确定 以下非法的表达式是( ) 义, a) a/=b+c b) a%(4.0) c) a=1/2*(x=y=20,x*3) d) a=b=c 15. 设x为int 类型,其值为11,则表达式( x++*1/3 )的值是: a) 3 b) 4 c) 11 d) 12 16.设a,b 均为double 型,且 a=5.5;b=2.5; 则表达式 (int)a+b/b 的值是( ) a) 6. b) 6 c) 5. d) 6. 17.若a为int型,且其值为3,则执行完表达式: a+=a-=a*a 后,a 的值是( ) a) -3 b) 9 c) -12 d) 6 18.设k 和x 均为int 型变量,且k=7 ;x=12;则能使值为 3 的表达式是( ) 凯里学院C 语言程序设计实验报告 ×××××专业××年级××班,学号××××××姓名××成绩 合作者实验日期年月日 指导教师评阅日期年月日 实验二数据类型、运算符和表达式 一、实验目的: (1)掌握C 语言数据类型,熟悉如何定义一个整型、字符型、实型变量、以及对它们赋值的方法,了解以上类型数据输出时所用的格式转换符。 (2)学会使用C 的有关算术运算符,以及包含这些运算符的表达式,特别是自加(++)和自减(――)运算符的使用。 (3)掌握C 语言的输入和输出函数的使用 (4)进一步熟悉C 程序的编辑、编译、连接和运行的过程,学会使用stepbystep 功能。 (5)认真阅读教材数据类型,算术运算符和表达式,赋值运算符和表达式部分内容。 二、实验内容: (1)输人并运行下面的程序 #include printf("%c%c\n",c1,c2); } (2)按习题3.7的要求编程序并上机运行 该题的要求是: 要将“China”译成密码,密码规律是:用原来字母后面的第4个字母代替原来的字母。 例如,字母“A”后面第4个字母是“E”,用“E”代替“A”。因此,“China”应译为“Glmre"。 请编一程序,用赋初值的方法使。cl,c2,c3,c4,c5五个变量的值分别为‘C’、‘h’、‘i’、‘n’、‘a’,经过运算,使cl,c2,c3,c4,c5分别变为‘G’、‘l’、‘m’、‘r’、‘e’,并输出。 三、实验步骤: (1)输人并运行下面的程序 #include C语言运算符与表达式的练习题 (作业写到纸质作业本上,在规定时间内交给助教批阅,不要 再网上提交) 一、单项选择题 (1) 以下选项中,正确的C 语言整型常量是。 A. 32L B. 510000 C. -1.00 D. 567 (2) 以下选项中,是不正确的C 语言字符型常量。 A. 'a' B. '\x41' C. '\101' D. "a" (3) 在C 语言中,字符型数据在计算机内存中,以字符 的形式存储。 A. 原码 B. 反码 C. ASCII 码 D. BCD码 (4) 字符串的结束标志是。 A. 0 B. '0' C. '\0' D. "0" (5) 算术运算符、赋值运算符和关系运算符的运算优先级按 从高到低依次为。 A. 算术运算、赋值运算、关系运算 B. 算术运算、关系运 算、赋值运算 C. 关系运算、赋值运算、算术运算 D. 关系运算、算术运算、赋值运算 (6) 逻辑运算符中,运算优先级按从高到低依次为。 A. &&,!,|| B. ||,&&,! C. &&,||,! D. !,&&,|| (7) 表达式!x||a==b 等效于。 A. !((x||a)==b) B. !(x||y)==b C. !(x||(a==b)) D. (!x)||(a==b) (8) 设整型变量m,n,a,b,c,d 均为1,执行(m=a>b)&&(n=c>d)后, m,n 的值是。 A. 0,0 B. 0,1 C. 1,0 D. 1,1 (9) int b=0,x=1;执行语句if(x++) b=x+1; 后,x,b 的值依次为。 A. 2,3 B. 2,0 C. 3,0 D. 3,2 (10) 设有语句int a=3;,则执行了语句a+=a-=a*=a; 后,变量 a 的值是。 A. 3 B. 0 C. 9 D. -12 (11) 在以下一组运算符中,优先级最低的运算符是。 A. * B. != C. + D. = 南京高等职业技术学校 课堂教学设计 授课时间:2014年11月6日第11周星期四教学目标教学准备 授课教师课时课程通过本节课的学习,学生能够: 1.清晰地说出各种逻辑运算符; 2.正确运算出逻辑表达式的值,并 通过程序验证结果; 重点教案□√ 马丽娟 1 C语言多媒体课件□√班级授课地点课题教学讲义□ 512212 机房4.2逻辑运算符与逻辑表达 式 1.逻辑表达式的求值运算; 2.逻辑表达式的求值优化。 学生工作页□ 课堂特殊要求(指教师、学生的课前准备活动等) 教师:安装Win-TC程序难点 教具□√ 逻辑表达式的求值优化授课形式 理论讲授课 教学环节时间分配教师活动学生活动教学方法媒体手段导入新课 5 提出问题,并举例说明听讲、思考、回答讲授法PPT 新课讲授20 讲授、分析听讲、讨论并记录讲授法PPT 巩固练习15 布置任务、巡视、答疑思考、编程并回答问题练习法Win-TC 课堂小结 3 归纳、总结听讲、回答问题讲授法PPT 布置作业 2 布置作业记录讲授法PPT 板书设计 §4.2 逻辑运算符与逻辑表达式 ?逻辑运算符:&&、||、! ?逻辑表达式的值:非0即1 ?逻辑运算的求值优化工作任务/教学情境设置 无课后作业 书面作业:P52 随堂练习 课后反思 教案纸 教学内容 4.2 逻辑运算符与逻辑表达式 一、复习导入(5min) 1. 复习:请学生说出关系运算符有哪些? 请学生回答关系运算表达式的值? 教师进行补充。 2.导入新课: 1、学生参加技能大赛培训的条件? ?扎实的专业知识与较高的实践能力 教师强调与的关系 2、参加技能大赛集训而停课的条件? ?移动互联或智能家居 教师强调或的关系 3、学生回答引入禁烟区的条件? ?没有吸烟非 教师强调非的关系 二、新课讲授(20min) 逻辑运算符 1.教师根据逻辑关系给出三种逻辑运算符的表示形式: &&、||、! 2.教师利用具体的表达式关系分析各种逻辑运算符的作用: 逻辑与相当于英语中的and; 逻辑或相当于英语中的or; 逻辑非相当于英语中的no; 3.教师根据具体的逻辑关系引出逻辑表达式的概念及表示形式: 表达式1&&表达式2 a&&b 表达式1||表达式2 a || b !表达式!a 第一章数据类型,运算符与表达式 一.选择题 1.不合法的常量是A。 A)‘/2’B) “”C)‘’D)“483” 2. B 是C语言提供的合法的数据类型关键字。 A)Float B)signed C)integer D)Char 3.在以下各组标识符中,合法的标识符是(1)A,(2) C ,(3) D 。 (1)A)B01 B)table_1 C)0_t D)k% Int t*.1 W10 point (2)A)Fast_ B)void C)pbl D) 第二章数据类型运算符和表达式 一、单项选择 1.若有定义语句:int k1=10,k2=20;,执行表达式(k1=k1>k2)&&(k2=k2>k1)后,k1和k2的值分别为( B ) 2.下面四个选项中,均是不合法的用户标识符的选项是( B )。 3.判断字符型变量c1是否为数字字符的正确表达式为( A ) 4.在C语言中,要求运算数必须是整型的运算符是( A ) 5.下面四个选项中,均是合法的用户标识符的选项是(A)。 6.假设所有变量均为整型,则表达式(a=2,b=5,b++,a+b)的值是(B)。 7.若x,i,j和k都是int型变量,则计算表达式x=(i=4,j=16,k=32)后,x的值为(B)。 8.表达式18/4*sqrt(4.0)/8值的数据类型为(B)。 9.若a是数值类型,则逻辑表达式(a==1)||(a!=1)的值是( D ) 10.判断字符型变量c1是否为小写字母的正确表达式为(B)。 11.在C语言中,char型数据在内存中的存储形式是(C)。 12.以下选项中关于C语言常量的叙述错误的是:( D ) 13.下面正确的字符常量是(B) 14.C语言中的标识符只能由字母、数字和下划线三种字符组成,且第一个字符( D) 15.以下选项中,能表示逻辑值"假"的是( B ) 16.设变量a是整型,f是实型,i是双精度型,则表达式10+'a'+i*f值的数据类型为(C) 17.以下选项中非法的字符常量是( B ) 18.以下关于C语言数据类型使用的叙述中错误的是(B ) 19.设:int a=1,b=2,c=3,d=4,m=2,n=2;执行(m=a>b) && (n=c>d)后n的值为(C)。 C语言运算符大全 C语言的内部运算符很丰富,运算符是告诉编译程序执行特定算术或逻辑操作的符号。C语言有三大运算符:算术、关系与逻辑、位操作。另外,C还有一些特殊的运算符,用于完成一些特殊的任务。 2.6.1算术运算符 表2-5列出了C语言中允许的算术运算符。在C语言中,运算符“+”、“-”、“*”和“/”的用法与大多数计算机语言的相同,几乎可用于所有C语言内定义的数据类型。当“/”被用于整数或字符时,结果取整。例如,在整数除法中,10/3=3。 一元减法的实际效果等于用-1乘单个操作数,即任何数值前放置减号将改变其符号。模运算符“%”在C语言中也同它在其它语言中的用法相同。切记,模运算取整数除法的余数,所以“%”不能用于float和double类型。 最后一行打印一个0和一个1,因为1/2整除时为0,余数为1,故1%2取余数1。 2.6.2自增和自减 C语言中有两个很有用的运算符,通常在其它计算机语言中是找不到它们的—自增和自减运算符,++和--。运算符“++”是操作数加1,而“--”是操作数减1,换句话说:x=x+1;同++x;x=x-1;同--x; 自增和自减运算符可用在操作数之前,也可放在其后,例如:x=x+1;可写成++x;或x++;但在表达式中这两种用法是有区别的。自增或自减运算符在操作数之前,C语言在引用操作数之前就先执行加1或减1操作;运算符在操作数之后,C语言就先引用操作数的值,而后再进行加1或减1操作。请看下例: x=10; ;y=++x; 此时,y=11。如果程序改为: x=10;y=x++; 则y=10。在这两种情况下,x都被置为11,但区别在于设置的时刻,这种对自增和自减发生时刻的控制是非常有用的。在大多数C编译程序中,为自增和自减操作生成的程序代码比等价的赋值语句生成的代码 要快得多,所以尽可能采用加1或减1运算符是一种好的选择。 。下面是算术运算符的优先级: :最高++、- -- -(一元减) *、/、%最低+、-编译程序对同级运算符按从左到右的顺序进行计算。当然,括号可改变计算顺序。C语言 处理括号的方法与几乎所有的计算机语言相同:强迫某个运算或某组运算的优先级升高。 2.6.3关系和逻辑运算符 关系运算符中的“关系”二字指的是一个值与另一个值之间的关系,逻辑运算符中的“逻辑”二字指的是连接关系的方式。因为关系和逻辑运算符常在一起使用,所以将它们放在一起讨论。关系和逻辑运算符概念中的关键是True(真)和Flase(假)。C语言中,非0为True,0为Flase。使用关系或逻辑运算符的表达式对Flase和Ture分别返回值0或1(见表2-6)。 凯里学院C 语言程序设计实验报告 ×××××专业××年级××班,学号××××××姓名××成绩 合作者实验日期年月日 指导教师评阅日期年月日 实验二数据类型、运算符和表达式 一、实验目的: (1)掌握C 语言数据类型,熟悉如何定义一个整型、字符型、实型变量、以及对它们赋值的方法,了解以上类型数据输出时所用的格式转换符。 (2)学会使用C 的有关算术运算符,以及包含这些运算符的表达式,特别是自加(++)和自减(――)运算符的使用。 (3)掌握C 语言的输入和输出函数的使用 (4)进一步熟悉C 程序的编辑、编译、连接和运行的过程,学会使用stepbystep 功能。 (5)认真阅读教材数据类型,算术运算符和表达式,赋值运算符和表达式部分内容。 二、实验内容: (1)输人并运行下面的程序 #include C语言程序设计实验报告实验数据类型运算符和表 达式 This model paper was revised by the Standardization Office on December 10, 2020 凯里学院C 语言程序设计实验报告 ×××××专业××年级××班,学号××××××姓名××成绩 合作者实验日期年月日 指导教师评阅日期年月日 实验二数据类型、运算符和表达式 一、实验目的: (1)掌握C 语言数据类型,熟悉如何定义一个整型、字符型、实型变量、以及对它们赋值的方法,了解以上类型数据输出时所用的格式转换符。 (2)学会使用C 的有关算术运算符,以及包含这些运算符的表达式,特别是自加(++)和自减(――)运算符的使用。 (3)掌握C 语言的输入和输出函数的使用 (4)进一步熟悉C 程序的编辑、编译、连接和运行的过程,学会使用stepbystep 功能。 (5)认真阅读教材数据类型,算术运算符和表达式,赋值运算符和表达式部分内容。 二、实验内容: (1)输人并运行下面的程序 #include C语言程序设计实验报告实验数据类型运算符 和表达式 标准化工作室编码[XX968T-XX89628-XJ668-XT689N] 凯里学院C 语言程序设计实验报告 ×××××专业××年级××班,学号××××××姓名××成绩 合作者实验日期年月日 指导教师评阅日期年月日 实验二数据类型、运算符和表达式 一、实验目的: (1)掌握C 语言数据类型,熟悉如何定义一个整型、字符型、实型变量、以及对它们赋值的方法,了解以上类型数据输出时所用的格式转换符。 (2)学会使用C 的有关算术运算符,以及包含这些运算符的表达式,特别是自加(++)和自减(――)运算符的使用。 (3)掌握C 语言的输入和输出函数的使用 (4)进一步熟悉C 程序的编辑、编译、连接和运行的过程,学会使用stepbystep 功能。 (5)认真阅读教材数据类型,算术运算符和表达式,赋值运算符和表达式部分内容。 二、实验内容: (1)输人并运行下面的程序 #include 整理自网络 C语言运算符 C语言的内部运算符很丰富,运算符是告诉编译程序执行特定算术或逻辑操作的符号。C语言有三大运算符:算术、关系与逻辑、位操作。另外,C还有一些特殊的运算符,用于完成一些特殊的任务。 2.6.1算术运算符 表2-5列出了C语言中允许的算术运算符。在C语言中,运算符“+”、“-”、“*”和“/”的用法与大多数计算机语言的相同,几乎可用于所有C语言内定义的数据类型。当“/”被用于整数或字符时,结果取整。例如,在整数除法中,10/3=3。 一元减法的实际效果等于用-1乘单个操作数,即任何数值前放置减号将改变其符号。模运算符“%”在C语言中也同它在其它语言中的用法相同。切记,模运算取整数除法的余数,所以“%”不能用于float和double类型。 最后一行打印一个0和一个1,因为1/2整除时为0,余数为1,故1%2取余数1。 2.6.2自增和自减 C语言中有两个很有用的运算符,通常在其它计算机语言中是找不到它们的—自增和自减运算符,++和--。运算符“++”是操作数加1,而“--”是操作数减1,换句话说:x=x+1;同++x;x=x-1;同--x; 自增和自减运算符可用在操作数之前,也可放在其后,例如:x=x+1;可写成++x;或x++;但在表达式中这两种用法是有区别的。自增或自减运算符在操作数之前,C语言在引用操作数之前就先执行加1或减1操作;运算符在操作数之后,C语言就先引用操作数的值,而后再进行加1或减1操作。请看下例: x=10; ;y=++x; 此时,y=11。如果程序改为: x=10;y=x++; 则y=10。在这两种情况下,x都被置为11,但区别在于设置的时刻,这种对自增和自减发生时刻的控制是非常有用的。在大多数C编译程序中,为自增和自减操作生成的程序代码比等价的赋值语句生成的代码 要快得多,所以尽可能采用加1或减1运算符是一种好的选择。 。下面是算术运算符的优先级: :最高++、- -- -(一元减) *、/、%最低+、-编译程序对同级运算符按从左到右的顺序进行计算。当然,括号可改变计 c语言运算符优先级 Turbo C的运算符非常丰富, 主要分为三大类: 算术运算符, 关系运算符与 逻辑运算符, 按位运算符。除此之外, 还有一些用于完成特殊任务的运算符。下 面分别进行介绍。 5.1 算术运算符 Turbo C的算术运算符如下: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 操作符作用 ──────────────────────────── + 加, 一目取正 - 减, 一目取负 * 乘 / 除 % 取模 -- 减1 ++ 加1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 一、一目和二目操作 一目操作是指对一个操作数进行操作。例如: -a是对a进行一目负操作。 二目操作(或多目操作)是指两个操作数(或多个操作数)进行操作。 在Turbo C中加、减、乘、除、取模的运算与其它高级语言相同。需要注意 的是除法和取模运算。 例如: 15/2 是15除以2商的整数部分7 15%2 是15除以2的余数部分1 对于取模运算符"%", 不能用于浮点数。 另外, 由于Turbo C中字符型数会自动地转换成整型数, 因此字符型数也可 以参加二目运算。 例如: main() { char m, n; /*定义字符型变量*/ m='c'; /*给m赋小写字母'c'*/ n=m+'A'-'a'; /*将c中的小写字母变成大写字母'B'后赋给n*/ ... } 上例中m='c'即m=98, 由于字母A和a的ASCII码值分别为65和97。这样可以将 小写字母变成大写字母, 反之, 如果要将大写字母变成小写字母, 则用c+ 'a' -'A'进行计算。 二、增量运算 在Turbo C中有两个很有用的运算符, 在其它高级语言中通常没有。这两个 运算符就是增1和减1运算符"++"和"--", 运算符"++"是操作数加1, 而"--" 则是 操作数减1。 例如: x=x+1 可写成x++, 或++x x=x-1 可写成x--, 或--x x++(x--)与++x(--x)在上例中没有什么区别, 但x=m++和x=++m却有很大差别。 x=m++ 表示将m的值赋给x后, m加1。 x=++m 表示m先加1后, 再将新值赋给x。 三、赋值语句中的数据类型转换 类型转换是指不同类型的变量混用时的类型改变。 在赋值语句中, 类型转换规则是: 等号右边的值转换为等号左边变量所属的类型。 例如: main() { int i, j; /*定义整型变量*/ float f, g=2.58; /*定义浮点型变量*/ f=i*j; /*i与j的乘积是整型数, 被转换成为浮点数赋给f*/ i=g; /*g中的浮点型数转换成为整型数赋给i*/ ... } 由于Turbo C按上述数据类型转换规则, 因此在作除法运算时应特别注意。 例如: main() { float f; int i=15; f=i/2; } 上面程序经运行后, f=7并不等于准确值7.5。正确的程序应该是: main() { float f; int i=15; C 语言运算符与表达式的练习题 单项选择题 (i )以下选项中,正确的C 语言整型常量是( D )。 A. 32L ⑵以下选项中,( A. 'a' B. 510000 D )是不正确的 B. '\x41' C. -1.00 C 语言字符型常量。 C. '\101' (3)字符串的结束标志是( C )。 A. 0 B. '0' C. '\0' (4) 算术运算 符、赋值运算符和关系运算符的运算优先级按从高到低依次为( B )。 A. 算术运算、赋值运算、关系运算 B. 算术运算、关系运算、赋值运算 C. 关系运算、赋值运算、算术运算 D. 关系运算、算术运算、赋值运算 (5) 逻辑运算符中,运算优先级按从高到低 依次为( D )。 A. && ! || (6)表达式!x||a B. || && ! C. && || ! ==b 等效于( D )。 ! D. ! && || A. !((x||a)==b) B. !(x||y)==b C .!(x||(a= ==b)) D (!x)||(a==b) (7)设整型变 量 m, n,a,b,c,d 均为 1,执行(m=a>b)&&(n=c>d) 后,m,n 的值是( A A. 0,0 B. 0 ,1 C. 1 ,0 D. 1 , 1 *(8)设有语句 i nt a=3 ;,则执行了语句 a+=a-=a*=a; 后, 变量a 的值是( B A. 3 B. 0 C. 9 D. -12 (9) 在以下一组运算符中,优先级最低的运算 符是( D )。 A. * B. != C. + D.= (10) 设 整型变量i 值为2,表达式(++i )+(++i )+(++i ) 的结果是( B,上机13 ) A. 6 B. 12 C. 15 D. 表达式出错 D. 567 D. "a" D. "0" C语言中的表达式及其知识要点 绍兴文理学院沈红卫 C语言中数据类型、运算符等语法点最终归一到表达式(expression)。表达式是C语言的重点和难点。 何为表达式?表达式就是,由运算符、变量和常量组成的具有一个确定结果(值)的一个式子。由于C语言中运算符的广泛性和广义性,所以,C语言中的表达式也是广义的一个概念,与数学中的表达式既有相同之处,又有许多不同的地方。例如:y=x=3,这是一个赋值表达式;x+3,y*5,这也是一个表达式,是逗号表达式,因为“,”(逗号)在C里也是一个运算符。这些是数学所没有的。 说它是重点,是因为几乎所有语句都会涉及到表达式;说它是难点,是因为表达式的广义性,以及求解表达式的复杂性。 为了学好表达式,首先必须掌握C语言中的表达式的种类。C语言表达式的种类较多,主要有以下类型: 算术表达式:大家最熟悉的,例如:x+5*y,算术运算的表达式 关系表达式:x>=5,x<6,x==8,大小关系判断的表达式 逻辑表达式:ans==’y’ || ans==’Y’,与、或、非三种逻辑运算的表达式 赋值表达式:x=6+y,进行变量赋值的表达式 条件表达式:x>y?1:0,如果x>y则取1,否则取0 逗号表达式:x+3,y*5,z-3,有逗号运算符组成的表达式 表达式结果求解是必须掌握的内容。求解一个表达式,必须使用三条规则:一是混合运算规则;二是优先级规则;三是结合性规则。 混合运算规则:当表达式涉及到不同的数据类型时,必须使用该规则,以确定表达式的结果的类型。该规则即教材P35中的“表达式中的自动类型转换规则”。 优先级规则:表达式涉及到多种运算符、优先级又各不相同时,必须使用该规则,就好像数学中的“先乘除后加减”。每种运算符的优先级参见教材P434“附录C”。例如:假设x=3,则算术表达式x+x*5,先乘后加,结果等于18。再如:假设x=3,则逻辑表达式x>5 中山大学南方学院 电子通信与软件工程系 课程名称:高级语言程序设计实践实验题目:运算符和表达式 附:实验报告 专业:年级:完成日期: 学号:姓名:成绩: 一、实验目的 1、能够使用C语言进行简单的算术运算、关系运算和逻辑运算。 2、掌握不同的类型数据之间赋值的规律。 3、进一步熟悉C语言程序的编辑、编译和运行的过程。 二、实验原理 1、用int定义整型变量来存放整数; 2、用float定义浮点数变量来存放小数; 3、使用scanf() 函数从键盘输入两个整型数据,并赋值给两个变量。 三、实验过程 1、算术运算 实验代码如下: #include { int a=2, b=3; float x=3. 9, y=2. 3; float result; result=(float) (a+b) /2+(int) x%(int) y; return result } 输出结果如图: 2、算术运算 实验代码如下: #include number=number/10; a2=number%10; a3=number/10; printf("三位数%d的个位数字是%d,十位数字是%d,百位数字 是%d\n",number,a1, a2, a3) ; return 0; } 输出结果如图: 3、关系运算与逻辑运算 实验代码如下: #include 爸爸很忙,哪有时间啊,于是就让小瑜自己去了,糖果3角钱一块,爸爸给小瑜n 元钱,请你告诉小瑜最多能买几块糖,还剩几角钱? 输入:爸爸给小瑜的钱n元,n为整数。 输出:小瑜最多能买回的糖块数以及剩下的钱(单位为:角),用空格分隔。 #include 一、分类 C语言一共有34种运算符,10种运算类型:算术运算符(+、-、*、/、%)、关系运算符(>、>=、==、!=、<、<=)、位运算符(>>、<<、==、!=、<、<=)、逻辑运算符(!、||、&&)、条件运算符、(?:)指针运算符(&、*)、赋值运算符(=)、逗号运算符(,)、求字节运算符(sizeof)、强制类型转换运算符((类型名))、其他(下标[]、分量、函数);若按参与运算的对象个数,C 语言运算符可分为单目运算符(如!)、双目运算符(如+、-)和三目运算符(如?:) 二、运算符的结合性和优先级 1. 运算符的结合性 在C语言的运算符中,所有的单目运算符、条件运算符、赋值运算符及其扩展运算符,结合方向都是从右向左,其余运算符的结合方向是从左向右。 2.运算符的优先级 初等运算符(圆括号()、下标运算符[]、结构体成员运算符->)>单目运算符>算术运算符(先乘除后加减)>关系运算符>逻辑运算符(不包括!)条件运算符>赋值运算符>逗号运算符 三、算术运算符合和算术表达式 1.基本的算数运算符 (1). +加法运算或正值运算符 4+4、+5、 (2).- 减法运算或负值运算符 6-4、-10、-29 (3). *乘法运算 注意符号,不是x,而是* (4). /除法运算 注意符号,不是÷,也不是\,而是/ 整数除于整数,还是整数。1/2的值是0,这个并不是二分之一,不会四舍五入,直接截断取值。 (5)%取余运算 取余:即两个整数相除之后的余数 注意:%两侧只能是整数,正负性取决于%左侧的数值 2.注意: (1).当运算对象是负数时,不同机器的运算结果也可能是不同的。 (2).双目运算符两边的数值类型必须一致才能进行运算,所得结果也是相同类型的数值。(3).双目运算符两边的数值类型如果不一致,必须由系统先进行一致性转换。 转换规则:char->short->int->unsigned->long->double->float二级C语言-运算符与表达式
(完整word版)C语言运算符与表达式的练习题答案
C语言中条件运算符的语法为
C语言数据类型和运算符及表达式复习题
C语言程序设计实验报告实验数据类型运算符和表达式
C语言运算符与表达式的练习题
逻辑运算符和表达式(c语言)
C语言数据类型与表达式习题及答案
C语言题库第2章 数据类型运算符和表达式√
C语言运算符大全资料
C语言程序设计实验报告实验数据类型运算符和表达式
C语言程序设计实验报告实验数据类型运算符和表达式
C语言程序设计实验报告实验数据类型运算符和表达式
C语言全部的运算符
c语言运算符优先级
(完整word版)C语言运算符与表达式的练习题答案
C语言中的表达式及其知识要点
C语言运算符和表达式-实验报告
9C语言——运算符和表达式-1——教案
C语言运算符与表达式