由于我仍然不明白问题是什么,我只是假设,并尝试“盲目地”回答。
我将首先回答准时的问题(在结尾处):
numpy.ctypeslib.as_ctypes 转换 NumPy 数组(或对象,或...) 变成一个CTypes。但是转换只发生在元数据上(因为 2 个模块不同)。 数组内容(或指针,或实际数组数据所在(或开始)的内存地址)保持不变(未复制/修改/更改,...)。
参考资料:
-
[NumPy.Docs]: C-Types Foreign Function Interface (numpy.ctypeslib)
1.1。源代码(末尾某处):[GitHub]: numpy/numpy - numpy/numpy/ctypeslib.py
-
[Python.Docs]: ctypes - A foreign function library for Python
因此,没有进行转置。
我不确定您所说的“预处理”是什么意思(as_ctypes 执行的所有检查和操作都适合吗?)。 “natural bit”也一样。
还要注意 as_ctypes(pyfunc_array_by_ref_modifying 的 1st 参数)完全不知道其余的(pnbrows em> 和 pnbcols 特别是)。
即使没有直接影响(我认为这是“运气”的问题),您可能想看看以下内容:[SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer)。
去(我认为是)真正的问题(导致问题):
- 数组通过reference(其缓冲区起始内存地址)传递给Fortran 子例程。我没有官方消息来源来说明这一点(从一开始就是一个假设),但这就像在 C 中传递一个指针(我猜它与 C绑定来自子程序声明)
- 没有传递元数据(行、列)(否则接下来的 2 个参数将无用)
(2D) 数组作为 1D 之一存储在内存中:第 1st 行,然后是第 2nd 一个,第三个rd,...,最后一个。
为了到达元素array[i][j] (i = [0, row_count), j = [0, column_count)),使用如下公式(指针逻辑):
array_start_address + array_element_size_in_bytes * (i * column_count + j)。
希望消除一些困惑,这里有一个小的 C 演示。我复制了(我认为的)Fortran 子程序的行为。我还增加了行数以使事情更清晰。
main00.c:
#include <stdio.h>
#include <string.h>
#define ROWS 3
#define COLS 6
//#pragma align 4
typedef int Array2D[ROWS][COLS];
/*
void modifyArray(Array2D array, int rows, int cols, int coef) {
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; ++j)
array[i][j] *= coef;
}
//*/
void modifyPtr(int *pArray, int rows, int cols, int coef) { // This is the equivalent.
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; ++j)
(*(pArray + (i * cols + j))) *= coef;
}
void printArray(Array2D array, int rows, int cols, char *head) {
printf("\n%s:\n", head);
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j)
printf("% 3d ", array[i][j]);
printf("\n");
}
}
void modify(Array2D arr, int rows, int cols, int coef, char *head) {
printf("\nRows: %d, Cols: %d, Coef: %d", rows, cols, coef);
Array2D arr_dup;
memcpy(arr_dup, arr, sizeof(Array2D));
modifyPtr(arr_dup, rows, cols, coef);
printArray(arr_dup, ROWS, COLS, head);
}
int main() {
Array2D arr = { // ROWS X COLS
{ 1, 2, 3, 4, 5, 6 },
{ 7, 8, 9, 10, 11, 12 },
{ 13, 14, 15, 16, 17, 18 },
};
char *modifiedText = "Modified";
//printf("Array size: %d %d %d\n", sizeof(arr), sizeof(arr[0]), sizeof(arr[0][0]));
printArray(arr, ROWS, COLS, "Original");
modify(arr, 3, 6, 2, modifiedText);
printArray(arr, ROWS, COLS, "Original");
modify(arr, 2, 6, 2, modifiedText);
modify(arr, 1, 6, 2, modifiedText);
modify(arr, 3, 5, 2, modifiedText);
modify(arr, 3, 4, 2, modifiedText);
modify(arr, 5, 2, 2, modifiedText);
modify(arr, 7, 1, 2, modifiedText);
printf("\nDone.\n");
return 0;
}
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q068314707]> sopr.bat
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2019\VC\Auxiliary\Build\vcvarsall.bat" x64 > nul
[prompt]> dir /b
code00.py
example.f90
main00.c
[prompt]>
[prompt]> cl /nologo /MD /W0 main00.c /link /NOLOGO /OUT:main00_pc064.exe
main00.c
[prompt]> dir /b
code00.py
example.f90
main00.c
main00.obj
main00_pc064.exe
[prompt]>
[prompt]> main00_pc064.exe
Original:
1 2 3 4 5 6
7 8 9 10 11 12
13 14 15 16 17 18
Rows: 3, Cols: 6, Coef: 2
Modified:
2 4 6 8 10 12
14 16 18 20 22 24
26 28 30 32 34 36
Original:
1 2 3 4 5 6
7 8 9 10 11 12
13 14 15 16 17 18
Rows: 2, Cols: 6, Coef: 2
Modified:
2 4 6 8 10 12
14 16 18 20 22 24
13 14 15 16 17 18
Rows: 1, Cols: 6, Coef: 2
Modified:
2 4 6 8 10 12
7 8 9 10 11 12
13 14 15 16 17 18
Rows: 3, Cols: 5, Coef: 2
Modified:
2 4 6 8 10 12
14 16 18 20 22 24
26 28 30 16 17 18
Rows: 3, Cols: 4, Coef: 2
Modified:
2 4 6 8 10 12
14 16 18 20 22 24
13 14 15 16 17 18
Rows: 5, Cols: 2, Coef: 2
Modified:
2 4 6 8 10 12
14 16 18 20 11 12
13 14 15 16 17 18
Rows: 7, Cols: 1, Coef: 2
Modified:
2 4 6 8 10 12
14 8 9 10 11 12
13 14 15 16 17 18
Done.
返回 Fortran(在本地保存您的脚本)+ Python(创建一个新脚本)。
code00.py:
#!/usr/bin/env python
import sys
import ctypes as ct
import numpy as np
IntPtr = ct.POINTER(ct.c_int)
DLL_NAME = "./example.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")
def modify(arr, rows, cols, coef, modify_func):
print("\nRows: {:d}, Cols {:d}, Coef: {:d}".format(rows, cols, coef))
arr_dup = arr.copy()
arr_ct = np.ctypeslib.as_ctypes(arr_dup)
rows_ct = ct.c_int(rows)
cols_ct = ct.c_int(cols)
coef_ct = ct.c_int(coef)
modify_func(arr_ct, ct.byref(rows_ct), ct.byref(cols_ct), ct.byref(coef_ct))
print("Modified array:\n {:}".format(arr_dup))
def main(*argv):
dll00 = ct.CDLL(DLL_NAME)
func = getattr(dll00, "array_by_ref_modifying")
#func.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
func.argtypes = (ct.c_void_p, IntPtr, IntPtr, IntPtr)
func.restype = None
arr = np.array([
[1, 2, 3, 4, 5, 6],
[7, 8, 9, 10, 11, 12],
[13, 14, 15, 16, 17, 18],
], dtype=ct.c_int)
print("Original array:\n {:}".format(arr))
modify(arr, 3, 6 , 2, func)
modify(arr, 2, 6 , 2, func)
modify(arr, 1, 6 , 2, func)
modify(arr, 3, 5 , 2, func)
modify(arr, 3, 4 , 2, func)
modify(arr, 5, 2 , 2, func)
modify(arr, 7, 1 , 2, func)
if __name__ == "__main__":
print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32, sys.platform))
rc = main(*sys.argv[1:])
print("\nDone.")
sys.exit(rc)
输出:
[prompt]> "f:\Install\pc032\Intel\OneAPI\Version\compiler\2021.3.0\windows\bin\intel64\ifort.exe" /c example.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.3.0 Build 20210609_000000
Copyright (C) 1985-2021 Intel Corporation. All rights reserved.
[prompt]> link /NOLOGO /DLL /OUT:example.dll /LIBPATH:"f:\Install\pc032\Intel\OneAPI\Version\compiler\2021.3.0\windows\compiler\lib\intel64_win" example.obj
Creating library example.lib and object example.exp
[prompt]> dir /b
code00.py
example.dll
example.exp
example.f90
example.lib
example.mod
example.obj
main00.c
main00.obj
main00_pc064.exe
[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.08.07_test0\Scripts\python.exe" code00.py
Python 3.8.7 (tags/v3.8.7:6503f05, Dec 21 2020, 17:59:51) [MSC v.1928 64 bit (AMD64)] 064bit on win32
Original array:
[[ 1 2 3 4 5 6]
[ 7 8 9 10 11 12]
[13 14 15 16 17 18]]
Rows: 3, Cols 6, Coef: 2
Modified array:
[[ 2 4 6 8 10 12]
[14 16 18 20 22 24]
[26 28 30 32 34 36]]
Rows: 2, Cols 6, Coef: 2
Modified array:
[[ 2 4 6 8 10 12]
[14 16 18 20 22 24]
[13 14 15 16 17 18]]
Rows: 1, Cols 6, Coef: 2
Modified array:
[[ 2 4 6 8 10 12]
[ 7 8 9 10 11 12]
[13 14 15 16 17 18]]
Rows: 3, Cols 5, Coef: 2
Modified array:
[[ 2 4 6 8 10 12]
[14 16 18 20 22 24]
[26 28 30 16 17 18]]
Rows: 3, Cols 4, Coef: 2
Modified array:
[[ 2 4 6 8 10 12]
[14 16 18 20 22 24]
[13 14 15 16 17 18]]
Rows: 5, Cols 2, Coef: 2
Modified array:
[[ 2 4 6 8 10 12]
[14 16 18 20 11 12]
[13 14 15 16 17 18]]
Rows: 7, Cols 1, Coef: 2
Modified array:
[[ 2 4 6 8 10 12]
[14 8 9 10 11 12]
[13 14 15 16 17 18]]
Done.
结论:
- 在 2 个示例中以相同的方式修改数组,证实了我的假设
- 无论传递什么行号和列号,数组都是从头修改,从左到右,从上到下。这可能有点令人困惑(当描绘 2D 表示并例如传递一个小于实际列号的列号时)。这就是为什么它使用比实际尺寸更大的尺寸。唯一需要注意的是不要超过元素的数量(row_count * column_count),因为这会产生未定义的行为(它可能会崩溃)
- 我不确定这一点,但还是要提一下:可能还有其他一些未定义行为情况,例如数组中的每一行都将被填充由编译器正确对齐(类似于
#pragma pack)。示例:具有 7 列(一行将有 7 个字节)的 char 数组可能是 8 个字节对齐的。不确定指针逻辑如何处理这个问题