[Python] Python/C API On Linux Mint

今天第一次嘗試使用 Python/C API

但是花了點時間處理 GCC 編譯器引入 Python.h 的問題
———————————————————————————–

Python/C 是 Python 作為黏合語言的一個重點,要用 C 呼叫 Python 的模組,或是在 C 中使用 Python 語法,基本上需要透過這個 API ,其中有一個主要的 Header 叫做 Python.h,需要在其他標頭檔之前引入,接著就可以呼叫這個標頭檔中定義一些與 Python 有關的 C 函式了

我實驗的平台是 Linux Mint x86_64 3.8.0-19-generic 、Python 2.7.4、GCC 4.7.3

這台電腦中的  Python 沒有包含 Python.h 及其他相關的 Python Header,上網查可以很輕易找到要安裝 python-dev:sudo apt-get install python-dev,如此在 /usr/include/python2.7 裡面就有Python.h 了

然後我寫了以下的 C 範例 (python_in_c.c):

接著就很期待的要進行編譯:gcc -o a.out python_in_c.c
結果編譯錯誤!
訊息說找不到 Python.h,基本上網路上很多說安裝好 python-dev 以後就可以讓 C 編譯器在編譯時找到 Python.h 了

但我的結果是無法自動找到,進一步搜尋到相同狀況的文章說需要將路徑加入到 Compiler Search Path,所以我嘗試編譯時這樣:
gcc -I/usr/include/python2.7 -o a.out python_in_c.c
接著就有找到這個 header 了!

但是!還是有錯誤啊!訊息如下:
python_in_c.c:(.text …): undefined reference to ‘Py_Initialize’
python_in_c.c:(.text …): undefined reference to ‘PyRun_SimpleStringFlags’
python_in_c.c:(.text …): undefined reference to ‘Py_Finalize’

這樣不就還是沒有成功引入嗎?
因為我有去 trace Python.h ,有引入 pythonrun.h
PyRun_SimpleStringFlag 就在那裡面,怎麼會還是沒定義?

後來測試很久終於找到成功的敘述了:
gcc -I/usr/include/python2.7 -o a.out python_in_c.c -lpython2.7

這樣子除了找的到 header ,也可以通過編譯找到定義函式了

接著就可以執行產生的可執行擋了!

如果覺得每次都要打 -I/usr/include/python2.7 很瑣碎,我找到的作法是將這個路徑加到 C_INCLUDE_PATH 這個環境變數中,開啟 .bashrc 加入如下:
export C_INCLUDE_PATH="/usr/include/python2.7:$C_INCLUDE_PATH"

Shell 內建命令 (Shell Built-in Command)

Shell Built-in Command 是指與 shell code 寫在一起編譯成,屬於 shell 這個 program 本身功能的指令,在Windows中稱為 internal command,例如:cd, exit, umask, alias 等等,這種內建在 shell 程式碼內的功能無法在 shell 中使用 execv() 去呼叫外部程式的方式執行


