不,你并没有为 y->x
分配内存两次。
相反,你为这个结构体(其中包括一个指针)分配了内存,再为指针所指向的内容分配了内存。
可以这样理解:
1 2
+-----+ +------+
y------>| x------>| *x |
| n | +------+
+-----+
实际上,您需要两个分配(1
和2
)才能存储您所需的所有内容。
此外,您的类型应该是struct Vector *y
,因为它是一个指针,在C中不应该转换malloc
的返回值。
这可能会隐藏您不想要的某些问题,而且C完全可以隐式地将void*
返回值转换为任何其他指针。
当然,您可能希望封装这些向量的创建以使其更易于管理,例如使用以下头文件vector.h
:
struct Vector {
double *data;
size_t size;
};
struct Vector *newVector(size_t sz);
void delVector(struct Vector *vector);
然后,在vector.c
中,你有用于管理向量的实际函数:
#include "vector.h"
struct Vector *newVector(size_t sz) {
struct Vector *vector = malloc(sizeof (struct Vector));
if (vector == NULL)
return NULL;
vector->data = malloc(sz * sizeof (double));
if (vector->data == NULL) {
free(vector);
return NULL;
}
vector->size = sz;
return vector;
}
void delVector(struct Vector *vector) {
if (vector != NULL) {
free(vector->data);
free(vector);
}
}
通过这样封装向量管理,您可以确保向量要么完全构建,要么根本没有构建——不存在它们半构建的机会。
这还允许您在不影响客户端的情况下完全更改未来的基础数据结构。例如:
- 如果您想将它们变为稀疏数组以在空间和速度之间进行平衡。
- 如果您希望在每次更改时将数据保存到持久存储中。
- 如果您希望确保所有向量元素都初始化为零。
- 如果您想为效率而将向量大小与向量容量分开。(1)
您还可以添加更多功能,例如安全地设置或获取向量值(请参见标头中的注释代码),随着需要的出现而增加。
例如,您可以(作为一种选项)在有效范围之外的设置值时默默地忽略并在获取这些值时返回零。或者您可以引发某种错误,或者尝试在幕后自动扩展向量。(1)
在使用向量方面,一个简单的示例是以下内容(非常基本的)main.c
:
#include "vector.h"
#include <stdio.h>
int main(void) {
Vector myvec = newVector(42);
myvec.data[0] = 2.718281828459;
delVector(myvec);
}
(1) 可扩展向量的潜力需要进一步说明。
许多向量实现将容量与大小分开。前者是在需要重新分配之前可以使用的元素数量,后者是实际向量大小(始终<=容量)。
在扩展时,你希望通常以这样的方式扩展,即尽量不频繁进行,因为它可能是一项昂贵的操作。例如,你可以添加比严格必要的多5%,以便在连续添加一个元素的循环中,它不必为每个单个项重新分配。
malloc()
的返回值转换为类型。我永远不会理解为什么每个人都感觉需要这样做。 :( - unwindvoid *
不会自动转换为其他指针,需要进行强制转换(或者,在C++中,当然可以不使用malloc()
)。 - unwind