顯示具有 C Programming 標籤的文章。 顯示所有文章
顯示具有 C Programming 標籤的文章。 顯示所有文章

2025年8月3日 星期日

C 語言裡面 Static 宣告 function或變數,的用途與意義

 static 在 C/C++ 有幾個不同的用途,根據用在不同地方意義不同:

(A) 用在函式或變數前面(檔案層級)
作用範圍只限於本檔案,其他檔案無法看到或使用這個函式/變數。
常用來避免命名衝突,或是只在本檔案內部使用的工具函式。

(B) 用在區域變數前面(函式內部)
變數只會初始化一次,之後的呼叫會保留上次的值(類似全域變數,但作用範圍只在這個函式內)。

2020年3月10日 星期二

C語言的運算符號"^"表示為XOR運算

如標題,C語言程式碼中看到    "^"   符號,是去做XOR運算的意思

XOR運算的真值表如下:










下面是一個簡單的範例:

int A=0, B=0;
int C;
C=A^B;  ==>   C結果是0

==============================

int A=0, B=1;
int C;
C=A^B;  ==>   C結果是1

==============================

int A=1, B=0;
int C;
C=A^B;  ==>   C結果是1

==============================

int A=1, B=1;
int C;
C=A^B;  ==>   C結果是0

2020年1月6日 星期一

printf印出16進制(HEX)的值

假設有個變數i,印出16進制的值的方法,如下:
printf("0x%08x\n", i);

簡單的範例如下:
==========================================
#include <stdio.h>

int main()
{
    int i=20;
    printf("i=0x%08x\n", i);
    return 0;
}
===========================================

Result:





2019年9月20日 星期五

C 更改檔名 example

紀錄C當中,一個簡單的改檔名function,rename();

#include <stdio.h>

int main () {
   int ret;
   char oldname[] = "file.txt";
   char newname[] = "newfile.txt";
 
   ret = rename(oldname, newname);

   if(ret == 0) {
      printf("renamed successfully");
   } else {
      printf("unable to rename");
   }
 
   return 0;
}


2018年10月8日 星期一

C語言使用malloc配置一個struct記憶體空間

假設有一個struct如下所示:

struct foo {
     char *a;
     int b;
     char c[20];
};

當我們要用malloc配置記憶體時,通常會用下面的方式來作宣告:


struct foo *ptr = (struct foo *) malloc(sizeof(struct foo));


另一種較簡潔的寫法,如下所示:

struct foo *ptr = malloc(sizeof(*ptr));

2018年7月20日 星期五

除錯工具strace的用法


strace   [parameter]   [除錯的執行檔]

執行 strace 相關參數
-o (將 strace 程式的過程寫至指定的 file 中)
-s (最多一次 print xxx bytes 的訊息)
-p (strace 某 pid 程序的執行過程)
-f (strace 某子程序的執行過程)

例如:   我執行smbd遇到無法跑在背景,卻又沒有任何錯誤訊息
就可以使用strace來觀察錯誤的原因
===> ./strace -o aa.log -s 1024 /sbin/smbd -D

2015年10月13日 星期二

使用printf印出有顏色的Console訊息

有時候Console訊息很多,如果能把自己想看的訊息加點顏色,可以加快Debug速度

下面記錄了一個基於printf可以印出彩色訊息的function。

====================================================================
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define NONECOLOR "\033[m"
#define RED "\033[0;32;31m"
#define LIGHT_RED "\033[1;31m"
#define GREEN "\033[0;32;32m"
#define LIGHT_GREEN "\033[1;32m"
#define BLUE "\033[0;32;34m"
#define LIGHT_BLUE "\033[1;34m"
#define DARY_GRAY "\033[1;30m"
#define CYAN "\033[0;36m"
#define LIGHT_CYAN "\033[1;36m"
#define PURPLE "\033[0;35m"
#define LIGHT_PURPLE "\033[1;35m"
#define BROWN "\033[0;33m"
#define YELLOW "\033[1;33m"
#define LIGHT_GRAY "\033[0;37m"
#define WHITE "\033[1;37m"