int main()
{
    …
     if( strcmp(cmd, “cd") == 0)
            cd(pathname);
    …
}

void cd(char *pathname)
{ … chdir(… pathname …) … }


這種命令的執行速度會比 external program 快,因為省去了 program loading 這段 overhead,但是因為這些功能的程式碼是寫在 shell 中,所以如果修改或更新了任何功能的程式碼,就必須將整個 shell program 重新 compile 一次

那麼是什麼樣的命令適合實作成 builtin 呢?

舉個例子來說明,像是 cd 這個命令,用途是在不同的目錄間移動,實作方式大致上是使用 chdir() 這個函式,不過我們討論的不是實作的方法,而是這個函式的運作方式,對大部分的作業系統而言,當一個 Process 開啟一個檔案(目錄也是一個檔案)時,OS會為該 Process 建立他自己的 Process Table,其中就包含了現行工作目錄的資訊,而 chdir() 就是用來讓呼叫它的 Process 在執行期間移動自己的現行目錄

我們知道,cd 這個命令主要是用來改變目前的工作目錄,而這個工作目錄是屬於目前這個 Process 自己知道的資訊,其他 Process 不會知道也不需要知道

當一個 Process 呼叫一個 function ,這個 function 基本上只會對呼叫它的 Process 有作用

如果我們把 cd 實作成一個外部程式,也就是說,寫一個命名為 cd.c 的 program 並且編譯後程式名稱為 cd,當然,cd.c 中呼叫了 chdir() 這個函式,那麼當 shell process 使用 execv() 呼叫了這個外部程式後,這個外部程式便開始執行,注意!因為 cd 也是一個程式,根據對 Process 的定義,cd 被執行後,也會是一個 Process,也就是說,現在同時有兩個不同的 Process 在系統中執行,又據前一段所言,因為呼叫 chdir() 的 Process 是 cd,所以 chdir() 只會對 cd process 有作用,也就是會切換 cd process 的工作目錄,如此一來,shell 的工作目錄還是維持不變

因此如果我們希望使用者在 shell 輸入 cd 以後,可以改變 shell 的工作目錄,則必須在 shell process 中呼叫 chdir() ,這就是之所以要將 cd 實作成內建命令的原因

像是 umask(改變建檔時檔案權限遮罩)、exit (呼叫 exit() 終止 shell 這個 Process)、alias…都是差不多的原因,因為建檔時對檔案的權限設定使用的遮罩是個別 process 的東西,因此需要在各Process 內使用;因為要結束 shell 自己這個 process,是呼叫 exit(),如果採用呼叫外部程式,那只會結束那個外部的程式 (那個程式被執行起來後用來結束自己 ……. ~"~? )

總結一下,由這些例子可知,要實作為 built-in 的基本概念就是『這個命令只會對使用它的 process 本身有影響』,像是 ls,純粹只是對一個目錄進行開啟、讀取與列印內容,對使用這個命令的 Process 並沒有影響;像是 mv,也只是對一個檔案進行移動,對 Process 的參照不會有影響,所以這些命令沒有必要做成內建命令

Linear Algebra – Trace of 2 Matrices Multiply

在線性代數中,一個 n x n 矩陣 A 的 trace (跡) 等於該矩陣主對角線(最左上至最右下)上各個元素的總和,一般記作 tr(A) 或 Sp(A):

       tr(A) = a1,1 + a2,2 + …+ an,n = Σaii, for all i = 1 to n


假如有兩個矩陣 A ∈ F(mxn), B∈F(nxm),則 tr(AB) = tr(BA)
以 C 程式實做如下:

#include        
#include
#define MSIZE 5
#define NSIZE 6

int main()
{
int m1[MSIZE][NSIZE], m2[NSIZE][MSIZE]; //mxn * nxm = mxm
int result=0, tmp=0;
int i=0,j=0,k=0;

puts("");
/*initialize matrix 1*/
for(i=0; i<MSIZE; i++)
{
for(j=0; j<NSIZE; j++)
{
m1[i][j] = (i+1)*(j+1);
printf("%d ",m1[i][j]);
}
puts("");
}

puts("\n");
/*initialize matrix 2*/
for(i=0; i<NSIZE; i++)
{
for(j=0; j<MSIZE; j++)
{
m2[i][j] = (i+1)/(j+1);
printf("%d ",m2[i][j]);
}
puts("");
}
puts(""); 

/*(m1 第 m 列的第 n 個元素)*(m2 第 m 行第 n 個元素), 然後將 n 項乘積相加,成為新的矩陣的第 n 列 n 行項(對角項)*/  
for(i=0; i<MSIZE; i++)
{
     for(k=0, tmp=0; k<NSIZE; k++)        
tmp += m1[i][k]*m2[k][i];

result += tmp;
}

/*更精簡的做法:
for(i=0; i<MSIZE; i++)
for(k=0; k<NSIZE; k++)
result += m1[i][k]*m2[k][i];

因為m1和m2行列乘積要相加才是對角項,接著又將對角項相加,因此可直接用result不斷相加
*/


printf("tr(m1*m2)=%d\n",result);

return 0;

結果:


1 2 3 4 5 6
2 4 6 8 10 12
3 6 9 12 15 18
4 8 12 16 20 24
5 10 15 20 25 30


1 0 0 0 0
2 1 0 0 0
3 1 1 0 0
4 2 1 1 0
5 2 1 1 1
6 3 2 1 1

tr(m1*m2)=360

[C] 從指定之位置比較兩字串

/*
 * Program: Complement header file of string compare
 * Language:GNU C/ ANSI C
 * Author: Veck Hsiao @ Taiwan, National Chung Cheng University
 * Time: June/21/2012
 * Usage: Compare string 1 with string 2 start from assigned index
 * Result: If matched, return 0, otherwise, return -1
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int strmidcmp(const char *str1, const char *str2, int start)
{
int i=0, j=0;

for(i=start, j=0; j<strlen(str2); i++, j++)
if(str1[i]==str2[j])
continue;
else
return -1;

return 0;

}



Usage:
    printf("%d\n",strmidcmp("middle","ddlejd",2));    //-1
printf("%d\n",strmidcmp("middle","dd",2)); //0
printf("%d\n",strmidcmp("middle","add",2)); //-1

C – fflush() & fpurge()

在寫C程式的時候,我們經常會碰到 scanf() 的一些陷阱,很多時候我們必須用函式來清除標準輸入的緩衝區,例如以下的程式碼:

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

int main()
{
int size = 0;
int i=0,j=0;

do
{
printf("Please input rows of triangle, exit please input -1\n");
scanf("%d",&size);

if(size<0)
{ return 0; }
else if(size==1 || size ==0)
{ printf("Can not form a triangle!\n"); }
else if(size >100)
{
printf("Too large!!!\n");
}
else
{
for(i=0;i<size;i++)
{
for(j=0;j<size-(i+1);j++)
{ printf(" "); }

for(j=0;j<2*(i+1)-1;j++)
{ printf("*"); }

printf("\n");
}
}

fpurge(stdin); /**/
size = 0;
}while(size>=0);

return 0;
}



這是個簡單的印出三角型的程式碼,會有以下的結果:
C:\>Please input rows of triangle, exit please input '-1'
2
  *
***
Please input rows of triangle, exit please input '-1'
3
    *
  ***
*****
Please input rows of triangle, exit please input '-1'
-1
C:\>

注意到在while前面兩行,會有一個 fpurge() 嗎? 我想大多數的人google一下應該會發現中文的資料並不多,而且會發現他與我們熟知的 fflush() 功能很類似,沒錯!這個函式與 fflush() 具有一樣的功能,都是清除緩衝區內的資料,那究竟這兩個的差別在哪裡呢?

首先,大家可以在 Windows 上面編譯看看這支程式,會發現 fpurge()竟然是 undefine 的函式,是的,因為這個程式碼是我在學校的程式設計課上面所做的,而當時採用的平台是 FreeBSD,因此在這個 fpurge() 是在 FreeBSD 等Linux中定義的函式,在Windows 平台上似乎沒有這個函式
但是當大家到 Linux 中去測試這支程式之後,可以再把 fpurge() 改回 fflush(),會發現 fflush()在 Linux 中竟然沒有作用,但是他仍然有被定義喔!  (所已在等待誰可以告訴我這兩個東西的實質差異)