或者,从哪一年/标准版本/编译器/编译器版本...我们可以这样做吗?
文件:fretstruct.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* this is global for MRFE simplicity - please don't do this. */
struct rectangle {
int width;
int height;
};
struct rectangle *create_rectangle(int width, int height) {
struct rectangle *retrect = malloc(sizeof *retrect);
retrect->width = width;
retrect->height = height;
return retrect;
}
int main (void) {
int rect_width = create_rectangle(20, 10)->width;
printf("width: %d\n", rect_width);
printf("Press [RET] or [ENTER] key to exit the program...");
getc(stdin);
return 0;
}
输出:
width: 20
Press any key to exit the program...
我对针对 GNU17(默认)编译的结果并不感到惊讶
gcc -Wall -o fretstruct fretscruct.c
-Ubuntu 22.04(Linux 5.19)GCC 12.2.0
gcc -Wall -o fretstruct.exe fretscruct.c
- Windows 11 (NT) GCC 12.1.0
但是我做了NOT期望针对C89(ANSI C)编译得到相同的结果
gcc -Wall -std=c89 -pedantic -o fretstruct fretstruct.c
-Ubuntu 22.04(Linux 5.19)GCC 12.2.0
gcc -Wall -std=c89 -pedantic -o fretstruct.exe fretstruct.c
- Windows 11 (NT) GCC 12.1.0
但是对于 C89,我没有编译错误和 exact 相同的输出。
现在,我知道在 C 函数调用中计算它们在表达式中的返回值,但在这种情况下,这似乎很浪费,对结构的引用连同创建它的堆栈帧一起丢失,所以我们无法获得对于实际的结构,不同的是,如果我们创建了一个静态分配的结构,将其传递给函数并使用修改后的值返回它,那么该结构仍然可以访问,但是,修改后的值呢?,因为这样做(从函数调用访问单个成员)我们丢弃了返回给我们的副本,以及该副本,除了我们通过调用访问的成员之外对其成员的所有修改。我希望你初始化静态分配的结构,否则它的成员肯定会包含垃圾数据。
与'信息'功能一起使用时似乎有一些用处例如:
假设我们有一个矩形数组 (
rect_array
),这个函数:
struct rectangle *get_rect_at(struct rectangle *rect, int index) { /* No pun intended ;) */
return rect + index;
}
我们可以像这样打一个
printf()
电话:
int index = 2;
printf("The rectangle at index '%d' is: (%d, %d).", index, get_rect_at(rect_array, index)->width, get_rect_at(rect_array, index)->height);
或者,由于封装不完全是 C 的东西(不需要访问器),也许我们可以跳过这个函数,直接访问成员。
int index = 2;
printf("The rectangle at index '%d' is: (%d, %d).", index, rect_array[index].width, rect_array[index].height);
但是,从函数访问数组的元素可能是个好主意,您可以添加代码来检查索引是in绑定inside函数但在这种情况下,您可能仍想检查在尝试访问该值或检查另一个异常之前返回函数值(例如针对
NULL
),在这两种情况下,立即访问结构的成员都不是一个好主意,甚至保证; 我可以添加这个例子的完整代码,如果这样做可以澄清我的观点.
所以这是完整的和某种程度上简化的例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* this is global for MCFE simplicity - please don't do this. */
struct rectangle {
int width;
int height;
};
struct rectangle *create_rectangle(int width, int height) {
struct rectangle *retrect = malloc(sizeof *retrect);
retrect->width = width;
retrect->height = height;
return retrect;
}
struct rectangle *create_rect_arr(int *width_arr, int *height_arr, size_t nelems) {
struct rectangle *rect_arr = malloc(sizeof(struct rectangle *) * nelems);
int i; /* For our loop, C90 whines about declaration inside the loop (the specifications) */
for(i = 0; i < nelems; i++) {
(rect_arr + i)->width = *(width_arr + i);
(rect_arr + i)->height = *(height_arr + i);
}
return rect_arr;
}
struct rectangle *get_rect_at(struct rectangle *rect, size_t array_len, int index) { /* No pun intended ;) */
return (index < array_len) ? rect + index : NULL;
}
int main (void) {
/* declarations block (for pedantic C89-C90) */
int rect_width = create_rectangle(20, 10)->width;
int width_array[] = {2, 4, 8, 8, 9, 16};
int height_array[] = {8, 4, 4, 10, 12, 16};
size_t width_arr_len = (sizeof width_array) / (sizeof width_array[0]);
size_t height_arr_len = (sizeof height_array) / (sizeof height_array[0]);
printf("width: %d\n", rect_width);
if(width_arr_len - height_arr_len) {
printf("the lenghts of \'width_array\' and \'height_array\' are not the same!");
/* return 1; // Uncomment if your done with the program if this is a no-deal */
} else {
size_t arr_len = width_arr_len;
struct rectangle *rect_array = create_rect_arr(width_array,
height_array,
arr_len);
if(rect_array) {
int first_square_index = 1;
int last_square_index = 5;
/* let's still avoid try to dereference a null pointer. */
if(first_square_index < arr_len &&
last_square_index < arr_len) {
printf("first square is: (%d, %d)\n",
get_rect_at(rect_array, arr_len,
first_square_index)->width,
get_rect_at(rect_array, arr_len,
first_square_index)->height);
printf("last square is: (%d, %d)\n",
get_rect_at(rect_array, arr_len,
last_square_index)->width,
get_rect_at(rect_array, arr_len,
last_square_index)->height);
printf("Or, without the function:\n");
printf("first square is: (%d, %d)\n",
rect_array[first_square_index].width,
rect_array[first_square_index].height);
printf("last square is: (%d, %d)\n",
rect_array[last_square_index].width,
rect_array[last_square_index].height);
} else {
printf("Either 'first_square_index' or 'last_square_index' - or both, are out of the boundaries of the array.");
}
} else {
printf("Couldn't allocate enought memory for \'rect_array\'\n");
}
}
printf("Press [RET] or [ENTER] key to exit the program...");
getc(stdin);
return 0;
它利用这种方式从不同上下文中的函数调用访问结构成员,并或多或少地影响“不可释放”的内存块(抱歉我的英语混蛋)。
所以,就是这样。重申一下,我的主要问题(针对 C 语言的老手)是:我们是否始终能够做到这一点?或者,从什么时候开始?而且您不想将其添加到您的答案中或添加评论,它真的有用吗?。
提前致谢。
回答您的第一个问题,因为我们什么时候可以直接访问从函数调用返回的结构的成员,就像您在示例中显示的那样:
它一直是 C 的一个特性,包括在 ANSI C (C89/C90) 和后续版本中。
你的第二个问题,我认为更多的是关于在处理封装和适当的内存管理时什么是好的编程实践。特别是在 C 中使用结构时。
正如您上面提到的,直接访问从函数调用返回的结构成员,确实会导致潜在的内存问题和未定义的行为。所以通常使用访问器函数或通过指针访问成员来访问结构是一个很好的做法。