//**********************************************************
// Usage Example:
// int num=7;
// COLOR_DBG("%s Lucky Number is %d", GREEN, num);     
//
// Description:
// This message will show Green color.
//
// viod COLOR_DBG(const char* Format, const char* Color, const char* str, ...)
void COLOR_DBG(const char* Format, ...)
{
char str[255];
va_list ap;
va_start(ap,Format);
vsprintf(str,Format,ap);
sprintf(str,"%s%s",str,NONECOLOR);
printf("%s",str);
va_end(ap);
}


int main()
{
COLOR_DBG("%s Hello World!!!!!\n", YELLOW);
return 0;
}
=====================================================================

以下是使用gcc compiler後執行結果的截圖!


2015年10月7日 星期三

將C++的function包裝成Library(so檔)給C的程式使用

今天要練習的是,把C++的Function包給C程式使用時該怎麼作

首先我們先撰寫一支簡單的C++程式,如下所示
////////////////////  cplus_lib.cpp  ////////////////////////
#include <iostream>
using std::cout;

void cout_func()
{
cout << "Hello World!\n";
}
//////////////////////////////////////////////////////////////////

接著我們將她打包成動態連結檔libcplus_lib.so,步驟如下:
1. gcc -Wall -O2 -c -o cplus_lib.o cplus_lib.cpp
2. gcc -shared -o libcplus_lib.so cplus_lib.o

經過以上兩個步驟,我們可以得到動態連結檔的函式庫 libcplus_lib.so






接著我們試著再寫一支C程式,此程式去使用函式庫的cout_func()來印出"Hello World"
///////////////////////  main_cout.c  //////////////////////////////
#include <stdio.h>

int main()
{
        cout_func();
        return 0;
}
/////////////////////////////////////////////////////////////////////////////

接著編譯過程如下,注意要額外指定C++ 標準函式庫 libstdc++:
1.gcc -o main_cout_dynamic main_cout.c -L. -lcplus_lib -lstdc++

結果會出下以下的錯誤訊息:












解決方法,要在產生library的程式當中加入一些東西,如下所示:
////////////////////  cplus_lib.cpp  ////////////////////////
#include <iostream>
using std::cout;

#ifdef __cplusplus
extern "C" {
#endif
void cout_func()
{
cout << "Hello World!\n";
}
#ifdef __cplusplus
}
#endif
//////////////////////////////////////////////////////////////////
註:上面程式碼的意思是: 如果這是一段cpp的程式碼,就加入extern "C"{} 來處理其中的程式碼


這樣子編譯器才看得懂,所以再整個重新編譯一次,就成功了!
















註: 上圖第三行,作者把產生出來的library複製到library環境變數的預設路徑下,如果不這樣作,當執行main_cout_dymanic時,會出現找不到library的錯誤訊息,關於這部分作者正在尋找可以自己指定吃特定路徑的library的方法.....

如果有朋友知道方法,也請給予指教,謝謝!

2015年7月1日 星期三

可變參數的function實作

由於最近寫的程式常常需要用到strcat ,而且一次要把好多個值strcat在一起

因此對可變參數的函式作了小小的研究,並寫成function以便日後使用


================== 正題開始 =================

在C/C++中,有所謂的variant argument(變動引數)。也就是可以讓函數
使用數量不固定的引數。printf()就是這類的函數。

定義一個函數的prototype(原型)時,若是將參數列以"..."代入,就表示這個函數即將
使用variant argument。如:

danny_func(char *result,  ...)

這樣子便可讓編繹器不檢驗傳入這種函數裡的引數型態和數量,編出來的程式碼在呼叫端
就能夠盡可能地把各式各樣引數傳入。


那麼,在如此的程式裡,要怎麼存取variant argument呢?因為缺少引數變數,所以我們
不可能像一般程式一樣直接存取它們,而是要改用stdarg.h裡面所提供的三個巨集與一個
型別,分別是:

va_list: 宣告一個指標,讓它指向引數串列。
va_start: 初始化這個指標,讓它真正指向正確的引數串列開頭。
va_arg: 來取得va_list中的資料。
va_end: 清除這個指標,把它設為NULL。範例如下:

因此打算實作一個 danny_strcat(char *result,  ...) 這樣子的function。

首先還是必須先使用到上面介紹的 linux當中的這些函式。




實作範例如下:
================= danny_strcat.c ====================
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

void danny_strcat(char *result,  ...)
{
     va_list ap;
     char *p;
     va_start(ap, result);
     p = va_arg(ap, char *);

     while (p != NULL) {
      strcat(result, p);
      p = va_arg(ap, char*);
     }
     va_end(ap);
}

