Python語言與C語言數據交互的場景還是比較常見的,例如在使用python優秀的數據處理可視化等優勢的同時,對於某些優秀的開源C/C++的軟件庫的調用就需要用到ctypes庫函數對動態庫進行API的靈活調用了,再例如在某些場景下,C語言的數據需要可視化,而C語言的可視化接口的支持是很薄弱的,這里可以采用Python強大的可視化效果來驗證數據的正確性(也可以采用MATLAB完成可視化)。 再比如在某些Demo測試場景下,使用Python便捷部署的神經網絡框架在使用獲取內存圖像數據時也可能使用到Python和C之間數據交換的需求。
一、python與C交互的重要庫 ctypes
ctypes庫作為python與C之間的交互的重要庫,其定義了各類數據類型與C語言中的數據類型進行對應,其中包括了char,int,POINTER等等,具體可以參看數據手冊。ctypes還能夠通過CDLL接口應用C語言的動態庫.so,在調用接口過程中,應該要嚴格配置Python端調用動態庫函數接口的參數類型(不能有任何偏差),應該掌握如何定義數組緩沖區並操作地址變量,同時能夠使用接口獲取變量地址,從而傳給C-API使用,基礎如下所示:
1. 數組類型定義及使用
測試代碼:
1 # 特殊ctypes類型數組類型定義 2 ArrayTestType = ctypes.c_uint8 * 10 # 重定義數據類型 3 ArrayTest = ArrayTestType(11) # 定義數據並初始化數據賦值為11 4 ArrayTest[5] = 20 # 對數據進行賦值處理 5 print('Line', sys._getframe().f_lineno, ':', ArrayTest, type(ArrayTest), ArrayTestType, type(ArrayTestType)) 6 print('Line', sys._getframe().f_lineno, ':', ArrayTest[0], ArrayTest[5])
運行結果:
Line 20 : <__main__.c_ubyte_Array_10 object at 0x7fd1318e8ea0> <class '__main__.c_ubyte_Array_10'> <class '__main__.c_ubyte_Array_10'> <class '_ctypes.PyCArrayType'> Line 21 : 11 20
這里可以看到 ArrayTestType 的類型其實為 PyCArrayType PythonC數組類型,因此 ArrayTestType 可以定義一個 unsigned char Array[10] 的數組。
注:數組類型的縮略定義方法(相當於上述步驟的兩步)
# 定義一個大小為10指針實例作為緩存, 等效為 Step1:DataType = c_uint8 * LENGTH --> Step2:DataPoint = DataType() DataPoint = (c_uint8 * LENGTH)()
2. 動態庫的加載及接口調用
測試代碼:
1 # 從C語言或者C++的動態庫當中加載C函數以調用 2 p = os.getcwd() + '/libfunc.so' # 獲取當前的動態庫的絕對路徑位置 3 f = cdll.LoadLibrary(p) # 使用LoadLibrary接口加載C語言動態庫 4 5 # Python Class類 到 C struct結構體 數據類型的傳輸轉換 6 Sfunction = f.py_struct_address # 從當前庫當中取得clib中的函數py_struct_address,並重命名為Sfunction 7 Sfunction.argtypes = [POINTER(POINT), POINTER(ctypes.c_char)] # 設置當前函數的輸入參數
這里使用了 os模塊獲取了動態庫的絕對路徑,並調用cdll.LoadLibrary(libpath),這里使用 argtypes 定義了動態庫函數的輸入參數類型,分別為結構體指針、char *指針。
3. 定義字符串類型(字符串string)
1 p = create_string_buffer(b"Hello World", 15) # create a 10 byte buffer 2 print('Line', sys._getframe().f_lineno, ':', p,sizeof(p), repr(p.raw))
創建一個string類型的緩沖空間,並返回一個字符串指針指向這串字符串,create_string_buffer 參數分別為字符串、buffer總長度,這個長度不能小於前一個參數字符串的長度大小,否則會報錯,p.raw取字符串的內容。
4. Numpy數組的地址獲取及應用
測試代碼:
1 # Numpy 數據類型等相互轉換測試(將內存數據轉換值Python當中) 2 ImgW = 1669 # 圖像寬度 3 ImgH = 21 # 圖像高度 4 ImgC = 3 # 圖像通道數 5 ImgL = ImgW*ImgH*ImgC # 圖像總長度 6 7 ImgArray = np.zeros((ImgW,ImgH,ImgC), dtype=np.ubyte) # 申請圖像總空間為多維 zeros 矩陣 8 print(ImgArray.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8))) # 將numpy數組轉換為地址表示方式 data_as 並打印數據類型 9 ImgArray_addr = ImgArray.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)) # 獲取的指針放置在ImgArray_addr變量當中 10 print('Line', sys._getframe().f_lineno, ':', ImgArray_addr[0],ImgArray_addr[1],ImgArray_addr[2],ImgArray_addr[3]) 11 12 Imgfunction = f.py_img_address # 從當前庫當中取得clib中的函數py_struct_address,並重命名為Sfunction 13 Imgfunction.argtypes = [POINTER(ctypes.c_ubyte), ctypes.c_uint32] # 設置當前函數的輸入參數規划 usigned char *pointer, unsigned int arg 14 Imgfunction.restype = ctypes.c_int # 設置當前函數返回值參數為 int 類型 15 time_start = time.time() 16 Res = Imgfunction(ImgArray_addr, ImgL) # 調用Imgfunction函數 17 time_end = time.time() 18 print('Line', sys._getframe().f_lineno, ':', 'TimeCost:', (time_end - time_start)*1000, 'ms') # 記錄當前函數調用消耗的時間 19 20 print('Line', sys._getframe().f_lineno, ':\n', ImgArray[:,:,0]) # 打印0通道的數據 21 print('Line', sys._getframe().f_lineno, ':\n', ImgArray[:,:,1]) # 打印1通道的數據 22 print('Line', sys._getframe().f_lineno, ':\n', ImgArray[:,:,2]) # 答應2通道的數據
通過numpy庫中數組的成員 ctypes.data_as 方法獲取指定類型的指針地址,並將此地址應用在函數庫的參數中。
5. 基本類型定義
測試代碼:
1 # 使用byref獲取ctypes類型數據的地址 2 data = ctypes.c_uint8(10) # 定義一個整數類型的變量,變量初始值為 10,相當於C語言中的 char data=42; 3 data_addr = ctypes.byref(data, 0) # 通過使用byref接口獲取地址,相當於C語言中的 char *data_addr = &data; byref(obj, offset) 對應於這段 C 代碼:(((char *)&obj) + offset) 4 print('Line', sys._getframe().f_lineno, ':', type(data)) 5 print('Line', sys._getframe().f_lineno, ':', type(data_addr))
這里使用 ctypes.c_uint8(10) 定義一個 unsigned char 類型的數據並賦予初值為10,byref函數接口用於獲取數據data的地址。
I) 完整的測試代碼:
MemoryTest.py

