在GPL3下分发的一个非常着名的 C 代码中,我发现了以下程序(我只是在这里报告一个最小的例子而不是简化的整个代码)。
#include <stdio.h>
typedef struct {
float *x;
} mystruct;
mystruct var_struct;
#define ALPHA 0
#define BETA 1
#define GAMMA 2
#define DELTA 3
#define MACRO1 var_struct.x[ALPHA]
#define MACRO2 var_struct.x[BETA]
#define MACRO3 var_struct.x[GAMMA]
#define MACRO4 var_struct.x[DELTA]
int main()
{
mystruct var_struct;
float a = 5.6f;
MACRO1 = a;
return 0;
}
我承认我对这种写入和使用指针的方式感到很困惑,因为我更喜欢每个单独的字段用于结构本身的指针而不是共享指针。但这就是我发现的 - 因为我认为它不起作用 - 我决定一步一步地分析代码,这样我就可以学到新的东西或理解我还不知道的东西。
无论如何,如果我在平台架构(ARM7)上运行代码,编译和运行。如果我在gcc (GCC)5.3.1 20151207(Red Hat 5.3.1-2)上尝试相同的代码,编译但不运行(分段错误)。
问题#1: 对我来说,希望程序员使用一个指向变量的共享指针,这将简单地使用MACROx定义的宏进行覆盖。但在这种情况下,我永远无法确定指定给指针* x的值,如果我没有跟踪我已经使用过的宏。或者我错了吗?
问题#2: 如上所述,here #define宏只是替换代码中的文本。因此,即使我使用MACRO1,MACRO2或MACRO3 ......它们只是引用相同的变量。在这种情况下,通过它在内存中的地址。或者不是?
问题#3: 这种代码风格是否与最常用和扩展的C技术和C编码规则兼容?我不这么认为......但我不确定。也许程序员正在使用一些非常有效的代码编写方法,这些方法并不常见。
更新
感谢许多答案和评论。
为什么我没有发布代码?!? 因为否则我会提醒发布一个最小的例子而不是整个代码。所以,只是尊重规则。我什么都不隐瞒。
所以here代码。我正在谈论的结构是nav_ukf.h
声明的,它被称为navUkfStruct_t
在文件nav_ukf.c
中,您可以在函数navUkfInitState()
中读取这些定义的使用方式。
我找不到任何malloc
或calloc
。
问题#4 :由于必须使用上述函数之一(malloc,calloc)分配指针,为什么最小的示例有效?!?
答案 0 :(得分:2)
在原始代码中,navUkfData.x
在写入任何内容之前已初始化。您可能错过了这个,因为您只搜索宏,而不是直接使用navUkfData.x
(这表明为什么这种代码被认为是糟糕的风格的几个原因之一)。但是你找到了正确的功能:navUkfInitState
的关键行是
navUkfData.kf = srcdkfInit(SIM_S, SIM_M, SIM_V, SIM_N, navUkfTimeUpdate);
navUkfData.x = srcdkfGetState(navUkfData.kf);
srcdkfGetState
定义为here:
float *srcdkfGetState(srcdkf_t *f) {
return f->x.pData;
}
和srcdkfInit
定义为here:
srcdkf_t *srcdkfInit(int s, int m, int v, int n, SRCDKFTimeUpdate_t *timeUpdate) {
...
matrixInit(&f->x, s, 1);
...
和matrixInit
定义为here:
void matrixInit(arm_matrix_instance_f32 *m, int rows, int cols) {
float32_t *d;
d = (float32_t *)aqDataCalloc(rows*cols, sizeof(float32_t));
arm_mat_init_f32(m, rows, cols, d);
arm_fill_f32(0, d, rows*cols);
}
和arm_mat_init_f32
定义为here:
void arm_mat_init_f32(
arm_matrix_instance_f32 * S,
uint16_t nRows,
uint16_t nColumns,
float32_t * pData)
{
/* Assign Number of Rows */
S->numRows = nRows;
/* Assign Number of Columns */
S->numCols = nColumns;
/* Assign Data pointer */
S->pData = pData;
}
因此,最终,navUkfData.x
将被设置为指向由...分配的内存
aqDataCalloc
,其定义我将不引用,因为它似乎特定于某个特定的嵌入式硬件。
所以问题的答案是:
如果我在平台架构(ARM7)上运行代码,它就会编译并运行。如果我在gcc(GCC)5.3.1 20151207(Red Hat 5.3.1-2)上尝试相同的代码,它会编译,但它不会运行(分段错误)。
如果这是在谈论您的缩减示例:只有出现才能在#34;平台架构&#34;上偶然工作。这是错误的,因为它永远不会初始化x
。这些宏不需要初始化x
。
如果这是在谈论原始程序:它似乎已被编写为仅在特定的嵌入式硬件上运行。让它在这种情况之外工作可能需要几个月的努力。
对我来说,希望程序员使用一个指向变量的共享指针,这将简单地使用MACROx定义的宏进行覆盖。但在这种情况下,我永远无法确定指定给指针* x的值,如果我没有跟踪我已经使用过的宏。或者我错了吗?
我不确定我是否理解这个问题,但是:使用MACROx宏不会改变指针x
本身,只改变x
指向的数组元素。你的困惑可能是这些宏风格不佳的另一个原因。
#define宏只是替换代码中的文本。因此,即使我使用MACRO1,MACRO2或MACRO3 ......它们只是引用相同的变量
它们都是指&#34;相同的变量&#34;从某种意义上说,它们都是指x
。但是,x
是一个数组,它们引用该数组的不同元素。
这样的代码风格是否与最常用和扩展的C技术和C编码规则兼容?我不这么认为......但我不确定。也许程序员正在使用一些非常有效的代码编写方法,这些方法并不常见。
正如我在前面的评论中所说的那样:这种诡计使用宏使用是正常且不起眼的,但现在却强烈反对。正如这个问题所表明的那样,它不再被认为是可接受的一个重要原因是它会使代码的新读者感到困惑。回到那一天(我在这里谈论1985-2000)这一点不太重要。
做这种事情的主要理由,当它是正常的时候,就是简洁(当你拥有80x25玻璃杯时非常重要)和可怜人的封装(现在你的状况更好了)如果你需要更多的封装,跳转到C ++)。效率
。为什么我没有发布代码?!?因为否则我会提醒发布一个最小的例子而不是整个代码。所以,只是尊重规则。我没有隐藏任何东西。
为了提出这个问题,简化代码是合适的。但是,您还已经发布了原始链接,因为您知道自己并不了解原始代码,因此您也应该知道您可能已经错误地进行了简化。这样可以节省每个人一些时间。
答案 1 :(得分:0)
假设原始代码正常且有效,则有两种可能:
分配 - 可能是在内存中分配结构。
mystruct var_struct;
var_struct.x = malloc(5 * sizeof(float));
// then you can do this:
var_struct.x[3] = 5.6f;
将Struct放置在“其他”之上:
float placeholder[5];
mystruct var_struct;
var_struct.x = &placeholder;
// then you can do this:
var_struct.x[3] = 5.6f;
还有第三种可能性 - 这是C ++代码,而不是指针,他们使用引用。结果与带指针的C相同。