int main(void)

{
     char tmp[512]={0};
     char tmp2[32]="strcat";
     danny_strcat(tmp, "hello-", "world-", "danny-", tmp2, NULL);
     printf("In main tmp=%s\n",tmp);
     return 0;
}


輸出結果:






*目前的作法- 當呼叫這個可變參數的函式時,使用者必須在參數的最後面加上NULL,來告知它結束的點。

2015年6月13日 星期六

String轉unsigned long/String轉int/String轉float

有時候會需要用到一些型態轉換的C function因此這邊寫個小範例作個記錄~

=============== test_transit.c =================

#include <stdio.h>
     
int main()
{
        unsigned long ul;
        char str1[]="A5";
        //strtoul第一個參數帶入要轉換的自串
        //strtoul第三個參數帶入"要取幾進位的數"
        ul = strtoul(str1, NULL, 16); // string to unsigned long
        printf("==== String to unsigned long value(Hex)=%lX ====\n", ul);
        printf("==== String to unsigned long value(D)=%ld ====\n", ul);


        char str2[]="123";
        int a = atoi(str2);
        printf ("==== String to integer value=%d ====\n",a+10);


        char str3[]="100.23";
        float b = atof(str3);
        printf ("==== String to float value=%f ====\n",b);

        return 0;
}

===================================================

其中String轉unsigned long是成功的(OK)
String轉int也是成功的(OK)
但String轉float的部分有點問題,印出來的值跟轉換的字串不相同,這部分還需研究(Fail!) @@"
如果有研究出結果會在上來把float的部分更新


輸出結果:


2015年5月25日 星期一

判斷特定字串, 並改寫檔案的範例~

原本想要作到開啟一個設定檔,接著根據特定字串來直接改寫檔案

查詢了幾篇文章發現似乎無法這樣作

其中有個最主要的因素是因為:

文字檔是循序存放的,如果確定新資料與舊資料文字長度是一樣的,才可以直接作開檔、讀檔、再改寫。

舉個例子:
現在有一個設定檔login.info內容如下

username=danny
password=5566
location=taiwan

現在你想要讀出當字串為password時去改寫密碼的動作,假設你要新寫入的密碼為"123456",然後你使用直接開檔、讀檔、再改寫的方法,想必會變成下面的情形

username=danny
password=123456cation=taiwan

因為你新輸入的文字長度比舊的來的大,所以他會覆蓋到原本舊的其他的資料

=======================================================================
因此看來要改寫檔案就得用另一種方法了,這邊介紹的是,讀取原本檔案,然後寫入到另一個檔案去,沒有改寫的地方就照原本資料寫入,改寫的也一併寫入到新的資料。

最後再把原本資料砍掉,然後把新的資料改名成舊的資料名子。

詳細的範例如下所示:

首先是原始設定檔===========
devicename:dannyDevice
protocol:Bloger
resolution:HD
framerate:30



主要的程式碼部分============
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <ctype.h>

#define DEVICE_INFO_PATH "./device.info"
#define TMP_DEVICE_INFO_PATH "./tmp_device.info"

void modify_file_func()
{
        char buffer[128];

/* open login info in device. */
FILE *fptr, *fptr_tmp;
char tmp[64]={0};

        if ((fptr = fopen(DEVICE_INFO_PATH, "r")) == NULL)
        {
                printf("open_file_error");
                exit(1);
        }
else if((fptr_tmp = fopen(TMP_DEVICE_INFO_PATH, "w")) == NULL)
{
                printf("open_tmp_file_error");
                exit(1);
}

while(fgets(buffer, sizeof(buffer), fptr) != NULL)
        {
if(strstr(buffer, "protocol:"))
{
sprintf(tmp,"protocol:%s\n", 123);
fputs(tmp, fptr_tmp);
memset(tmp,'\0',sizeof(tmp));
}
else if(strstr(buffer, "resolution:"))
{
sprintf(tmp,"resolution:%s\n", 456);
fputs(tmp, fptr_tmp);
memset(tmp,'\0',sizeof(tmp));
}
else if(strstr(buffer, "framerate:"))
{
sprintf(tmp,"framerate:%s\n", 789);
fputs(tmp, fptr_tmp);
memset(tmp,'\0',sizeof(tmp));
}
else
fputs(buffer, fptr_tmp);
        }
        fclose(fptr);
fclose(fptr_tmp);
        
        //刪掉原檔案,並改寫新檔案的名子
sprintf(tmp,"rm %s;mv %s %s", DEVICE_INFO_PATH, TMP_DEVICE_INFO_PATH, DEVICE_INFO_PATH);
system(tmp);
}


