在Terminal使用su指令切換卻發現密碼錯誤。
要解決此問題請依照下面步驟:
1. 在終端輸入 sudo passwd root <----這一步驟會叫你先輸入當前使用者的密碼
2. 接著會出現"Enter new UNIX password:",這時請輸入要設定的root的密碼
3. 然後再輸入comfirm一次即可
下次你再輸入su時,鍵入設定的密碼即可切換成root身分
整個流程如下圖所示:
***補充
後來發現想要直接切換成root其實不用這麼麻煩
只需要下"sudo su -"
接下來會要求你輸入當前使用者的密碼,輸入成功後就切換成root了
2015年3月23日 星期一
2015年3月19日 星期四
宣告struct的兩種方式(struct與typedef struct)/struct給值的兩種方式
之前寫了許多struct的程式,因為趕project沒有對這些寫法特別去研究(多數是copy, paste Orz),現在比較有空了,就研究了一下並且做個紀錄。
首先是宣告struct的方法,分別有兩種方式首先講第一種(struct):
假設現在要宣告一個student的一個結構(struct),裡面包含學生的學號(id)以及年紀(age)可以這樣子寫=====>
struct Student{ //定義
int id;
int age;
};
而使用的寫法如下===>
struct Student stu[2]; //宣告
stu[0].id=903135; //給值
stu[0].age=19;
stu[1].id=903137;
stu[1].age=20;
再講第二種寫法之前,稍微說明一下typedef這個東西,它的用法如下:
typedef int _Uint32; //把_Uint32這個字串定義為等同於int
_Uint32 i=0; //用_Uint32宣告整數變數
我相信大家對這種用法都滿熟悉的~
接下來如果使用第二種(typedef struct)的方式的話:
是這樣子寫的====>
typedef struct{ //定義
int id;
int age;
}Student;
上面的寫法可以看成Student被定義成是種struct型態(Student代表struct Student),並且成員有id與age。
因此使用的方式是這樣子寫的====>
Student stu[2]; //宣告
stu[0].id=903135; //給值
stu[0].age=19;
stu[1].id=903137;
stu[1].age=20;
也可以寫成這樣:
Student stu[2] = {
{903135, 19},{903137, 20}
}
====================分隔線======================
另外一個Topic是struct給值的方法,上面的給值方式是我們比較常看到的,另外還有一種進階的寫法,是這樣子寫的====>
struct Student{ //定義
int id;
int age;
};
struct Student stu[2]=
{
[0]={
.id=903135,
.age=19,
},
[1]={
.id=903137,
.age=20,
},
}
**注意這種寫法式宣告同時就給值,給值的部分都是用逗號區隔喔(不是分號哦)!**
以下程式是實作的範例
//================= struct_example.c =================//
#include <stdio.h>
struct staff
{
int id;
int age;
};
typedef struct
{
int id;
int age;
}student;
int main()
{
int i;
// ==========================================
// struct first define usage
struct staff sta[2]=
{
[0]={
.id=100001,
.age=22,
},
[1]={
.id=100002,
.age=23,
},
};
// ==========================================
// ==========================================
// struct another define usage
student stu[2]={
[0]={
.id=903135,
.age=19,
},
[1]={
.id=903137,
.age=20,
},
};
// ============================================
for(i=0; i<2; i++)
{
printf("struct staff[%d].id=%d\n", i+1, sta[i].id);
printf("struct staff[%d].age=%d\n", i+1, sta[i].age);
printf("\n");
}
printf("\n");
for(i=0; i<2; i++)
{
printf("struct student[%d].id=%d\n", i+1, stu[i].id);
printf("struct student[%d].age=%d\n", i+1, stu[i].age);
printf("\n");
}
return 0;
}
首先是宣告struct的方法,分別有兩種方式首先講第一種(struct):
假設現在要宣告一個student的一個結構(struct),裡面包含學生的學號(id)以及年紀(age)可以這樣子寫=====>
struct Student{ //定義
int id;
int age;
};
而使用的寫法如下===>
struct Student stu[2]; //宣告
stu[0].id=903135; //給值
stu[0].age=19;
stu[1].id=903137;
stu[1].age=20;
再講第二種寫法之前,稍微說明一下typedef這個東西,它的用法如下:
typedef int _Uint32; //把_Uint32這個字串定義為等同於int
_Uint32 i=0; //用_Uint32宣告整數變數
我相信大家對這種用法都滿熟悉的~
接下來如果使用第二種(typedef struct)的方式的話:
是這樣子寫的====>
typedef struct{ //定義
int id;
int age;
}Student;
上面的寫法可以看成Student被定義成是種struct型態(Student代表struct Student),並且成員有id與age。
因此使用的方式是這樣子寫的====>
Student stu[2]; //宣告
stu[0].id=903135; //給值
stu[0].age=19;
stu[1].id=903137;
stu[1].age=20;
也可以寫成這樣:
Student stu[2] = {
{903135, 19},{903137, 20}
}
====================分隔線======================
另外一個Topic是struct給值的方法,上面的給值方式是我們比較常看到的,另外還有一種進階的寫法,是這樣子寫的====>
struct Student{ //定義
int id;
int age;
};
struct Student stu[2]=
{
[0]={
.id=903135,
.age=19,
},
[1]={
.id=903137,
.age=20,
},
}
**注意這種寫法式宣告同時就給值,給值的部分都是用逗號區隔喔(不是分號哦)!**
以下程式是實作的範例
//================= struct_example.c =================//
#include <stdio.h>
struct staff
{
int id;
int age;
};
typedef struct
{
int id;
int age;
}student;
int main()
{
int i;
// ==========================================
// struct first define usage
struct staff sta[2]=
{
[0]={
.id=100001,
.age=22,
},
[1]={
.id=100002,
.age=23,
},
};
// ==========================================
// ==========================================
// struct another define usage
student stu[2]={
[0]={
.id=903135,
.age=19,
},
[1]={
.id=903137,
.age=20,
},
};
// ============================================
for(i=0; i<2; i++)
{
printf("struct staff[%d].id=%d\n", i+1, sta[i].id);
printf("struct staff[%d].age=%d\n", i+1, sta[i].age);
printf("\n");
}
printf("\n");
for(i=0; i<2; i++)
{
printf("struct student[%d].id=%d\n", i+1, stu[i].id);
printf("struct student[%d].age=%d\n", i+1, stu[i].age);
printf("\n");
}
return 0;
}
//====================== End =======================//
輸出結果如下:
2015年3月18日 星期三
在linux系統下,"ls -l"前面的符號或英文分別對應的檔案類型
在linux裡面,下"la -l"會看到如下圖當中的資訊:
最左邊列出的第一個字元,我們最常看到的標誌 為" d 或 - ",分別代表目錄(directory)或檔案為此檔案的種類,以下的內容擷取自鳥哥,詳細的說明了各種符號或是字母所代表的檔案類型:
Linux 的檔案種類主要有底下這幾種:
正規檔案( regular file ):就是一般類型的檔案,在由 ls –al 所顯示出來的屬性方面,第一個屬性為 [ - ]。另外,依照檔案的內容,又大略可以分為兩種檔案種類:
目錄 (directory):就是目錄啦!第一個屬性為 [ d ];
連結檔 (link):就是類似 Windows 底下的捷徑啦!第一個屬性為 [ l ];
設備檔 (device):與系統周邊相關的一些檔案,通常都集中在 /dev 這個目錄之下!通常又分為兩種:
那麼使用剛剛的『 ls -al 』這個指令,你就可以簡單的經由判斷每一個檔案的第一個屬性來瞭解這個檔案是何種類型!很簡單吧!
除了設備檔是我們系統中很重要的檔案,最好不要隨意修改之外(通常他也不會讓你修改的啦!),另一個比較有趣的檔案就是連結檔。如果你常常將應用程式捉到桌面來的話,你就應該知道在 Windows 底下有所謂的『捷徑』。同樣的,你可以將 linux 下的連結檔簡單的視為一個檔案或目錄的捷徑。但是基本上這兩個東西是不一樣的!在 Windows 系統的捷徑中,你將無法修改主程式,但是在 Linux 中,連結檔可以直接連結到主程式,因此你只要改了這個連結檔,則主程式亦被改變了!
最左邊列出的第一個字元,我們最常看到的標誌 為" d 或 - ",分別代表目錄(directory)或檔案為此檔案的種類,以下的內容擷取自鳥哥,詳細的說明了各種符號或是字母所代表的檔案類型:
Linux 的檔案種類主要有底下這幾種:
正規檔案( regular file ):就是一般類型的檔案,在由 ls –al 所顯示出來的屬性方面,第一個屬性為 [ - ]。另外,依照檔案的內容,又大略可以分為兩種檔案種類:
- 純文字檔(ascii) :這是 Unix 系統中最多的一種囉,幾乎只要我們可以用來做為設定的檔案都屬於這一種;
- 二進位檔(binary) :通常執行檔除了 scripts (文字型批次檔)之外,就是這一種檔案格式;
目錄 (directory):就是目錄啦!第一個屬性為 [ d ];
連結檔 (link):就是類似 Windows 底下的捷徑啦!第一個屬性為 [ l ];
設備檔 (device):與系統周邊相關的一些檔案,通常都集中在 /dev 這個目錄之下!通常又分為兩種:
- 區塊 (block) 設備檔 :就是一些儲存資料,以提供系統存取的周邊設備,簡單的說就是硬碟啦!例如你的一號硬碟的代碼是 /dev/hda1 等等的檔案啦!第一個屬性為 [ b ];
- 字元 (character) 設備檔 :亦即是一些序列埠的周邊設備,例如鍵盤、滑鼠等等!第一個屬性為 [ c ]。
那麼使用剛剛的『 ls -al 』這個指令,你就可以簡單的經由判斷每一個檔案的第一個屬性來瞭解這個檔案是何種類型!很簡單吧!
除了設備檔是我們系統中很重要的檔案,最好不要隨意修改之外(通常他也不會讓你修改的啦!),另一個比較有趣的檔案就是連結檔。如果你常常將應用程式捉到桌面來的話,你就應該知道在 Windows 底下有所謂的『捷徑』。同樣的,你可以將 linux 下的連結檔簡單的視為一個檔案或目錄的捷徑。但是基本上這兩個東西是不一樣的!在 Windows 系統的捷徑中,你將無法修改主程式,但是在 Linux 中,連結檔可以直接連結到主程式,因此你只要改了這個連結檔,則主程式亦被改變了!
a++與++a的差異
a++是先調用a的值,再把a的值加1; 換句話說a++是先處理其他的運算,然後a再加1
而++a則是先把a的值加1,再調用a的值; 也就是說++a是先把本身的值加1,然後再處理其他運算
接著看下面的兩個例子就可以清楚的了解:
範例一:
#include<stdio.h>
int main()
{
int a,b,c;
a=5;
b=a++;
printf("round_one a=%d, b=%d\n", a, b);
c=++a;
printf("round_two a=%d, c=%d\n", a, c);
return 0
}
瞭解了嗎,輸出結果如下===>
round_one a=6, b=5
round_two a=7, c=7
範例二:
#include <stdio.h>
int main ()
{
int a=5;
int b=10;
printf("a=%d\n",a++); //印出a=5
printf("a=%d\n",a); //印出a=6
printf("b=%d\n",++b); //印出b=11
printf("b=%d\n",b); //印出b=11
return 0;
}
而++a則是先把a的值加1,再調用a的值; 也就是說++a是先把本身的值加1,然後再處理其他運算
接著看下面的兩個例子就可以清楚的了解:
範例一:
#include<stdio.h>
int main()
{
int a,b,c;
a=5;
b=a++;
printf("round_one a=%d, b=%d\n", a, b);
c=++a;
printf("round_two a=%d, c=%d\n", a, c);
return 0
}
瞭解了嗎,輸出結果如下===>
round_one a=6, b=5
round_two a=7, c=7
範例二:
#include <stdio.h>
int main ()
{
int a=5;
int b=10;
printf("a=%d\n",a++); //印出a=5
printf("a=%d\n",a); //印出a=6
printf("b=%d\n",++b); //印出b=11
printf("b=%d\n",b); //印出b=11
return 0;
}
2015年3月16日 星期一
NOR Flash與NAND Flash介紹
幾乎每個人都聽過CF卡(Compact Flash)跟SD卡,在各種消費性產品上,例如數位相機或是手機上面都可以看到。這些記憶卡模組的底層技術,使用的就是快閃記憶體(Flash memory)。我們可以把它想像成類似固態硬碟的東西,可以在很小的空間裡儲存好幾MB、甚至好幾GB的資料,裡面沒有會動的碟片,很堅固耐用,而且只需單一電壓即可運作。接下來我們來了解一下快閃記憶體一些特別的地方。
快閃記憶體被劃分成一塊塊相當大的可抹除單位,稱之為抹除區塊(erase block)。要區別各種快閃記憶體技術的一項決定性特徵,就是快閃記憶體是如何寫入與抹除資料的。
一. NOR Flash
拿一顆普通的NOR Flash來說,經由軟體下指令直接把資料寫進某位置,我們就可以把一個位元的資料從1改成0,一次改一個位元(bit)或是一個字組(word)都可以。不過想從0改回1,就要先用一串特殊指令先把整個抹除區塊清掉才行。
普通的NOR Flash被劃分為很多個抹除區塊,例如: 一個4MB的晶片可能有64個抹除區塊,每個大小為64KB,有些快閃記憶體的抹除區塊大小可以不同,這樣一來資料儲存時就更有彈性了,這些通常被稱為啟動區塊(boot block)或是啟動區段(boot sector)快閃記憶體晶片。開機載入程式位於較小的區塊,核心跟其他必備資料夾放在較大的區塊。
想修改快閃記憶體中的某資料,必須先把資料所在的區塊整個抹除掉,即使只有1byte的資料需要更動,還是需要把整個區塊抹除掉,然後再把資料寫進去。跟硬碟磁區的大小相比,硬碟可單一寫入的磁區單位大小只有512byte或是1024byte,這一點的差異就可以知道NOR Flash更新資料所需花費的寫入時間會是硬碟的好幾倍。
二. NAND Flash
NAND與NOR兩者的差異跟快閃記憶體內部的單元架構有關,NAND比NOR的快閃記憶體少了些限制,因為區塊可較小,所以寫入速度就變快了,使用起來也更有效率。
NOR Flash跟微處理器之間的介面,也跟其他和微處理器作連接的裝置很類似,它們都有獨立運作的資料與位置匯流排,直接跟微處理器的資料/位址匯流排相連,快閃記憶體陣列中任何1 byte或word都可用某位置標示出來。相對而言,NAND Flash的裝置是以串列的方式作傳輸,介面很複雜,每家廠商作法都不同。使用NAND的裝置其操作模式比較類似傳統的硬碟與其控制器之間的傳輸介面,資料以一段段資料脈衝的方試存取,跟NOR的區塊比起來小多了,NAND的寫入時間比NOR長上10倍,不過抹除時間快很多。
總結:
NOR可以直接被微處理器存取,放在NOR上的程式碼甚至可以直接被執行(不過基於效能考量很少這麼作,除非是在資源極少的系統上),實際上很多處理器都沒辦法像存取動態記憶體(DRAM)那樣,對快閃記憶體進行快取,所以執行起來速度就更慢了。另一方面,NAND比較適合用在以整批整塊方式儲存的那種檔案系統,不適合用在儲存二進位執行檔程式碼跟資料上。
Reference by: [嵌入式系統開發實務]~
快閃記憶體被劃分成一塊塊相當大的可抹除單位,稱之為抹除區塊(erase block)。要區別各種快閃記憶體技術的一項決定性特徵,就是快閃記憶體是如何寫入與抹除資料的。
一. NOR Flash
拿一顆普通的NOR Flash來說,經由軟體下指令直接把資料寫進某位置,我們就可以把一個位元的資料從1改成0,一次改一個位元(bit)或是一個字組(word)都可以。不過想從0改回1,就要先用一串特殊指令先把整個抹除區塊清掉才行。
普通的NOR Flash被劃分為很多個抹除區塊,例如: 一個4MB的晶片可能有64個抹除區塊,每個大小為64KB,有些快閃記憶體的抹除區塊大小可以不同,這樣一來資料儲存時就更有彈性了,這些通常被稱為啟動區塊(boot block)或是啟動區段(boot sector)快閃記憶體晶片。開機載入程式位於較小的區塊,核心跟其他必備資料夾放在較大的區塊。
想修改快閃記憶體中的某資料,必須先把資料所在的區塊整個抹除掉,即使只有1byte的資料需要更動,還是需要把整個區塊抹除掉,然後再把資料寫進去。跟硬碟磁區的大小相比,硬碟可單一寫入的磁區單位大小只有512byte或是1024byte,這一點的差異就可以知道NOR Flash更新資料所需花費的寫入時間會是硬碟的好幾倍。
二. NAND Flash
NAND與NOR兩者的差異跟快閃記憶體內部的單元架構有關,NAND比NOR的快閃記憶體少了些限制,因為區塊可較小,所以寫入速度就變快了,使用起來也更有效率。
NOR Flash跟微處理器之間的介面,也跟其他和微處理器作連接的裝置很類似,它們都有獨立運作的資料與位置匯流排,直接跟微處理器的資料/位址匯流排相連,快閃記憶體陣列中任何1 byte或word都可用某位置標示出來。相對而言,NAND Flash的裝置是以串列的方式作傳輸,介面很複雜,每家廠商作法都不同。使用NAND的裝置其操作模式比較類似傳統的硬碟與其控制器之間的傳輸介面,資料以一段段資料脈衝的方試存取,跟NOR的區塊比起來小多了,NAND的寫入時間比NOR長上10倍,不過抹除時間快很多。
總結:
NOR可以直接被微處理器存取,放在NOR上的程式碼甚至可以直接被執行(不過基於效能考量很少這麼作,除非是在資源極少的系統上),實際上很多處理器都沒辦法像存取動態記憶體(DRAM)那樣,對快閃記憶體進行快取,所以執行起來速度就更慢了。另一方面,NAND比較適合用在以整批整塊方式儲存的那種檔案系統,不適合用在儲存二進位執行檔程式碼跟資料上。
Reference by: [嵌入式系統開發實務]~
2015年3月13日 星期五
2015年3月10日 星期二
字串反轉-範例練習(錯誤發掘&修正)
一開始在練習這個範例的時候我是這樣寫的:
/******************** reverse function exercise has two error*****************/
#include <stdio.h>
#include <string.h>
int reverse(char *str)
{
int i,j;
char reg;
j = strlen(str);
for(i=0; i<j; i++)
{
reg = str[i];
str[i] =str[j];
str[j] = reg;
j--;
}
return 0;
}
int main()
{
char *m_str = "12345";
reverse(m_str);
printf("After reversing m_str=%s\n", m_str);
return 0;
}
/**********************************************************/
上面的程式,編譯過程很順利,但是執行起來卻有兩個嚴重的錯誤,先賣個關子,各位看倌有看出來嗎?
首先第一個錯誤======> 執行後會看到錯誤訊息 "Segmentation fault",發生原因在於main裡面的 char *m_str= "12345"宣告,這樣的宣告方式只宣告了指標,但是並沒有實體位置,因此會有這個錯誤訊息。
上面問題解決之後,編譯完成後再執行一次執行檔,輸出結果如下:
After reversing m_str=
m_str的值竟然會是空的! 這是第二個錯誤的地方。
而發生的原因是因為reverse() 這隻function裡面,在計算str的長度時忘了減1。解釋如下:
原始的str內容===> str[0]='1', str[1]='2', str[2]='3', str[3]='4', str[4]='5' 另外還有一個結尾字元 str[5]='/0'
因此在字元陣列交換時誤把str[5]的'/0'交換到了str[0],因此printf出來才會是空的值。
把這兩個問題修改過後,就沒有問題了,正確的程式碼如下:
/********************reverse function exercise*************************************/
#include <stdio.h>
#include <string.h>
int reverse(char *str)
{
int i,j;
char reg;
j = strlen(str)-1;
for(i=0; i<j; i++)
{
reg = str[i];
str[i] =str[j];
str[j] = reg;
j--;
}
return 0;
}
int main()
{
char m_str[] = "12345";
reverse(m_str);
printf("After reversing m_str=%s\n", m_str);
return 0;
}
/**********************************************************/
/******************** reverse function exercise has two error*****************/
#include <stdio.h>
#include <string.h>
int reverse(char *str)
{
int i,j;
char reg;
j = strlen(str);
for(i=0; i<j; i++)
{
reg = str[i];
str[i] =str[j];
str[j] = reg;
j--;
}
return 0;
}
int main()
{
char *m_str = "12345";
reverse(m_str);
printf("After reversing m_str=%s\n", m_str);
return 0;
}
/**********************************************************/
上面的程式,編譯過程很順利,但是執行起來卻有兩個嚴重的錯誤,先賣個關子,各位看倌有看出來嗎?
首先第一個錯誤======> 執行後會看到錯誤訊息 "Segmentation fault",發生原因在於main裡面的 char *m_str= "12345"宣告,這樣的宣告方式只宣告了指標,但是並沒有實體位置,因此會有這個錯誤訊息。
上面問題解決之後,編譯完成後再執行一次執行檔,輸出結果如下:
After reversing m_str=
m_str的值竟然會是空的! 這是第二個錯誤的地方。
而發生的原因是因為reverse() 這隻function裡面,在計算str的長度時忘了減1。解釋如下:
原始的str內容===> str[0]='1', str[1]='2', str[2]='3', str[3]='4', str[4]='5' 另外還有一個結尾字元 str[5]='/0'
因此在字元陣列交換時誤把str[5]的'/0'交換到了str[0],因此printf出來才會是空的值。
把這兩個問題修改過後,就沒有問題了,正確的程式碼如下:
/********************reverse function exercise*************************************/
#include <stdio.h>
#include <string.h>
int reverse(char *str)
{
int i,j;
char reg;
j = strlen(str)-1;
for(i=0; i<j; i++)
{
reg = str[i];
str[i] =str[j];
str[j] = reg;
j--;
}
return 0;
}
int main()
{
char m_str[] = "12345";
reverse(m_str);
printf("After reversing m_str=%s\n", m_str);
return 0;
}
/**********************************************************/
2015年3月6日 星期五
./configure 遇到的錯誤訊息 bin/sh^M: bad interpreter: No such file or directory
今天在下載libjpeg並要進行configure時出現了===> "bin/sh^M: bad interpreter: No such file or directory" 的錯誤訊息,解決了之後幫自己作一個紀錄。
會發生的原因:
原因是configure這個script是在windows系統下用記事本文件編寫的。不同系統的編碼格式所引起的。為了確認是這個原因造成,請作下面幾個動作:
1. vi 或是 vim這個configure檔案
2. 進到vi的 command mode(按下ESC)
3. 鍵入 :set fileformat 或是 :set ff
結果會看到 fileformat=dos
解決方法:
1. vi 或是 vim這個configure檔案
2. 進到vi的 command mode(按下ESC)
3. 鍵入 :set fileformat=unix 或是 :set ff=unix
4. 鍵入 :wq進行儲存
這樣子在下一次./configure就不會有這樣的錯誤訊息了~
會發生的原因:
原因是configure這個script是在windows系統下用記事本文件編寫的。不同系統的編碼格式所引起的。為了確認是這個原因造成,請作下面幾個動作:
1. vi 或是 vim這個configure檔案
2. 進到vi的 command mode(按下ESC)
3. 鍵入 :set fileformat 或是 :set ff
結果會看到 fileformat=dos
解決方法:
1. vi 或是 vim這個configure檔案
2. 進到vi的 command mode(按下ESC)
3. 鍵入 :set fileformat=unix 或是 :set ff=unix
4. 鍵入 :wq進行儲存
這樣子在下一次./configure就不會有這樣的錯誤訊息了~
2015年3月5日 星期四
Makefile的賦值運算符(=, :=, +=, ?=)
在Makefile的文件當中常常會看到這四種賦值運算符,下面我們寫個範例來介紹一下它們各別代表的意思。
首先編寫一個Makefile檔
ifdef DEFINE_INIT_VALUE
VALUE = "Hello!"
endif
ifeq ($(CMD),colon)
VALUE := “Hello all the World!”
endif
ifeq ($(CMD),question_mark)
VALUE ?= “Hello World!”
endif
ifeq ($(CMD),plus)
VALUE += “Danny!”
endif
all:
@echo $(VALUE )
看看這幾種賦值運算的結果:
make DEFINE_INIT_VALUE=true CMD=colon 輸出: ==> Hello all the World!
make DEFINE_INIT_VALUE=true CMD=question_mark 輸出: ==> Hello!
make DEFINE_INIT_VALUE=true CMD=plus 輸出: ==> Hello! Danny!
make DEFINE_INIT_VALUE= CMD=colon 輸出: ==> Hello all the World!
make DEFINE_INIT_VALUE= CMD=question_mark 輸出: ==> Hello World!
make DEFINE_INIT_VALUE= CMD=plus 輸出: ==> Danny!
總結:
= 是最基本的賦值
:= 會覆蓋變數之前的值
?= 變數為空時才給值,不然則維持之前的值
+= 將值附加到變數的後面
另外 =, :=這兩賦值運算符號在網路上查詢,常常會看到這種說法:
= 在執行時擴展(values within it are recursively expanded when the variable is used, not when it's declared)
:= 在定義時擴展(values within it are expanded at declaration time)
而白話一點的說法如下範例所示
1. =
make會將整個makefile展開後,才決定變數的值。也就是说,變數的值會是整個Makefile中最後被指定的值。看例子:
x = hello
y = $(x) world!
x = hi
all:
@echo $(y)
在上例中,輸出結果將會是 hi world! ,而不是 hello world!
2. :=
變數的值在Makefile展開途中就會被給定,而不是整個Makefile展開後的最终值。
x := hello
y := $(x) world!
x := hi
all:
@echo $(y)
輸出結果 ====> hello world!
首先編寫一個Makefile檔
ifdef DEFINE_INIT_VALUE
VALUE = "Hello!"
endif
ifeq ($(CMD),colon)
VALUE := “Hello all the World!”
endif
ifeq ($(CMD),question_mark)
VALUE ?= “Hello World!”
endif
ifeq ($(CMD),plus)
VALUE += “Danny!”
endif
all:
@echo $(VALUE )
看看這幾種賦值運算的結果:
make DEFINE_INIT_VALUE=true CMD=colon 輸出: ==> Hello all the World!
make DEFINE_INIT_VALUE=true CMD=question_mark 輸出: ==> Hello!
make DEFINE_INIT_VALUE=true CMD=plus 輸出: ==> Hello! Danny!
make DEFINE_INIT_VALUE= CMD=colon 輸出: ==> Hello all the World!
make DEFINE_INIT_VALUE= CMD=question_mark 輸出: ==> Hello World!
make DEFINE_INIT_VALUE= CMD=plus 輸出: ==> Danny!
總結:
= 是最基本的賦值
:= 會覆蓋變數之前的值
?= 變數為空時才給值,不然則維持之前的值
+= 將值附加到變數的後面
另外 =, :=這兩賦值運算符號在網路上查詢,常常會看到這種說法:
= 在執行時擴展(values within it are recursively expanded when the variable is used, not when it's declared)
:= 在定義時擴展(values within it are expanded at declaration time)
而白話一點的說法如下範例所示
1. =
make會將整個makefile展開後,才決定變數的值。也就是说,變數的值會是整個Makefile中最後被指定的值。看例子:
x = hello
y = $(x) world!
x = hi
all:
@echo $(y)
在上例中,輸出結果將會是 hi world! ,而不是 hello world!
2. :=
變數的值在Makefile展開途中就會被給定,而不是整個Makefile展開後的最终值。
x := hello
y := $(x) world!
x := hi
all:
@echo $(y)
輸出結果 ====> hello world!
智力測驗-秤重問題
題目一:
有8顆球,其中7個重量一樣,只有1個較輕,用一個天平,試以最少測量次數來找出有問題的那一個球。
答案:
最少為兩次可找出。
解法:
將球編號並分堆如下:
A = [1, 2, 3]
B = [4, 5, 6]
C = [7, 8]
B = [4, 5, 6]
C = [7, 8, 9]
D = [10]
有8顆球,其中7個重量一樣,只有1個較輕,用一個天平,試以最少測量次數來找出有問題的那一個球。
答案:
最少為兩次可找出。
解法:
將球編號並分堆如下:
A = [1, 2, 3]
B = [4, 5, 6]
C = [7, 8]
- 如果A=B,則C有問題。
- 如果7>8,則8有問題。
- 如果7<8,則7有問題。
- 如果A>B,則B有問題。
- 如果4=5,則6有問題。
- 如果4>5,則5有問題。
- 如果4<5,則4有問題。
- 如果A<B,則A有問題。
- 如果1=2,則3有問題。
- 如果1>2,則2有問題。
- 如果1<2,則1有問題。
還有另一個較複雜的衍申題
題目二:
籃子裡面有10顆球,其中一顆有問題,請你利用天平,秤重三次之後把那顆球找出來,並說明他是比較重或輕?
答案&解法:
將球編號並分堆如下:
A = [1, 2, 3]B = [4, 5, 6]
C = [7, 8, 9]
D = [10]
- 如果A=B,則C或10有問題。
- 如果A=C,則10有問題。
- 如果1>10,則10較輕。
- 如果1<10,則10較重。
- 如果A>C,則C有問題且較輕。
- 如果7=8,則9較輕。
- 如果7>8,則8較輕。
- 如果7<8,則7較輕。
- 如果A<C,則C有問題且較重。
- 如果7=8,則9較重。
- 如果7>8,則7較重。
- 如果7<8,則8較重。
- 如果A>B,則A或B有問題,且A較重或B較輕。
- 如果A=C,則B有問題且較輕。
- 如果4=5,則6較輕。
- 如果4>5,則5較輕。
- 如果4<5,則4較輕。
- 如果A>C,則A有問題且較重。
- 如果1=2,則3較重。
- 如果1>2,則1較重。
- 如果1<2,則2較重。
- 如果A<B,則A或B有問題,且A較輕或B較重。
- 如果A=C,則B有問題且較重。
- 如果4=5,則6較重。
- 如果4>5,則4較重。
- 如果4<5,則5較重。
- 如果A<C,則A有問題且較輕。
- 如果1=2,則3較輕。
- 如果1>2,則2較輕。
- 如果1<2,則1較輕。
2015年3月2日 星期一
union的使用時機&struct的對齊(natural alignment)
繼前篇所討論的struct與union的差異, 這邊有個使用union的範例可以參考~
假設現在有三個種類的封包要被傳送, 而同一時間只會送出這三類的其中一類封包, 同時我們用struct與union兩種方式宣告, 在執行結果進行比較所需佔用的記憶體空間。
因此程式可以這樣寫:
//*****************example_union_usage.c****************//
#include <stdio.h>
struct structA
{
int a;
char b;
};
struct structB
{
char a;
short b;
};
struct structC
{
int a;
char b;
float c;
};
假設現在有三個種類的封包要被傳送, 而同一時間只會送出這三類的其中一類封包, 同時我們用struct與union兩種方式宣告, 在執行結果進行比較所需佔用的記憶體空間。
因此程式可以這樣寫:
//*****************example_union_usage.c****************//
#include <stdio.h>
struct structA
{
int a;
char b;
};
struct structB
{
char a;
short b;
};
struct structC
{
int a;
char b;
float c;
};
struct CommuPacket //使用union的
{
int iPacketType;
union
{
struct structA packetA;
struct structB packetB;
struct structC packetC;
};
};
struct CommuPacket_2
{
int iPacketType;
struct
{
struct structA packetA;
struct structB packetB;
struct structC packetC;
};
};
int main()
{
struct CommuPacket struct_CommuPacket;
struct CommuPacket_2 struct_CommuPacket_2;
int struct_size, struct_size_2;
struct_size = sizeof(struct_CommuPacket);
struct_size_2 = sizeof(struct_CommuPacket_2);
printf("struct_CommuPacket size=%d\n", struct_size);
printf("struct_CommuPacket_2 size=%d\n\n", struct_size_2);
int m_size_packetA, m_size_packetB, m_size_packetC;
m_size_packetA = sizeof(struct_CommuPacket.packetA);
printf("packetA size is %d\n", m_size_packetA);
m_size_packetB = sizeof(struct_CommuPacket.packetB);
printf("packetB size is %d\n", m_size_packetB);
m_size_packetC = sizeof(struct_CommuPacket.packetC);
printf("packetC size is %d\n", m_size_packetC);
}
//***************************************************//
輸出結果:
可以看到使用union的,佔了16byte,與struct的28byte比較,小了許多。
========================================================================
另外你可能會有疑問,為什麼packetA,packetB,packetC的size分別是8byte,4byte,12byte?
照理說不是應該是5byte,3byte,9byte嗎?
這就要講到struct的"自然對齊"(natural alignment)特性與"指定對齊":
一. natural alignment,是指按結構體的成員中size最大的成員對齊。
舉個例子:
struct natural_alignment
{
char a;
short b;
char c;
};
在上述struct中,size最大的是short,其長度為2byte,因而結構中的char成員a、c都以2為單位對齊,sizeof(natural_alignment)的結果等於6;
如果改為:
struct natural_alignment
{
char a;
int b;
char c;
};
其結果顯然為12。
二. 還有另外一種用法名為"指定對齊"
通常可以通過下面的方法來改變指定對齊的條件:
• 使用虛擬指令#pragma pack (n),編譯器將按照n個位元組對齊;
• 使用虛擬指令#pragma pack (),取消自定義位元組對齊方式
*如果#pragma pack (n)中指定的n大於結構體中最大成員的size,則沒有作用,結構體仍然按照size最大的成員進行對齊。
例如:
#pragma pack (n)
struct natural_alignment
{
char a;
int b;
char c;
};
#pragma pack ()
當n為4、8、16時,其對齊方式都一樣,sizeof(naturalalign)的結果都等於12。而當n為2時,sizeof(naturalalign)的結果為8。
在VC++ 6.0編譯器中,我們可以指定其對齊條件,其操作方式為依次選擇projetct > setting > C/C++功能表,在struct member alignment中指定你要的對齊條件。
另外,通過__attribute((aligned (n)))也可以讓所作用的結構體成員對齊在n位元組邊界上,但是它較少被使用。
訂閱:
文章 (Atom)