c語言tlv
❶ 在pic中如何用C語言編寫程序
//09/10/24
//lcd1602顯示時間 日期 星期 溫度
//通過按鍵校時:K10--小時,K11--分鍾,K12--秒(歸零),K13-星期,BR1--年,RB2--月,RB3--日。
//晶元要求:PIC16F877A
#include<pic.h> //包含單片機內部資源預定義
__CONFIG(0x1832);
//晶元配置字,看門狗關,上電延時開,掉電檢測關,低壓編程關,加密,4M晶體HS振盪
#define i_o RB4 //定義DS1302的數據口
#define sclk RB0 //定義DS1302的時鍾口
#define rst RB5 //定義DS1302的復位口
#define rs RA1 //1602
#define rw RA2
#define e RA3
# define DQ RA0 //定義18B20數據埠
unsigned char TLV=0 ; //採集到的溫度高8位
unsigned char THV=0; //採集到的溫度低8位
unsigned char ;
unsigned char shi; //整數十位
unsigned char ge; //整數個位
unsigned char shifen; //十分位
float temp;
void display();
//定義讀取時間和日期存放表格
char table1[7];
//定義0-9的顯示代碼
const char table2[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
unsigned char rx_data,read_data,count,sec,min,hour,day,mon,week,year,time;
//----------------------------------------------
//ds18b20部分
//------------------------------------------------
//延時函數
void delay1(unsigned int x)
{
unsigned int i;
for(i=x;i>0;i--);
}
//------------------------------------------------
//延時函數
void delay2(char x,char y)
{
char z;
do{
z=y;
do{;}while(--z);
}while(--x);
}
//其指令時間為:7+(3*(Y-1)+7)*(X-1)如果再加上函數調用的call 指令、頁面設定、傳遞參數花掉的7 個指令。
//則是:14+(3*(Y-1)+7)*(X-1)。
//***************************************
//初始化ds18b20
void ds18b20_init()
{
char presence=1;
while(presence)
{
TRISA0=0; //主機拉至低電平
DQ=0;
delay2(2,99); //延時503us
TRISA0=1; //釋放匯流排等電阻拉高匯流排,並保持15~60us
delay2(2,8); //延時70us
if(DQ==1) presence=1; //沒有接收到應答信號,繼續復位
else presence=0; //接收到應答信號
delay2(2,60); //延時430us
}
}
//*****************************************************
//寫ds18b20
void ds18b20_write_byte(unsigned char code)
{
unsigned char i,k;
for(i=8;i>0;i--)
{
k=code&0x01;
TRISA0=0;
DQ=0; //數據線拉低產生時間片
asm("nop");
asm("nop");
if(k) DQ=1; //寫1則拉高數據電平
delay1(3); //延時42us,ds18b20對數據線采樣
asm("nop");
TRISA0=1; //采樣結束,釋放匯流排,拉高電平
code=code>>1;
delay1(7); //延時82us
}
}
//****************************************************
//讀ds18b20
unsigned char ds18b20_read_byte()
{
unsigned char i,k;
for(i=8;i>0;i--)
{
k=k>>1;
TRISA0=0;
DQ=0; //數據線拉低再拉高產生讀時間片
asm("nop");
asm("nop");
TRISA0=1;
asm("nop");
asm("nop");
if(DQ) k=k|0x80; //15us內要完成讀位
delay1(6); //延時72us後釋放匯流排
}
return (k);
}
//********************************************
//啟動溫度轉換函數
void get_temp()
{
int i;
signed int t;
TRISA0=1;
ds18b20_init(); //復位等待從機應答
ds18b20_write_byte(0XCC); //忽略ROM匹配
ds18b20_write_byte(0X44); //發送溫度轉化命令
for(i=2;i>0;i--)
{
display(); //調用多次顯示函數,確保溫度轉換完成所需要的時間
}
ds18b20_init(); //再次復位,等待從機應答
ds18b20_write_byte(0XCC); //忽略ROM匹配
ds18b20_write_byte(0XBE); //發送讀溫度命令
TLV=ds18b20_read_byte(); //讀出溫度低8
THV=ds18b20_read_byte(); //讀出溫度高8位
TRISA0=1; //釋放匯流排
t=THV<<8;
t=t|TLV;
if(t<0) //負溫度
{
temp=(~t+1)*0.0625*10+0.5; //負溫度時,取反加1再乘以0.0625得實際溫度,乘10+0.5顯示小數點一位,且四捨五入
}
else
temp=t*0.0625*10+0.5; //正溫度
if(t<0)
='-'; //負溫度時百位顯示負號
else
=(const) temp/1000+0x30; //百位
shi=((const) temp%1000)/100; //十位
ge=((const) temp%1000)%100/10; //個位
shifen=((const) temp%1000)%100%10; //十分位
NOP();
}
//---------------------------------------------
//------------DS1303部分-----------------------
//---------------------------------------------
//延時程序
void delay() //延時程序
{
int i; //定義整形變數
for(i=0x64;i--;); //延時
}
//寫一個位元組數據函數
void write_byte(unsigned char data)
{
int j; //設置循環變數
for(j=0;j<8;j++) //連續寫8bit
{
i_o=0; //先設置數據為0
sclk=0; //時鍾信號拉低
if(data&0x01) //判斷待發送的數據位是0或1
{
i_o=1; //待發送數據位是1
}
data=data>>1; //待發送的數據右移1位
sclk=1; //拉高時鍾信號
}
sclk=0; //寫完一個位元組,拉低時鍾信號
}
//---------------------------------------------
//讀一個位元組函數
unsigned char read_byte()
{
int j; //設置循環變數
TRISB4=1; //設置數據口方向為輸入
for(j=8;j--;) //連續讀取8bit
{
sclk=0; //拉低時鍾信號
rx_data=rx_data>>1; //接收寄存器右移1位
if(i_o==1) rx_data=rx_data|0x80;
sclk=1; //拉高時鍾信號
}
TRISB4=0; //恢復數據口方向為輸出
sclk=0; //拉低時鍾信號
return(rx_data); //返回讀取到的數據
}
//----------------------------------------------
//寫DS1302
void write_ds1302(unsigned char addr,unsigned char code)
{
rst=0;
sclk=0;
rst=1;
write_byte(addr);
write_byte(code);
sclk=0;
rst=1;
}
//-------------------------------------------
//讀DS1302
void read_ds1302(unsigned char addr)
{
rst=0;
sclk=0;
rst=1;
write_byte(addr);
read_data=read_byte();
//return read_data;
}
//---------------------------------------------
//讀取時間函數
void get_time()
{
int i; //設置循環變數
rst=1; //使能DS1302
write_byte(0xbf); //發送多位元組讀取命令
for(i=0;i<7;i++) //連續讀取7個位元組數據
{
table1[i]=read_byte(); //調用讀取1個位元組數據的函數
}
rst=0; //復位DS1302
}
//DS1302初始化函數
void ds1302_init()
{
sclk=0; //拉低時鍾信號
rst =0; //復位DS1302
rst=1; //使能DS1302
write_ds1302(0x8e,0); //發控制命令
rst=0; //復位
}
//---------------------------------------------
//設置時間函數
void set_time()
{
//定義待設置的時間: 秒、 分、 時、 日、月、星期、年、控制字
const char table[]={0x00,0x00,0x12,0x23,0x10,0x05,0x09,0x00};
int i; //定義循環變數
rst=1; //使能DS1302
write_byte(0xbe); //時鍾多位元組寫命令
for(i=0;i<8;i++) //連續寫8個位元組數據
{
write_byte(table[i]); //調用寫一個位元組函數
}
rst=0; //復位
}
//-------------------------------------------
//8位二進制數轉換為十進制數
void two_to_ten(unsigned char i)
{
time=(table1[i]&0x0f)+(table1[i]>>4)*0x0a;
}
//-------------------------------------------
//十進制數轉換為BCD碼
void ten_to_bcd(unsigned char i)
{
time=((i/0x0a)<<4)|(i%0x0a);
}
//------------------------------------------
//校時程序
void change_time()
{
if(RC0==0) //改變星期---k13
{
delay();
if(RC0==0)
{
if(count==0)
{
count=1;
two_to_ten(5);
week=time;
week++;
if(week>=8)
{
week==1;
write_ds1302(0x8A,1);
}
else
write_ds1302(0x8A,week);
}
}
}
else if(RC1==0) //秒歸零--k12
{
delay();
if(RC1==0)
{
if(count==0)
{
count=1;
write_ds1302(0x80,0);
}
}
}
else if(RC2==0) //改變分位--k11
{
delay();
if(RC2==0)
{
if(count==0)
{
count=1;
two_to_ten(1);//BCD碼轉換成十進制數
min=time;
min++;
if(min>=60)
{
min=0;
write_ds1302(0x82,min);
}
else
{
ten_to_bcd(min);//十進制數轉換為BCD碼存進DS1302
write_ds1302(0x82,time);
}
}
}
}
else if(RC3==0) //改變小時位--k10
{
delay();
if(RC3==0)
{
if(count==0)
{
count=1;
two_to_ten(2);//BCD碼轉換成十進制數
hour=time;
hour++;
if(hour>=24)
{
hour=0;
write_ds1302(0x84,hour);
}
else
{
ten_to_bcd(hour);
write_ds1302(0x84,time);
}
}
}
}
else if(RB2==0)
{
delay();
if(RB2==0)
{
if(count==0)
{
count=1;
two_to_ten(4);//BCD碼轉換成十進制數
mon=time;
mon++;
if(mon>=13)
{
mon=1;
write_ds1302(0x88,mon);
}
else
{
ten_to_bcd(mon);
write_ds1302(0x88,time);
}
}
}
}
else if(RB3==0)
{
delay();
if(RB3==0)
{
if(count==0)
{
count=1;
two_to_ten(3);//BCD碼轉換成十進制數
day=time;
day++;
if((table1[6]%4==0)&&(table1[4]==2)&&(day>=30)) //潤年2月
{
day=1;
write_ds1302(0x86,day);
}
else if(((table1[6]%4)!=0)&&(table1[4]==2)&&(day>=29))//非潤年的2月
{
day=1;
write_ds1302(0x86,day);
}
else if(((table1[4]==1)||(table1[4]==3)||(table1[4]==5)||(table1[4]==7)||(table1[4]==8)||(table1[4]==0x10)||(table1[4]==0x12))&&(day>=32))
{
day=1;
write_ds1302(0x86,day);
}
else if(((table1[4]==4)||(table1[4]==6)||(table1[4]==9)||(table1[4]==0x11))&&(day>=31))
{
day=1;
write_ds1302(0x86,day);
}
else
{
ten_to_bcd(day);
write_ds1302(0x86,time);
}
}
}
}
else if(RB1==0)
{
delay();
if(RB1==0)
{
if(count==0)
{
count=1;
two_to_ten(6);//BCD碼轉換成十進制數
year=time;
year++;
if(year>=16)
{
year=0x00;
write_ds1302(0x8c,0);
}
else
{
ten_to_bcd(year);
write_ds1302(0x8c,time);
}
}
}
}
else
count=0;
}
//****************************************
//**************lcd1602*******************
//****************************************
//延時程序
//void delay()
// {
// unsigned char i;
// for(i=100;i>0;i--);
// }
//****************************************
//LCD寫一個位元組數據
void write_lcd(unsigned char code)
{
PORTD=code;
rs=1;
rw=0;
e=0;
delay();
e=1;
}
//****************************************
//lcd寫命令函數
void lcd_enable(unsigned char code)
{
PORTD=code;
rs=0;
rw=0;
e=0;
delay();
e=1;
}
//*****************************************
//lcd顯示設置
void lcd_init()
{
lcd_enable(0x01); //清除顯示
lcd_enable(0x38); //設置16X2顯示,5X7點陣
lcd_enable(0x0c); //開顯示,不顯示游標
lcd_enable(0x06); //游標左移
}
//-------------------------------------------
//顯示函數
void display()
{
// PORTD=0X80; //小時
lcd_enable(0X80);
write_lcd((table1[2]>>4)+0x30);
// PORTD=0x81;
lcd_enable(0x81);
write_lcd((table1[2]&0x0f)+0x30);
// PORTD=0X82;
lcd_enable(0X82);
write_lcd(':');
// PORTD=0X83; //分
lcd_enable(0X83);
write_lcd((table1[1]>>4)+0x30);
// PORTD=0x84;
lcd_enable(0x84);
write_lcd((table1[1]&0x0f)+0x30);
// PORTD=0X85;
lcd_enable(0X85);
write_lcd(':');
// PORTD=0X86; //秒
lcd_enable(0X86);
write_lcd((table1[0]>>4)+0x30);
// PORTD=0x87;
lcd_enable(0x87);
write_lcd((table1[0]&0x0f)+0x30);
// PORTD=0X89; //溫度的百位
lcd_enable(0X89);
write_lcd();
// PORTD=0X8a; //溫度的十位
lcd_enable(0X8a);
write_lcd(shi+0x30);
// PORTD=0X8b; //溫度的個位
lcd_enable(0X8b);
write_lcd(ge+0x30);
// PORTD=0X8c;
lcd_enable(0X8c);
write_lcd('.');
// PORTD=0X8d; //溫度的十分位
lcd_enable(0X8d);
write_lcd(shifen+0x30);
// PORTD=0X8e; //顯示'C'
lcd_enable(0X8e);
write_lcd('C');
//
// PORTD=0XC0; //年
lcd_enable(0XC0);
write_lcd((table1[6]>>4)+0x30);
//PORTD=0XC1;
lcd_enable(0XC1);
write_lcd((table1[6]&0x0f)+0x30);
// PORTD=0XC2;
lcd_enable(0XC2);
write_lcd('-');
// PORTD=0XC3; //月
lcd_enable(0XC3);
write_lcd((table1[4]>>4)+0x30);
// PORTD=0xC4;
lcd_enable(0xC4);
write_lcd((table1[4]&0x0f)+0x30);
// PORTD=0XC5;
lcd_enable(0XC5);
write_lcd('-');
// PORTD=0XC6; //日
lcd_enable(0XC6);
write_lcd((table1[3]>>4)+0x30);
// PORTD=0xC7;
lcd_enable(0xC7);
write_lcd((table1[3]&0x0f)+0x30);
// PORTD=0XCD; //星期
lcd_enable(0XCD);
write_lcd((table1[5]&0x0f)+0x30);
}
//--------------------------------------------
//引腳定義函數
void port_init()
{
TRISA=0x00; //設置A口全輸出
TRISD=0X00; //設置D口全輸出
ADCON1=0X06; //設置A口為普通I/O口
TRISB=0X0E; //
OPTION=0X00; //開啟B口弱上拉
PORTA=0XFF;
PORTD=0XFF; //先熄滅所有顯示
lcd_init();
TRISC=0XEF; //RC3輸出,其他為輸入
PORTC=0XEF;
count=0;
}
//----------------------------------------------
//主函數
void main()
{
port_init(); //調用引腳初始化函數
read_ds1302(0x81); //查看DS1302是否起振
if(read_data&0x80) //否,則初始化DS1302
{
ds1302_init(); //調用DS1302初始化函數
set_time(); //調用設置時間函數
}
while(1)
{
get_time(); //調用取時間函數
change_time();
get_temp(); //調用溫度轉換函數
display(); //調用顯示函數
}
}
❷ c璇璦鎬庝箞瀹炵幇DSR鐨勫姛鑳斤紵
鍔ㄦ佹簮璺鐢卞崗璁(Dynamic Source Routing, DSR)鏄鍦ㄧЩ鍔ㄨ嚜緇勭綉(MANET)涓浣跨敤鐨勪竴縐嶈礬鐢卞崗璁銆傚畠宸ヤ綔鍦═CP/IP鍗忚鏃忕殑緗戦檯灞傘
銆銆鍔ㄦ佹簮璺鐢卞崗璁(Dynamic Source Routing Protocol,DSR)鏄涓涓涓撻棬涓哄氳煩鏃犵嚎Ad Hoc緗戠粶璁捐$殑綆鍗曚笖楂樻晥鐨勮礬鐢卞崗璁銆傛墍鏈夌殑璺鐢遍兘鏄鐢盌SR璺鐢卞崗璁鍔ㄦ佸湴銆佽嚜鍔ㄥ湴紜瀹氬拰緇存姢錛屽畠鎻愪緵蹇閫熷弽搴斿紡鏈嶅姟錛屼互渚垮府鍔╃『淇濇暟鎹鍒嗙粍鐨勬垚鍔熶氦浠橈紝鍗充嬌鍦ㄨ妭鐐圭Щ鍔ㄦ垨鑰呭叾浠栫綉緇滅姸鍐靛彉鍖栫殑鏉′歡涓嬩篃鏄濡傛ゃ
銆銆DSR璺鐢卞崗璁鏈変袱涓涓昏佹満鍒剁粍鎴愨斺旇礬鐢卞繪壘(Route Discovery錛夋満鍒跺拰璺鐢辯淮鎶(RouteMaintenance)鏈哄埗銆傝礬鐢卞繪壘鏈哄埗鍦ㄦ簮鑺傜偣闇瑕佺粰鐩鐨勮妭鐐瑰彂閫佷竴涓鍒嗙粍騫朵笖榪樹笉鐭ラ亾鍒拌揪鐩鐨勮妭鐐圭殑璺鐢辯殑鏃跺欎嬌鐢ㄣ傚綋婧愯妭鐐規e湪浣跨敤涓鏉″埌杈劇洰鐨勮妭鐐圭殑婧愯礬鐢辯殑鏃跺欙紝婧愯妭鐐逛嬌鐢ㄨ礬鐢辯淮鎶ゆ満鍒跺彲浠ユ嫻嬪嚭鍥犱負鎷撴墤鍙樺寲涓嶈兘浣跨敤鐨勮礬鐢憋紝褰撹礬鐢辯淮鎶ゆ寚鍑轟竴鏉℃簮璺鐢卞凡緇忎腑鏂鑰屼笉鍐嶈搗浣滅敤鐨勬椂鍊欙紝涓轟簡灝嗛殢鍚庣殑鏁版嵁鍒嗙粍浼犺緭鍒扮洰鐨勮妭鐐癸紝婧愯妭鐐硅兘澶熷敖鍔涗嬌鐢ㄤ竴鏉″伓鐒惰幏鐭ョ殑鍒拌揪鐩鐨勮妭鐐圭殑璺鐢憋紝鎴栬呴噸鏂拌皟鐢ㄨ礬鐢卞繪壘鏈哄埗鎵懼埌涓鏉℃柊璺鐢便傚湪DSR璺鐢卞崗璁涓錛岃礬鐢卞繪壘鏈哄埗鍜岃礬鐢辯淮鎶ゆ満鍒跺潎鏄瀹屽叏鎸夐渶鎿嶄綔錛屼笉闇瑕佹煇涓緗戠粶灞傛$殑鏌愮嶅懆鏈熷垎緇勶紝濡侱SR涓嶉渶瑕佷換浣曞懆鏈熸х殑璺鐢卞箍鎾鍒嗙粍銆侀摼璺鐘舵佹帰嫻嬪垎緇勩侱SR璺鐢卞崗璁鐨勭殑鎵鏈夌姸鎬侀兘鏄鈥滆蔣鐘舵佲濓紝鍥犱負浠諱綍鐘舵佺殑涓㈠け閮戒笉浼氬獎鍝岲SR璺鐢卞崗璁鐨勬g『鎿嶄綔錛屽洜涓烘墍鏈夌姸鎬侀兘鏄鎸夐渶寤虹珛錛屾墍鏈夌姸鎬佸湪涓㈠け涔嬪悗濡傛灉浠嶇劧闇瑕佺殑璇濆垯鑳藉熷緢瀹規槗寰楀埌榪呴熸仮澶嶃侱SR璺鐢卞崗璁鐨勮礬鐢卞繪壘鏈哄埗鍜岃礬鐢辯淮鎶ゆ満鍒剁殑鎿嶄綔鏄鐨勫崟鍚戦摼鍜屼笉瀵圭О璺鐢卞緢瀹規槗寰楀埌鏀鎸併
銆銆DSR璺鐢卞崗璁鐨勫畬鏁寸増鏈鐩存帴浣跨敤鈥滄簮璺鐢扁濓紝鑺傜偣浣跨敤璺鐢辯紦鍐插櫒瀛樺偍鑺傜偣鎵鐭ョ殑婧愯礬鐢憋紝褰撳彂鐜版柊璺鐢辨椂錛屾洿鏂扮紦鍐插櫒鍐呯殑鏉$洰銆傝妭鐐規墍鍙戦佺殑姣忎釜鏁版嵁鍒嗙粍鍧囧湪鍏跺垎緇勫ご涓鎼哄甫鍏跺皢瑕侀氳繃鐨勪竴涓瀹屾暣鐨勩佹寜搴忔帓鍒楃殑璺鐢變俊鎮銆
銆銆DSR閫夐」澶存牸寮忓備笅錛
銆銆瀹冪殑棣栭儴閲囩敤鎵╁睍鎬ц壇濂界殑TLV鏍煎紡銆傞櫎鍥哄畾閮ㄥ垎澶栵紝涓嶅悓綾誨瀷鐨勯夐」(option)浠TLV鏍煎紡闄勫姞鍦ㄥ滻瀹氶儴鍒嗕箣鍚庛
銆銆閫夐」鐨勭嶇被鍖呮嫭錛
銆銆璺鐢辮鋒眰(Route Request)
銆銆璺鐢卞簲絳(Route Reply)
銆銆紜璁よ鋒眰(ACK Request)
銆銆紜璁(ACK)
銆銆婧愯礬鐢(Source Route)
❸ C語言怎麼寫底層.這是什麼情況
C語言的內存模型基本上對應了現在von Neumann(馮.諾伊曼)計算機的實際存儲模型,很好的達到了對機器的映射,這是C/C++適合做底層開發的主要原因,另外,C語言適合做底層開發還有另外一個原因,那就是C語言對底層操作做了很多的的支持,提供了很多比較底層的功能。
下面結合問題分別進行闡述。
問題:移位操作
在運用移位操作符時,有兩個問題必須要清楚:
(1)、在右移操作中,騰空位是填 0 還是符號位;
(2)、什麼數可以作移位的位數。
答案與分析:
">>"和"<<"是指將變數中的每一位向右或向左移動, 其通常形式為:
右移: 變數名>>移位的位數
左移: 變數名<<移位的位數
經過移位後, 一端的位被"擠掉",而另一端空出的位以0 填補,在C語言中的移位不是循環移動的。
(1) 第一個問題的答案很簡單,但要根據不同的情況而定。如果被移位的是無符號數,則填 0 。如果是有符號數,那麼可能填 0 或符號位。如果你想解決右移操作中騰空位的填充問題,就把變數聲明為無符號型,這樣騰空位會被置 0。
(2) 第二個問題的答案也很簡單:如果移動 n 位,那麼移位的位數要不小於 0 ,並且一定要小於 n 。這樣就不會在一次操作中把所有數據都移走。
比如,如果整型數據占 32 位,n 是一整型數據,則 n << 31 和 n << 0 都合法,而 n << 32 和 n << -1 都不合法。
注意即使騰空位填符號位,有符號整數的右移也不相當與除以 。為了證明這一點,我們可以想一下 -1 >> 1 不可能為 0 。
問題:位段結構
struct RPR_ATD_TLV_HEADER
{
ULONG res1:6;
ULONG type:10;
ULONG res1:6;
ULONG length:10;
};
位段結構是一種特殊的結構, 在需按位訪問一個位元組或字的多個位時, 位結構比按位運算符更加方便。
位結構定義的一般形式為:
struct位結構名{
數據類型 變數名: 整型常數;
數據類型 變數名: 整型常數;
} 位結構變數;
其中: 整型常數必須是非負的整數, 范圍是0~15, 表示二進制位的個數, 即表示有多少位。
變數名是選擇項, 可以不命名, 這樣規定是為了排列需要。
例如: 下面定義了一個位結構。
struct{
unsigned incon: 8; /*incon佔用低位元組的0~7共8位*/
unsigned txcolor: 4;/*txcolor佔用高位元組的0~3位共4位*/
unsigned bgcolor: 3;/*bgcolor佔用高位元組的4~6位共3位*/
unsigned blink: 1; /*blink佔用高位元組的第7位*/
}ch;
位結構爛穗猛成員的訪問與結構成員的訪問相同。
例如: 訪問上例飢橋位結構中的bgcolor成員可寫成:
ch.bgcolor
位結構成員可以與其它結構成員一起使用。 按位訪問與設置,方便&節省
例如:
struct info{
char name[8];
int age;
struct addr address;
float pay;
unsigned state: 1;
unsigned pay: 1;
}workers;'
上例的結構定義了關於一個工從的信息。其中有兩個位結構成員, 每個位結構成員只有一位, 因此只佔一個位元組但保存了兩個信息, 該位元組中第一位表示工人的狀態, 第二位表示工資是否已發放。由此可見使用位結構可以節省存貯空間。
注意不要超過值限制
問題:位元組對齊
我在使用VC編程的過程中,有一次調用DLL中定義的結構時,發覺結構都亂掉了,完全不能讀取正確的值,後來發現這是因為DLL和調用程序使用的位元組對齊選項不同,那麼我想問一下,位元組對齊究竟是怎麼一回事?
答案與分析:
關於位元組對齊:
1、 當不同的族歲結構使用不同的位元組對齊定義時,可能導致它們之間交互變得很困難。
2、 在跨CPU進行通信時,可以使用位元組對齊來保證唯一性,諸如通訊協議、寫驅動程序時候寄存器的結構等。
三種對齊方式:
1、 自然對齊方式(Natural Alignment):與該數據類型的大小相等。
2、 指定對齊方式 :
#pragma pack(8) //指定Align為 8;
#pragma pack() //恢復到原先值
3、 實際對齊方式:
Actual Align = min ( Order Align, Natual Align )
對於復雜數據類型(比如結構等):實際對齊方式是其成員最大的實際對齊方式:
Actual Align = max( Actual align1,2,3,…)
編譯器的填充規律:
1、 成員為成員Actual Align的整數倍,在前面加Padding。
成員Actual Align = min( 結構Actual Align,設定對齊方式)
2、 結構為結構Actual Align的整數倍,在後面加Padding.
例子分析:
#pragma pack(8) //指定Align為 8
struct STest1
{
char ch1;
long lo1;
char ch2;
} test1;
#pragma pack()
現在
Align of STest1 = 4 , sizeof STest1 = 12 ( 4 * 3 )
test1在內存中的排列如下( FF 為 padding ):
00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- --
01 FF FF FF 01 01 01 01 01 FF FF FF
ch1 -- lo1 -- ch2
#pragma pack(2) //指定Align為 2
struct STest2
{
char ch3;
STest1 test;
} test2;
#pragma pack()
現在 Align of STest1 = 2, Align of STest2 = 2 , sizeof STest2 = 14 ( 7 * 2 )
test2在內存中的排列如下:
00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- --
02 FF 01 FF FF FF 01 01 01 01 01 FF FF FF
ch3 ch1 -- lo1 -- ch2
注意事項:
1、 這樣一來,編譯器無法為特定平台做優化,如果效率非常重要,就盡量不要使用#pragma pack,如果必須使用,也最好僅在需要的地方進行設置。
2、 需要加pack的地方一定要在定義結構的頭文件中加,不要依賴命令行選項,因為如果很多人使用該頭文件,並不是每個人都知道應該pack。這特別表現在為別人開發庫文件時,如果一個庫函數使用了struct作為其參數,當調用者與庫文件開發者使用不同的pack時,就會造成錯誤,而且該類錯誤很不好查。
3、 在VC及BC提供的頭文件中,除了能正好對齊在四位元組上的結構外,都加了pack,否則我們編的Windows程序哪一個也不會正常運行。
4、 在 #pragma pack(n) 後一定不要include其他頭文件,若包含的頭文件中改變了align值,將產生非預期結果。
5、 不要多人同時定義一個數據結構。這樣可以保證一致的pack值。 問題:按位運算符
C語言和其它高級語言不同的是它完全支持按位運算符。這與匯編語言的位操作有些相似。 C中按位運算符列出如下:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
操作符 作用
————————————————————————————
& 位邏輯與
位邏輯或
^ 位邏輯異或
- 位邏輯反
>> 右移
<< 左移
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
注意:
1、 按位運算是對位元組或字中的實際位進行檢測、設置或移位, 它只適用於字元型和整數型變數以及它們的變體, 對其它數據類型不適用。
2、 關系運算和邏輯運算表達式的結果只能是1或0。 而按位運算的結果可以取0或1以外的值。 要注意區別按位運算符和邏輯運算符的不同, 例如, 若x=7, 則x&&8 的值為真(兩個非零值相與仍為非零), 而x&8的值為0。
3、 與 ,&與&&,~與! 的關系
&、 和 ~ 操作符把它們的操作數當作一個為序列,按位單獨進行操作。比如:10 & 12 = 8,這是因為"&"操作符把 10 和 12 當作二進制描述 1010 和 1100 ,所以只有當兩個操作數的相同位同時為 1 時,產生的結果中相應位才為 1 。同理,10 12 = 14 ( 1110 ),通過補碼運算,~10 = -11 ( 11...110101 )。<以多少為一個位序列> &&、 和!操作符把它們的操作數當作"真"或"假",並且用 0 代表"假",任何非 0 值被認為是"真"。它們返回 1 代表"真",0 代表"假",對於"&&"和""操作符,如果左側的操作數的值就可以決定表達式的值,它們根本就不去計算右側的操作數。所以,!10 是 0 ,因為 10 非 0 ;10 && 12 是 1 ,因為 10 和 12 均非 0 ;10 12也是 1 ,因為 10 非 0 。並且,在最後一個表達式中,12 根本就沒被計算,在表達式 10 f( ) 中也是如此。