int main()
{
modify_file_func();
return 0;
}


最後的device.info檔案內容=======
devicename:dannyDevice
protocol:123
resolution:456
framerate:789



2015年5月6日 星期三

當apt_get_update時遇到問題時的解決方法 ERROR MSG:(Problem with MergeList)(status file could not be parsed or opened)

之前要作 apt-get update,當更新到最後時出現了下面的Error message:

E: Encountered a section with no Package: header
E: Problem with MergeList /var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_natty_main_binary-i386_Packages
E: The package lists or status file could not be parsed or opened.

======================================================================

當遇到上面的錯誤情形時,只需要作以下幾個步驟就可以解決了:

首先叫出Terminal接著輸入以下幾個指令

1. sudo rm /var/lib/apt/lists/* -vf

2. sudo apt-get update


註:執行上面兩個指令之前,可以先將 /var/lib/apt/list/ 底下的東西全部備份,以免作了上面兩個步驟之後還是無法解決的話,可以復原 :D

如何把make時的錯誤訊息輸出到檔案

寫程式編譯的時候,有時候錯誤訊息很多

想要把所有的錯誤訊息都輸出到一個檔案,可以用下面的指令來實現~

make 2> error_msg

數字 含義 標准叫法
0 標准輸入  stdin(standard input )
1 標准輸出  stdout(standard output )
2 標准錯誤輸出  stderr(standard error)

而只下make,系統預設的是會把這三種訊息stdin、stdout、stderr都印到螢幕上。

想要將對應的信息輸出到某個檔案中,就用對應的數字加上重定向符號'>'來輸出到文件中。

下面是幾個範例:

1.想要把make輸出的全部信息,輸出到某個文件中,最常見的辦法就是:
make > error_msg 

此時預設情況是沒有改變2=stderr的輸出方式,還是輸出到螢幕,所以,如果有錯誤訊息,還是可以在螢幕上看到。


2.想要把make輸出中的錯誤與警告訊息輸出到檔案,可以使用:
make 2> error_msg

由於1=stdout沒有變,還是輸出到螢幕,所以,那些命令執行時候輸出的正常訊息,你還是可以在螢幕上看到。


3.只需要把make輸出中的正常的信息輸出到文件中(例如要看Makefile作了哪些編譯動作),可以用:
make 1> build_msg

由於2=stder沒有變,還是輸出到螢幕,所以,那些命令執行時候輸出的錯誤信息,你還是可以在螢幕上看到的。


4.想要把正常輸出信息和錯誤信息輸出到分別的文件中,可以用:
make 1> build_msg 2>error.msg 


5. 所有的信息都輸出到同一個文件中:
make > build_all_msg 2>&1 

其中的2>&1表示錯誤信息輸出到&1中,而&1,指的是前面的那個文件:build_all_msg 。

注意:上面所有的1,2等數字,後面緊跟着大於符號'>' ,中間不能有空格


2015年4月27日 星期一

fopen的開檔,讀檔與寫檔

開檔,讀檔,寫檔在資訊科學這門課程是相當基礎的資料處理,

但是假使平常比較不常做這些處理,當要你作的時候,真的是很要人命

因此作一些筆記是必要的,到時候要處理檔案的時候,就可以很快上手了

這邊分三部分來作筆記,分別為讀檔,寫檔,讀檔並寫檔

===========================================================
首先是讀檔的部分:

讀檔和寫檔是較為簡單的部分,讀檔的話基本上第一步先對檔案作fopen

接著只要使用fgets,這支C函式可以幫你把檔案一行一行的讀出來,fgets也會在每一行讀到結
尾"\n"時幫你把檔案指標指到下一行的起始

簡單的範例如下:
///////////// Read_File.c////////////////////
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define PATH "/etc/login.info"

int main()
{
    FILE *fptr;
    char login_info[128];

    if ((fptr = fopen(PATH, "r")) == NULL)
    {
                printf("open_file_error");
                exit(1);
     }
   
     while(fgets(login_info, 128, fptr) != NULL)
     {
                //fgets會把每一行資訊copy到login_info字元陣列
                //此處油開發者自行決定要對讀出的內容作甚麼處理
                //寫檔則再搭配使用 fwrite(buffer,1,sizeof(buffer),fptr);
      }
      fclose(fptr);
      return 1;
}
///////////////////////////////////////////////////////////////////////

fopen的第二個參數,用法請參考下圖:




接著是讀檔並且同時寫檔,作者這邊就只作一些重點整理了

首先也是對檔案作fopen,後面參數就請填上"r+"

接著以下這幾個function的使用就是讀檔並同時寫檔的重點

**********************************************
int ftell(FILE *fptr);
==>這支function可以取到目前fptr所offset的位置

舉個例子有個login.info的檔案內容如下:
username:admin
password:1234

那我用fptr=fopen("login.info", "r+")之後再搭配fget一次之後去作ftell(fptr)會等於15
也就是此時指標的位置會是在第二行一開頭
******************************************************

******************************************************
第二個重要的是
rewind(FILE *fptr);
==>這支function可以把fptr指標移到最前頭
*******************************************************

**************************************************
最後一個是
fseek(FILE *fptr, int offset, SEEK_SET);
==>此fuction可以幫你把fptr的指標位置移到你想要開始的地方

接著你可以作fprintf(fptr, " "); 你就可以修改你想改寫的部分了
******************************************************


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;
}
//====================== End =======================//


輸出結果如下:









2015年3月18日 星期三

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;
}

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;
}

/**********************************************************/

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 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位元組邊界上,但是它較少被使用。







