C 动态增长数组
我有一个程序可以读取游戏中实体的“原始”列表,并且我打算创建一个数组来保存不确定数量的实体的索引号(int),以处理各种事物。我想避免使用太多的内存或 CPU 来保存此类索引...
到目前为止,我使用的一个快速而肮脏的解决方案是在主处理函数(本地焦点)中声明具有最大游戏实体大小的数组,以及另一个整数来跟踪已添加到列表中的数量。 这并不令人满意,因为每个列表都包含 3000 多个数组,虽然不算多,但感觉有点浪费,因为我可能会使用 6-7 个列表的解决方案来实现不同的功能。
我还没有找到任何 C(不是 C++ 或 C#)特定的解决方案来实现此目的。我可以使用指针,但我有点害怕使用它们(除非这是唯一可能的方法)。
数组不会离开本地函数作用域(它们将被传递给函数,然后被丢弃),以防发生变化。
如果指针是唯一的解决方案,我如何跟踪它们以避免泄漏?
I have a program that reads a "raw" list of in-game entities, and I intend to make an array holding an index number (int) of an indeterminate number of entities, for processing various things. I would like to avoid using too much memory or CPU for keeping such indexes...
A quick and dirty solution I use so far is to declare, in the main processing function (local focus) the array with a size of the maximum game entities, and another integer to keep track of how many have been added to the list.
This isn't satisfactory, as every list holds 3000+ arrays, which isn't that much, but feels like a waste, since I'll possible use the solution for 6-7 lists for varying functions.
I haven't found any C (not C++ or C#) specific solutions to achieve this. I can use pointers, but I am a bit afraid of using them (unless it's the only possible way).
The arrays do not leave the local function scope (they are to be passed to a function, then discarded), in case that changes things.
If pointers are the only solution, how can I keep track of them to avoid leaks?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
如果您需要动态数组,则无法转义指针。你为什么害怕呢?它们不会咬人(只要你小心)。 C 中没有内置的动态数组,您只需自己编写一个。在 C++ 中,您可以使用内置的
std::vector
类。 C# 和几乎所有其他高级语言也有一些类似的类来为您管理动态数组。如果您确实打算编写自己的数组,可以从这里开始:大多数动态数组实现都是从某个(小)默认大小的数组开始工作的,然后每当添加新元素时空间不足时,将数组的大小。正如您在下面的示例中看到的,这根本不是很困难:(为了简洁起见,我省略了安全检查)
使用它也很简单:
If you need a dynamic array, you can't escape pointers. Why are you afraid though? They won't bite (as long as you're careful, that is). There's no built-in dynamic array in C, you'll just have to write one yourself. In C++, you can use the built-in
std::vector
class. C# and just about every other high-level language also have some similar class that manages dynamic arrays for you.If you do plan to write your own, here's something to get you started: most dynamic array implementations work by starting off with an array of some (small) default size, then whenever you run out of space when adding a new element, double the size of the array. As you can see in the example below, it's not very difficult at all: (I've omitted safety checks for brevity)
Using it is just as simple:
一种简单的解决方案涉及
mmap
。如果您可以容忍 POSIX 解决方案,那么这非常棒。只需映射整个页面并防止溢出,因为realloc
对于此类值无论如何都会失败。现代操作系统不会承诺全部,直到您使用它,并且您可以根据需要截断文件。或者,还有
realloc
。就像所有一开始看起来比后来更可怕的事情一样,克服最初的恐惧的最好方法就是让自己沉浸在未知的不适中!毕竟,有时候我们学到的东西最多。不幸的是,存在局限性。例如,当您仍在学习使用函数时,您不应该扮演老师的角色。我经常阅读那些看似不知道如何使用
realloc
(即当前接受的答案!)的人的答案,告诉其他人如何错误地使用它,有时是在幌子下他们忽略了错误处理,尽管这是一个需要提及的常见陷阱。 这是一个解释如何正确使用realloc
的答案。 请注意,答案是将返回值存储到不同的变量中,以便执行错误检查。每次调用函数以及每次使用数组时,你正在使用一个指针。这些转换是隐式发生的,如果有的话应该更可怕,因为我们看不到的东西往往会导致最多的问题。例如,内存泄漏...
数组运算符是指针运算符。
array[x]
实际上是*(array + x)
的缩写,它可以分解为:*
和(数组+x)
。很可能*
令您感到困惑。我们可以通过假设x
为0
来进一步消除问题中的加法,因此,array[0]
变为*array< /code> 因为添加
0
不会改变值......因此我们可以看到
*array
相当于array[0]
。您可以在想要使用另一个的地方使用一个,反之亦然。数组运算符是指针运算符。malloc
、realloc
和朋友们并没有发明您一直在使用的指针概念;他们只是使用它来实现一些其他功能,这是一种不同形式的存储持续时间,最适合当您希望大小发生剧烈的动态变化时。遗憾的是,目前接受的答案也违背了StackOverflow 上的其他一些非常有根据的建议,同时,错过了介绍一个鲜为人知的功能的机会,该功能正是针对此用例而闪耀的:灵活的数组成员!这实际上是一个相当糟糕的答案...:(
当您定义
结构
时,请在结构的末尾声明您的数组,而不需要任何例如:这将允许您将
int
数组合并到与count
相同的分配中,并且像这样绑定它们可能非常方便!sizeof (struct int_list)
的作用就好像value
的大小为 0,因此它会告诉您结构的大小对于空列表,您仍然需要添加传递给 realloc 的大小来指定列表的大小。另一个方便的提示是记住 realloc(NULL, x)。 ) 相当于
malloc(x)
,我们可以用它来简化我们的代码,例如:我选择使用
struct int_list **
的原因。因为第一个参数可能看起来并不明显,但如果您考虑第二个参数,那么在push_back
中对value
所做的任何更改对于我们的函数来说都是不可见的是从哪里打来的,对吗?第一个参数也是如此,我们需要能够修改我们的数组
,不仅仅是这里,而且也可能在我们传递的任何其他函数中它到...array
开始时什么都没有指向;这是一个空列表。 初始化与添加它相同。例如:PS 完成后记得
free(array);
!One simple solution involves
mmap
. This is great if you can tolerate a POSIX solution. Just map a whole page and guard against overflows, sincerealloc
would fail for such values anyway. Modern OSes won't commit to the whole lot until you use it, and you can truncate files if you want.Alternatively, there's
realloc
. As with everything that seems scarier at first than it was later, the best way to get over the initial fear is to immerse yourself into the discomfort of the unknown! It is at times like that which we learn the most, after all.Unfortunately, there are limitations. While you're still learning to use a function, you shouldn't assume the role of a teacher, for example. I often read answers from those who seemingly don't know how to use
realloc
(i.e. the currently accepted answer!) telling others how to use it incorrectly, occasionally under the guise that they've omitted error handling, even though this is a common pitfall which needs mention. Here's an answer explaining how to userealloc
correctly. Take note that the answer is storing the return value into a different variable in order to perform error checking.Every time you call a function, and every time you use an array, you are using a pointer. The conversions are occurring implicitly, which if anything should be even scarier, as it's the things we don't see which often cause the most problems. For example, memory leaks...
Array operators are pointer operators.
array[x]
is really a shortcut for*(array + x)
, which can be broken down into:*
and(array + x)
. It's most likely that the*
is what confuses you. We can further eliminate the addition from the problem by assumingx
to be0
, thus,array[0]
becomes*array
because adding0
won't change the value...... and thus we can see that
*array
is equivalent toarray[0]
. You can use one where you want to use the other, and vice versa. Array operators are pointer operators.malloc
,realloc
and friends don't invent the concept of a pointer which you've been using all along; they merely use this to implement some other feature, which is a different form of storage duration, most suitable when you desire drastic, dynamic changes in size.It is a shame that the currently accepted answer also goes against the grain of some other very well-founded advice on StackOverflow, and at the same time, misses an opportunity to introduce a little-known feature which shines for exactly this usecase: flexible array members! That's actually a pretty broken answer... :(
When you define your
struct
, declare your array at the end of the structure, without any upper bound. For example:This will allow you to unite your array of
int
into the same allocation as yourcount
, and having them bound like this can be very handy!sizeof (struct int_list)
will act as thoughvalue
has a size of 0, so it'll tell you the size of the structure with an empty list. You still need to add to the size passed torealloc
to specify the size of your list.Another handy tip is to remember that
realloc(NULL, x)
is equivalent tomalloc(x)
, and we can use this to simplify our code. For example:The reason I chose to use
struct int_list **
as the first argument may not seem immediately obvious, but if you think about the second argument, any changes made tovalue
from withinpush_back
would not be visible to the function we're calling from, right? The same goes for the first argument, and we need to be able to modify ourarray
, not just here but possibly also in any other function/s we pass it to...array
starts off pointing at nothing; it is an empty list. Initialising it is the same as adding to it. For example:P.S. Remember to
free(array);
when you're done with it!我能想到几个选择。
1-99
,就无法执行array[100]
。而且您使用起来可能不太方便。很难说哪种选择最适合您的情况。简单地创建一个大数组当然是最简单的解决方案之一,并且不会给您带来太多问题,除非它真的很大。
There are a couple of options I can think of.
array[100]
without having to walk through1-99
first. And it might not be that handy for you to use either.It is hard to say what option would be best in your situation. Simply creating a large array is ofcourse one of the easiest solutions and shouldn't give you much problems unless it's really large.
基于 Matteo Furlans 设计,当他说“大多数动态数组实现都是从某个(小)默认大小的数组开始工作的,然后每当添加新数组时空间不足时元素,数组大小的两倍”。下面的“正在进行的工作”的不同之处在于它的大小不会加倍,它的目标是仅使用所需的内容。为了简单起见,我还省略了安全检查...同样基于 brimboriums 想法,我尝试向代码添加删除功能...
看起来像这样...
storage.h文件 storage.c 文件看起来像这样...
main.c 看起来像这样...
期待接下来的建设性批评...
Building on Matteo Furlans design, when he said "most dynamic array implementations work by starting off with an array of some (small) default size, then whenever you run out of space when adding a new element, double the size of the array". The difference in the "work in progress" below is that it doesn't double in size, it aims at using only what is required. I have also omitted safety checks for simplicity...Also building on brimboriums idea, I have tried to add a delete function to the code...
The storage.h file looks like this...
The storage.c file looks like this...
The main.c looks like this...
Look forward to the constructive criticism to follow...
当你说
,您基本上是在说您正在使用“指针”,但它是数组范围的本地指针而不是内存范围的指针。既然你在概念上已经在使用“指针”(即引用数组中元素的id号),为什么不只使用常规指针(即引用最大数组中元素的id号:整个内存) )。
您可以让对象存储指针,而不是存储资源 ID 号。基本上是同样的事情,但效率更高,因为我们避免将“数组+索引”变成“指针”。
如果您将指针视为整个内存的数组索引(这就是它们的实际情况),那么指针并不可怕
When you're saying
you're basically saying you're using "pointers", but one which is a array-wide local pointer instead of memory-wide pointer. Since you're conceptually already using "pointers" (i.e. id numbers that refers to an element in an array), why don't you just use regular pointers (i.e. id numbers that refers to an element in the biggest array: the whole memory).
Instead of your objects storing a resource id numbers, you can make them store a pointer instead. Basically the same thing, but much more efficient since we avoid turning "array + index" into a "pointer".
Pointers are not scary if you think of them as array index for the whole memory (which is what they actually are)
创建任何类型的无限项目的数组:
以及如何使用它:
此向量/数组可以容纳任何类型的项目,并且大小完全动态。
To create an array of unlimited items of any sort of type:
and how to use it:
This vector/array can hold any type of item and it is completely dynamic in size.
好吧,我想如果您需要删除一个元素,您将复制一个数组,忽略要排除的元素。
假设
getElement2BRemove()
、copy2TempVector( void* ...)
和fillFromTempVector(...)
是处理临时向量的辅助方法。Well, I guess if you need to remove an element you will make a copy of the array despising the element to be excluded.
Assume that
getElement2BRemove()
,copy2TempVector( void* ...)
andfillFromTempVector(...)
are auxiliary methods to handle the temp vector.这些帖子的顺序显然是错误的!这是系列 3 篇文章中的第 1 篇。对不起。
在尝试使用 Lie Ryan 的代码时,我在检索存储的信息时遇到了问题。向量的元素不是连续存储的,正如您通过“作弊”一点并将指针存储到每个元素的地址(这当然违背了动态数组概念的目的)并检查它们所看到的那样。
通过一些修补,via:
只要您知道每个数组元素的地址,就可以毫无问题地访问每个数组元素,所以我想我会尝试添加一个“下一个”元素并将其用作链接列表。当然还有更好的选择。请指教。
These posts apparently are in the wrong order! This is #1 in a series of 3 posts. Sorry.
In attempting to use Lie Ryan's code, I had problems retrieving stored information. The vector's elements are not stored contiguously,as you can see by "cheating" a bit and storing the pointer to each element's address (which of course defeats the purpose of the dynamic array concept) and examining them.
With a bit of tinkering, via:
It's possible to access each array element without problems, as long as you know its address, so I guess I'll try adding a "next" element and use this as a linked list. Surely there are better options, though. Please advise.
这些帖子的顺序显然是错误的!这是 3 篇文章系列中的第 3 篇文章。对不起。
我对 Lie Ryan 的代码“采取了更多的自由”。诚然,访问单个链接列表非常耗时
由于搜索开销而导致的元素,即沿着列表查找直到找到正确的元素。我现在已经治愈了这个问题
维护一个包含下标 0 到与内存地址配对的地址向量。这有效
因为地址向量是一次性分配的,因此在内存中是连续的。由于不再需要链表,
我已经撕掉了它的相关代码和结构。
这种方法不如简单的静态数组那么有效,但至少您不必“遍历列表”
寻找合适的物品。您现在可以使用下标访问元素。为了实现这一点,我已经
添加代码来处理元素被删除并且“实际”下标不会反映在
指针向量的下标。这对于用户来说可能重要也可能不重要。对我来说,这很重要,所以
我已经将下标的重新编号设置为可选。如果不使用重新编号,程序流程将进入虚拟流程
“missing”元素返回一个错误代码,用户可以选择忽略该错误代码或根据需要采取行动。
从这里开始,我建议用户对“元素”部分进行编码以满足他们的需求并确保其正确运行。如果你的
添加的元素是数组,仔细编写子例程来访问它们,看看如何有额外的数组结构
静态数组不需要这样做。享受!
These posts apparently are in the wrong order! This is #3 in a series of 3 posts. Sorry.
I've "taken a few MORE liberties" with Lie Ryan's code. The linked list admittedly was time-consuming to access individual
elements due to search overhead, i.e. walking down the list until you find the right element. I have now cured this by
maintaining an address vector containing subscripts 0 through whatever paired with memory addresses. This works
because the address vector is allocated all-at-once, thus contiguous in memory. Since the linked-list is no longer required,
I've ripped out its associated code and structure.
This approach is not quite as efficient as a plain-and-simple static array would be, but at least you don't have to "walk the list"
searching for the proper item. You can now access the elements by using a subscript. To enable this, I have had
to add code to handle cases where elements are removed and the "actual" subscripts wouldn't be reflected in the
pointer vector's subscripts. This may or may not be important to users. For me, it IS important, so
I've made re-numbering of subscripts optional. If renumbering is not used, program flow goes to a dummy
"missing" element which returns an error code, which users can choose to ignore or to act on as required.
From here, I'd advise users to code the "elements" portion to fit their needs and make sure that it runs correctly. If your
added elements are arrays, carefully code subroutines to access them, seeing as how there's extra array structure
that wasn't needed with static arrays. Enjoy!
这些帖子的顺序可能有误!这是系列 3 篇文章中的第 2 篇文章。对不起。
我对 Lie Ryan 的代码进行了“一些自由”,实现了一个链表,以便可以通过链表访问他的向量的各个元素。这允许访问,但不可否认的是,由于搜索开销,访问单个元素非常耗时,即沿着列表走下去直到找到正确的元素。我将通过维护一个包含下标 0 到与内存地址配对的地址向量来解决这个问题。这仍然不如简单数组那么有效,但至少您不必“遍历列表”来搜索正确的项目。
These posts may be in the wrong order! This is #2 in a series of 3 posts. Sorry.
I've "taken a few liberties" with Lie Ryan's code, implementing a linked list so individual elements of his vector can be accessed via a linked list. This allows access, but admittedly it is time-consuming to access individual elements due to search overhead, i.e. walking down the list until you find the right element. I'll cure this by maintaining an address vector containing subscripts 0 through whatever paired with memory addresses. This is still not as efficient as a plain-and-simple array would be, but at least you don't have to "walk the list" searching for the proper item.