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(数据类型详细踩坑指南)
未完待续 ~