2015年2月27日 星期五

struct與union練習&宣告型態個別是幾byte

今天在學習struct與union的差異, 首先第一部分先說明這兩者的差異點, 第二部分再研究一下各個型態宣告所佔的記憶體大小。


一. struct與union的差異
- 當你宣告了一個struct他所佔的記憶體空間會是所有的membrt大小加起來, 舉個例:

  struct example_struct{
      double a;
      int b;
  };

  從上面的struct我們可以簡單的計算整個struct所佔用的空間是12byte. 接下來我們來看union.


  union所佔用的記憶體空間只會是member當中最大的一個member的size. 舉個例子來說明:

  union example_union{
     double a;
     int b;
  };

這個union所佔用的記憶體空間為8byte. 相對於struc來說是較省空間, 但是使用上必須特別小心,為什麼這麼說呢? 下面再舉個例子:

/************************ example_1,c ********************/

#include <stdio.h>

struct example_struct{
  int a;
  char b;
};

union example_union{
  int a;
  char b;
};

int main()
{
   struct example_struct m_struct;
   union example_union m_union;

   //struct
   m_struct.a = 3;
   printf("m_struct.a=%d\n", m_struct.a);
   m_struct.b = 'c';
   printf("m_struct.a=%d, m_struct.b=%d\n\n", m_struct.a, m_struct.b);

   //union
   m_union.a = 3;
   printf("m_union.a=%d\n", m_union.a);
   m_union.b = 'c';
   printf("m_union.a=%d, m_union.b=%d\n", m_union.a, m_union.b);
}

/*********************************************************/

輸出結果如下:
m_struct.a=3
m_struct.a=3, m_struct.b=99

m_union.a=3
m_union.a=99, m_union.b=99    //<===== the value of m_union.a is modified by m_union.b






二. 各個型態宣告所佔的記憶體大小

在研究上半部分的同時另外發現了long這個型態在32bit與64bit電腦上宣告所佔的記憶體大小是不相同的(不同的地方我用Bold顯示)。

32bit:
    long size is 4
    char size is 1
    unsigned long size is 4
    int size is 4
    float size is 4
    double size is 8


64bit:
    long size is 8
    char size is 1
    unsigned long size is 8
    int size is 4
    float size is 4
    double size is 8








2011年11月11日 星期五

註冊完成第一波

有鑑於好幾位同事都已經開始寫自己的網誌, 我也趕著這波流行, 來這邊練習一下文筆

希望可以在這邊撰寫一些出遊的文章, 或者是工作上的一些點點滴滴