1 import os 2 import sys 3 import time 4 import ctypes 5 from ctypes import * 6 from ctypes import cdll 7 import numpy as np 8 9 BUFF_SIZE = 6*1024*1024 # 基本的緩沖區域尺寸大小定義 10 LENGTH = 2*2*3 # 數據長度定義 11 12 # 類定義 13 class POINT(Structure): # 定義了一個類,類當中的基本成員變量包括了x、y, 相當於C語言中的 struct POINT{int x;inty}; 14 _fields_ = [("x", c_int),("y", c_int),("addr", POINTER(c_uint8)),("len", c_int)] 15 16 # 特殊ctypes類型數組類型定義 17 ArrayTestType = ctypes.c_uint8 * 10 # 重定義數據類型 18 ArrayTest = ArrayTestType(11) # 定義數據並初始化數據賦值為11 19 ArrayTest[5] = 20 # 對數據進行賦值處理 20 print('Line', sys._getframe().f_lineno, ':', ArrayTest, type(ArrayTest), ArrayTestType, type(ArrayTestType)) 21 print('Line', sys._getframe().f_lineno, ':', ArrayTest[0], ArrayTest[5]) 22 23 DataPoint = (c_uint8 * LENGTH)() # 定義一個大小為10指針實例作為緩存, 等效為 Step1:DataType = c_uint8 * LENGTH --> Step2:DataPoint = DataType() 24 DataPoint[3] = 22 25 26 point = POINT(10, 20, DataPoint,LENGTH) # 定義個類對象Obj, 相當於 struct POINT point={10,20}; 27 print('Line', sys._getframe().f_lineno, ':', point.x, point.y, point.addr[3], point.len) 28 29 point = POINT(y=5) # 定義個類對象Obj, 相當於 struct POINT point={,20}; 30 print('Line', sys._getframe().f_lineno, ':', point.x, point.y) 31 32 # Class類數組定義 33 addrTest = (POINT * 1)() # 定義一個POINT結構體緩沖地址 34 addrTest[0].x = 10 # POINT緩沖地址的第一個變量的x成員值 35 addrTest[0].y = 11 # POINT緩沖地址的第一個變量的y成員值 36 addrTest[0].addr = DataPoint # POINT緩沖地址的第一個變量的addr成員賦值 37 addrTest[0].len = LENGTH # POINT緩沖地址的第一個變量的len成員賦值 38 print('Line', sys._getframe().f_lineno, ':', addrTest, addrTest[0].x,addrTest[0].y,addrTest[0].addr[3],addrTest[0].len) 39 40 TenPointsArrayType = POINT * 3 # 重定義了一個POINT數組類型,相當於C語言中的 #define TenPointsArrayType POINT*3 41 arr = TenPointsArrayType() 42 for pt in arr: 43 print(pt.x, pt.y, pt.addr) 44 45 # 從C語言或者C++的動態庫當中加載C函數以調用 46 p = os.getcwd() + '/libfunc.so' # 獲取當前的動態庫的絕對路徑位置 47 f = cdll.LoadLibrary(p) # 使用LoadLibrary接口加載C語言動態庫 48 function = f.py_point_address # 從當前庫當中取得clib中的函數py_point_address,並重命名為function 49 function.argtypes = [POINTER(c_byte)] # 設置當前函數的輸入參數 50 51 # Python Class類 到 C struct結構體 數據類型的傳輸轉換 52 Sfunction = f.py_struct_address # 從當前庫當中取得clib中的函數py_struct_address,並重命名為Sfunction 53 Sfunction.argtypes = [POINTER(POINT), POINTER(ctypes.c_char)] # 設置當前函數的輸入參數 54 55 p = create_string_buffer(b"Hello World", 15) # create a 10 byte buffer 56 print('Line', sys._getframe().f_lineno, ':', p,sizeof(p), repr(p.raw)) 57 58 time_start = time.time() 59 Res = Sfunction(addrTest, p) 60 time_end = time.time() 61 for i in range(LENGTH): 62 print(addrTest[0].addr[i]) 63 64 print('Line', sys._getframe().f_lineno, ':', addrTest[0].x, addrTest[0].y, addrTest[0].len, addrTest[0].addr) 65 print('Line', sys._getframe().f_lineno, ':', 'TimeCost:', (time_end - time_start)*1000, 'ms') # 記錄當前函數調用消耗的時間 66 67 # Numpy 數據類型等相互轉換測試(將內存數據轉換值Python當中) 68 ImgW = 1669 # 圖像寬度 69 ImgH = 21 # 圖像高度 70 ImgC = 3 # 圖像通道數 71 ImgL = ImgW*ImgH*ImgC # 圖像總長度 72 # ImgArray = np.array([[0, 1], [2, 3]], dtype=np.uint8) 73 ImgArray = np.zeros((ImgW,ImgH,ImgC), dtype=np.ubyte) # 申請圖像總空間為多維 zeros 矩陣 74 # ImgArray = np.array([1,2,3,4], dtype=np.int32) 75 # print(ImgArray) 76 # print(ImgArray.ctypes.data) 77 print(ImgArray.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8))) # 將numpy數組轉換為地址表示方式 data_as 並打印數據類型 78 ImgArray_addr = ImgArray.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)) # 獲取的指針放置在ImgArray_addr變量當中 79 print('Line', sys._getframe().f_lineno, ':', ImgArray_addr[0],ImgArray_addr[1],ImgArray_addr[2],ImgArray_addr[3]) 80 # print(ImgArray.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)).contents) 81 # print(ImgArray.ctypes.data_as(ctypes.POINTER(ctypes.c_uint16)).contents) 82 # print(ImgArray.ctypes.shape) 83 # print(ImgArray.ctypes.strides) 84 85 Imgfunction = f.py_img_address # 從當前庫當中取得clib中的函數py_struct_address,並重命名為Sfunction 86 Imgfunction.argtypes = [POINTER(ctypes.c_ubyte), ctypes.c_uint32] # 設置當前函數的輸入參數規划 usigned char *pointer, unsigned int arg 87 Imgfunction.restype = ctypes.c_int # 設置當前函數返回值參數為 int 類型 88 time_start = time.time() 89 Res = Imgfunction(ImgArray_addr, ImgL) # 調用Imgfunction函數 90 time_end = time.time() 91 print('Line', sys._getframe().f_lineno, ':', 'TimeCost:', (time_end - time_start)*1000, 'ms') # 記錄當前函數調用消耗的時間 92 93 print('Line', sys._getframe().f_lineno, ':\n', ImgArray[:,:,0]) # 打印0通道的數據 94 print('Line', sys._getframe().f_lineno, ':\n', ImgArray[:,:,1]) # 打印1通道的數據 95 print('Line', sys._getframe().f_lineno, ':\n', ImgArray[:,:,2]) # 答應2通道的數據 96 97 # 一般類型定義以及數據取地址方法 int *pi = &i; 98 i = ctypes.c_int(42) # 定義一個整數類型的變量,變量初始值為 42,相當於C語言中的 int i=42; 99 pi = ctypes.pointer(i) # 通過使用pointer接口獲取,相當於C語言中的 int *pi = &i; 100 print('Line', sys._getframe().f_lineno, ':', pi.contents) # 查看指針變量的信息 101 print('Line', sys._getframe().f_lineno, ':', pi[0]) # 查看指針所指向的內容,相當於C語言中的 *pi; 102 103 # ctypes.c_byte 類型的數組定義,等效於 byte a[BUFF_SIZE]; 104 a = (c_byte * BUFF_SIZE)() # 定義一個大小為BUFF_SIZE指針實例作為緩存 105 # cast(a, POINTER(c_uint8)) # 函數可以將一個指針實例強制轉換為另一種 ctypes 類型 106 print('Line', sys._getframe().f_lineno, ':', a) 107 print('Line', sys._getframe().f_lineno, ':', type(a)) 108 time_start = time.time() 109 function(a) # 執行function函數並傳入a地址參數 110 time_end = time.time() 111 112 for i in range(10): 113 print(a[i]) 114 115 print('Line', sys._getframe().f_lineno, ':', 'TimeCost:', (time_end - time_start)*1000, 'ms') # 記錄當前函數調用消耗的時間 116 117 # 使用byref獲取ctypes類型數據的地址 118 data = ctypes.c_uint8(10) # 定義一個整數類型的變量,變量初始值為 10,相當於C語言中的 char data=42; 119 data_addr = ctypes.byref(data, 0) # 通過使用byref接口獲取地址,相當於C語言中的 char *data_addr = &data; byref(obj, offset) 對應於這段 C 代碼:(((char *)&obj) + offset) 120 print('Line', sys._getframe().f_lineno, ':', type(data)) 121 print('Line', sys._getframe().f_lineno, ':', type(data_addr)) 122 123 # 使用id獲取變量在python的地址 124 value = 'hello world' # 定義一個字符串變量 125 address = id(value) # 獲取value的地址,賦給address 126 get_value = ctypes.cast(address, ctypes.py_object).value # 讀取地址中的變量 127 print('Line', sys._getframe().f_lineno, ':', address, get_value) # 128 129 # 一般Clib函數的調用 130 res = f.func(99) # 普通函數調用 131 print('Line', sys._getframe().f_lineno, ':', res)
function.c

1 #include <stdio.h> 2 #include <sys/shm.h> 3 #include <string.h> 4 #include <stdlib.h> 5 #include <time.h> 6 #include <sys/time.h> 7 8 9 #define BUFF_SIZE 6*1024*1024 10 11 typedef struct T_POINT{ 12 int x; 13 int y; 14 char * addr; 15 int len; 16 }POINT; 17 18 time_t get_timestamp_us(void) 19 { 20 time_t timestamp_ms = 0; 21 struct timeval tv; 22 23 gettimeofday(&tv,NULL); 24 timestamp_ms = tv.tv_sec * 1000 * 1000 + tv.tv_usec; 25 return timestamp_ms; 26 } 27 28 char *file_read(unsigned long *file_bytes, char *file_name) 29 { 30 int file_size; 31 FILE *fd = NULL; 32 char *file_data = NULL; 33 fd = fopen(file_name, "rw"); 34 if(fd < 0) 35 { 36 printf("File open failed...\n"); 37 return NULL; 38 } 39 fseek(fd, 0, SEEK_END); 40 file_size = ftell (fd); 41 file_data = malloc(sizeof(char)*file_size); 42 if(file_data == NULL) 43 { 44 printf("Malloc failed...\n"); 45 return NULL; 46 } 47 fseek(fd, 0, SEEK_SET); 48 *file_bytes = fread(file_data,sizeof(char),file_size,fd); 49 fclose(fd); 50 return file_data; 51 } 52 53 /* func.c */ 54 int func(int a) 55 { 56 return a*a; 57 } 58 59 void cycle_calc(int b) 60 { 61 int count = 100; 62 while(count--){ 63 b*=2; 64 printf("%d - %d\n", count, b); 65 } 66 } 67 68 unsigned char * c_point_address(void) 69 { 70 unsigned char *Img = malloc(sizeof(unsigned char)*1000); 71 printf("C-Address:%hhn\n", Img); 72 memset(Img, 20, 1000); 73 return Img; 74 } 75 76 int py_point_address(unsigned char * Addr) 77 { 78 unsigned char *Img = malloc(sizeof(unsigned char)*BUFF_SIZE); 79 // printf("C-Address:%x\n", Img); 80 // printf("Python-Address:%x\n", Addr); 81 memset(Img, 20, BUFF_SIZE); 82 memcpy((unsigned char * )Addr, Img, BUFF_SIZE); 83 return 1; 84 } 85 86 int py_struct_address(POINT *pt_POINT, char *str) 87 { 88 int i = 0; 89 for(i=0; i < pt_POINT->len; i++) 90 { 91 pt_POINT->addr[i] = i; 92 } 93 pt_POINT->x = 16; 94 pt_POINT->y = 17; 95 printf("FunctionPrint:%s\n",str); 96 return 1; 97 } 98 99 int py_img_address(unsigned char *data, unsigned int lenght) 100 { 101 time_t st,et; 102 unsigned long count=0; 103 unsigned long i=0; 104 st = get_timestamp_us(); 105 unsigned char *ImgData = file_read(&i, "./Test.PNG"); 106 et = get_timestamp_us(); 107 printf("C ### ReadFile time Cost:%ld\n", et - st); 108 109 printf("Data[%ld]:%d\n", count, *(ImgData+i-1)); 110 111 st = get_timestamp_us(); 112 memcpy(data, ImgData, sizeof(unsigned char)*lenght); 113 et = get_timestamp_us(); 114 printf("C ### Memcpy time Cost:%ld\n", et - st); 115 116 printf("py_img_address FunctionPrint:%ld\n", i); 117 return 1; 118 }
這里將存儲在data一維數組中的數據直接拷貝到二維數組空間中是沒有問題的,中間不會存在內存跨越,直接拷貝即可,拷貝完成后可對數據進行 numpy.reshape 等各種操作,完成數據索引方式的更改。(一定要理解一維、二維、多維數組早內存中的存儲方式都是線性儲存的!)
exe_shell.sh
1 #!/bin/bash 2 gcc -fPIC -shared function.c -o libfunc.so 3 python3 MemoryTest.py
II) 測試結果如下(測試平台: Ubuntu 18.04.6 LTS + Intel(R) Core(TM) i5-10400F CPU @ 2.90GHz ):
Line 91 : TimeCost: 0.07772445678710938 ms 102KB Speed=1.25GB/s
二、采用共享內存方式進行IPC通信
內存共享基本方法參考 《進程間通信原理》 ,共享內存的方式需要通過其他通信方式進行進程間數據同步,從而保證共享內存在使用的過程中不被其他進程修改。
main.py

1 from ctypes import * 2 import numpy as np 3 import codecs 4 import datetime 5 6 SHM_SIZE = 1024*1024*20 # 20MBytes 7 SHM_KEY = 123559 8 9 OUTFILE="Shared.PNG" 10 try: 11 rt = CDLL('librt.so') 12 except: 13 rt = CDLL('librt.so.1') 14 15 shmget = rt.shmget 16 shmget.argtypes = [c_int, c_size_t, c_int] 17 shmget.restype = c_int 18 shmat = rt.shmat 19 shmat.argtypes = [c_int, POINTER(c_void_p), c_int] 20 shmat.restype = c_void_p 21 22 shmid = shmget(SHM_KEY, SHM_SIZE, 0o666) 23 24 if shmid < 0: 25 print ("System not infected") 26 else: 27 addr = shmat(shmid, None, 0) 28 f=open(OUTFILE, 'wb') 29 begin_time = datetime.datetime.now() 30 DataLength = int.from_bytes(string_at(addr,4), byteorder='little', signed=True) #這里數據文件是小端int16類型 31 ImgData = string_at(addr+4,DataLength) 32 end_time = datetime.datetime.now() 33 print(DataLength, ' Bytes') 34 print('Type:',type(ImgData),' Bytes:', len(ImgData)) 35 f.write(ImgData) 36 f.close() 37 #print ("Dumped %d bytes in %s" % (SHM_SIZE, OUTFILE)) 38 print("Success!",end_time-begin_time)
main.c

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/shm.h> 4 #include <string.h> 5 #include <time.h> 6 #include <sys/time.h> 7 8 #define SHAERD_MEM_SIZE 20 * 1024 * 1024 // 20MBytes 9 #define ImgWidth 1024 10 #define ImgHeight 1024 11 12 char mem_free(void *ptr); 13 time_t get_timestamp_ms(void); 14 char *file_read(unsigned int *file_bytes, char *file_name); 15 16 int main(int argc, char *argv[]) 17 { 18 int i,j; 19 int id = 0; 20 size_t offset = 0; 21 char *data = NULL; 22 unsigned char *ImgData = NULL; 23 unsigned char *TmpPointer = NULL; 24 unsigned int file_bytes = 0; 25 time_t start_time, end_time; 26 unsigned char ImgDataMatrix[ImgWidth][ImgHeight] = {0}; 27 28 if (argc < 2) 29 { 30 printf("args too less\n"); 31 return 0; 32 } 33 34 id = shmget(123559, SHAERD_MEM_SIZE, IPC_CREAT | 0777); 35 if (id < 0) 36 { 37 printf("get id failed\n"); 38 return 0; 39 } 40 41 data = shmat(id, NULL, 0); 42 if (data == NULL) 43 { 44 printf("shamt failed\n"); 45 return 0; 46 } 47 48 ImgData = file_read(&file_bytes, argv[1]); 49 offset = sizeof(unsigned int); 50 printf("Size of unsigned long:%ld\n", offset); 51 printf("Size of Image File:%d\n", file_bytes); 52 start_time = get_timestamp_ms(); 53 memcpy(data, &file_bytes, sizeof(unsigned int)); 54 memcpy(data + offset, ImgData, file_bytes); 55 end_time = get_timestamp_ms(); 56 57 printf("Time Cost:%ld\n", end_time - start_time); 58 59 mem_free(ImgData); 60 61 /* 這里代碼用來驗證多維數組在內存中存儲的順序方式是否為順序存儲,因此可以直接將一維數組中的數據拷貝至多維數組中,應為其內存存儲方式相同,只是索引方式不同 */ 62 ImgData = (unsigned char *)malloc(sizeof(unsigned char)*ImgHeight*ImgWidth); // 申請一維數組內存空間 63 memset(ImgData, 110, sizeof(unsigned char)*ImgWidth*ImgHeight); // 初始化一維數組內容 64 65 for(i=0;i<100;i++) // 設置部分特殊值參數用於驗證數據正確性 66 { 67 for (j = 100; j < 200; j++) 68 { 69 TmpPointer = ImgData + i*ImgWidth + j; 70 printf("CheckOut the TmpPointer is Continue? %x\n", TmpPointer); // 查看當前二維數組的地址在內存中的地址順序,可以發現打印結果是連續的 71 *TmpPointer = i+j; // 賦值特殊值用於驗證 72 } 73 } 74 75 memcpy(ImgDataMatrix, ImgData, sizeof(unsigned char)*ImgHeight*ImgWidth); // 拷貝內存數據到二維數組內存當中 76 77 for(i=0;i<100;i++) // 打印拷貝到二維數組中的數據,並驗證正確性 78 { 79 for (j = 100; j < 220; j++) 80 { 81 printf("ImgDataMatrix[%d][%d] = %d\n", i, j , ImgDataMatrix[i][j]); 82 } 83 } 84 85 printf("ImgDataMatrix[%d][%d] Address: %x\n",ImgWidth-5, ImgHeight-1, &ImgDataMatrix[ImgWidth-5][ImgHeight-1]); 86 printf("ImgDataMatrix[%d][%d] Address: %x\n",ImgWidth-4, 0, &ImgDataMatrix[ImgWidth-4][0]); 87 88 mem_free(ImgData); 89 return 0; 90 } 91 92 char *file_read(unsigned int *file_bytes, char *file_name) 93 { 94 int file_size; 95 FILE *fd = NULL; 96 char *file_data = NULL; 97 fd = fopen(file_name, "rw"); 98 if(fd < 0) 99 { 100 printf("File open failed...\n"); 101 return NULL; 102 } 103 fseek(fd, 0, SEEK_END); 104 file_size = ftell (fd); 105 file_data = malloc(sizeof(char)*file_size); 106 if(file_data == NULL) 107 { 108 printf("Malloc failed...\n"); 109 return NULL; 110 } 111 fseek(fd, 0, SEEK_SET); 112 *file_bytes = fread(file_data,sizeof(char),file_size,fd); 113 fclose(fd); 114 return file_data; 115 } 116 117 time_t get_timestamp_ms(void) 118 { 119 time_t timestamp_ms = 0; 120 struct timeval tv; 121 122 gettimeofday(&tv,NULL); 123 timestamp_ms = tv.tv_sec * 1000 + tv.tv_usec / 1000; 124 return timestamp_ms; 125 } 126 127 char mem_free(void *ptr) 128 { 129 if(NULL != ptr) 130 { 131 free(ptr); 132 return 0; 133 } 134 printf("Memory is Empty...\n"); 135 return -1; 136 }
編譯執行即可
gcc -o main main.c ./main sdlinux.zip # 12MB python3 main.py
測試結果如下(測試平台:Ubuntu 18.04.6 LTS + Intel(R) Core(TM) i5-10400F CPU @ 2.90GHz ):
Success! 7.091045379638672 ms 12MB Speed=1.67GB/s
注:這里還可以關注到,一維數組內容直接memcpy到二維數組當中是沒有問題的,這是因為二維數組在內存中的存儲方式依舊是線性連續的,只是索引方式不同而已,可以看到 ImgDataMatrix[1019][1023].addr + 1 = ImgDataMatrix[1020][0].addr 。
Reference:
REF1:Python--ctypes(數據類型詳細踩坑指南)
未完待續 ~