Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #9220 > unrolled thread
| Started by | lolzy@live.nl |
|---|---|
| First post | 2011-08-11 10:43 -0700 |
| Last post | 2011-08-14 11:57 +0100 |
| Articles | 20 on this page of 40 — 22 participants |
Back to article view | Back to comp.lang.c
Hash table implementation. lolzy@live.nl - 2011-08-11 10:43 -0700
Re: Hash table implementation. China Blue Tip Wrench <chine.bleu@yahoo.com> - 2011-08-11 11:25 -0700
Re: Hash table implementation. Keith Thompson <kst-u@mib.org> - 2011-08-11 11:49 -0700
Re: Hash table implementation. Ralf Damaschke <rwspam@gmx.de> - 2011-08-11 20:02 +0000
Re: Hash table implementation. Ike Naar <ike@sverige.freeshell.org> - 2011-08-11 22:02 +0000
Re: Hash table implementation. "Scott Fluhrer" <sfluhrer@ix.netcom.com> - 2011-08-11 18:14 -0400
Re: Hash table implementation. lolzy@live.nl - 2011-08-11 23:55 -0700
Re: Hash table implementation. lolzy@live.nl - 2011-08-12 01:38 -0700
Re: Hash table implementation. Nobody <nobody@nowhere.com> - 2011-08-12 09:47 +0100
Re: Hash table implementation. tom st denis <tom@iahu.ca> - 2011-08-12 06:15 -0700
Re: Hash table implementation. James Kuyper <jameskuyper@verizon.net> - 2011-08-12 11:34 -0400
Re: Hash table implementation. Keith Thompson <kst-u@mib.org> - 2011-08-12 08:50 -0700
Re: Hash table implementation. James Kuyper <jameskuyper@verizon.net> - 2011-08-12 12:24 -0400
Re: Hash table implementation. Keith Thompson <kst-u@mib.org> - 2011-08-12 08:38 -0700
Re: Hash table implementation. tom st denis <tom@iahu.ca> - 2011-08-12 10:17 -0700
Re: Hash table implementation. James Kuyper <jameskuyper@verizon.net> - 2011-08-12 13:54 -0400
Re: Hash table implementation. tom st denis <tom@iahu.ca> - 2011-08-12 11:30 -0700
Re: Hash table implementation. Seebs <usenet-nospam@seebs.net> - 2011-08-12 20:39 +0000
Re: Hash table implementation. Keith Thompson <kst-u@mib.org> - 2011-08-12 14:08 -0700
Re: Hash table implementation. gordonb.4sxmx@burditt.org (Gordon Burditt) - 2011-08-12 20:04 -0500
Re: Hash table implementation. Keith Thompson <kst-u@mib.org> - 2011-08-12 18:40 -0700
Re: Hash table implementation. Harald van Dijk <truedfx@gmail.com> - 2011-08-13 12:59 -0700
Re: Hash table implementation. Seebs <usenet-nospam@seebs.net> - 2011-08-13 02:34 +0000
Re: Hash table implementation. Ike Naar <ike@iceland.freeshell.org> - 2011-08-12 11:13 +0000
Re: Hash table implementation. Eric Sosman <esosman@ieee-dot-org.invalid> - 2011-08-12 07:24 -0400
Re: Hash table implementation. Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2011-08-12 09:27 -0600
Re: Hash table implementation. Seebs <usenet-nospam@seebs.net> - 2011-08-12 18:37 +0000
Re: Hash table implementation. Jens Gustedt <Jens.Gustedt@loria.fr> - 2011-08-13 00:08 +0200
Re: Hash table implementation. Keith Thompson <kst-u@mib.org> - 2011-08-12 15:26 -0700
Re: Hash table implementation. Jens Gustedt <Jens.Gustedt@loria.fr> - 2011-08-13 15:11 +0200
Re: Hash table implementation. Keith Thompson <kst-u@mib.org> - 2011-08-13 11:38 -0700
Re: Hash table implementation. Jens Gustedt <Jens.Gustedt@loria.fr> - 2011-08-13 21:03 +0200
Re: Hash table implementation. "Herbert Rosenau" <os2guy@pc-rosenau.de> - 2011-08-19 08:17 +0000
Re: Hash table implementation. Jens Gustedt <jens.gustedt@loria.fr> - 2011-08-19 13:29 +0200
Re: Hash table implementation. Phil Carmody <thefatphil_demunged@yahoo.co.uk> - 2011-08-19 14:35 +0300
Re: Hash table implementation. Michael Angelo Ravera <maravera@prodigy.net> - 2011-08-12 02:54 -0700
Re: Hash table implementation. Rui Maciel <rui.maciel@gmail.com> - 2011-08-13 23:11 +0100
Re: Hash table implementation. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2011-08-14 00:56 +0100
Re: Hash table implementation. lolzy@live.nl - 2011-08-14 00:11 -0700
Re: Hash table implementation. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2011-08-14 11:57 +0100
Page 2 of 2 — ← Prev page 1 [2]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2011-08-12 18:40 -0700 |
| Message-ID | <lnliuy5brv.fsf@nuthaus.mib.org> |
| In reply to | #9316 |
gordonb.4sxmx@burditt.org (Gordon Burditt) writes:
> IEEE floating point numbers have at least two different representations
> for zero and negative zero. They can't *BOTH* be represented by
> all-bits-zero. That's a very large chunk of implementations that
> have at least one representation for zero that isn't all-bits-zero.
Your fear of properly attributing quoted text has led you to snip
the entire context for this. Thank you for respecting my wishes by
not quoting my text without attribution, though. (Would changing
your attribution lines to "Someone posting as So-and-so appears to
have written:" work for you?)
Anyway...
The fact that there can be more than one representation for zero
isn't a problem for calloc(), as long as all-bits-zero is *a*
representation for zero.
Integer types can have multiple representations for 0, either because
they use either 1s'-complement or sign-and-magnitude, or because they
have padding bits. The standard merely requires that all-bits-zero
must be one of the representations (more commonly it's the only
representation for zero). This requirement was added in one of the
post-C99 Technical Corrigenda. (Presumably the committee was willing
to do this because every implementation already satisified it.)
That's enough to ensure that
int *p = calloc(N, sizeof *p);
will set all N elements of the allocated array to 0 (if it doesn't
return NULL, of course).
If there were similar guarantees for floating-point and pointer
types, then calloc() could be used to initialize any object to zero
in a manner similar to a "{ 0 }" initializer.
I'm not entirely convinced that adding such guarantees to the
language would be a bad idea.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Harald van Dijk <truedfx@gmail.com> |
|---|---|
| Date | 2011-08-13 12:59 -0700 |
| Message-ID | <37fbdba3-1dc7-4b18-a34a-bc69774b4ede@h14g2000yqd.googlegroups.com> |
| In reply to | #9318 |
On Aug 13, 3:40 am, Keith Thompson <ks...@mib.org> wrote:
[ Regarding all bits zero representing a value of 0 ]
> If there were similar guarantees for floating-point and pointer
> types, then calloc() could be used to initialize any object to zero
> in a manner similar to a "{ 0 }" initializer.
>
> I'm not entirely convinced that adding such guarantees to the
> language would be a bad idea.
The difference with integer types is that real implementations where
all bits zero does not represent 0.0 or NULL do exist, even if they
are uncommon. For integer types, no implementation was known to exist
where all bits zero was anything else, so it was no problem to require
this for all future implementations as well. If C1x were to disallow
this for other types, all it would do is make sure that such systems
would never get conforming C1x implementations. There is effectively
no difference for programmers between that (C makes guarantees, but
some not-exactly-C implementations are made unable to conform to the
standard) and the current situation (C doesn't make those guarantees,
but almost all implementations do): code which relies on all bits zero
being a representation of 0.0 or a null pointer works on exactly the
same set of systems.
That said, I do wonder if conforming C99 compilers exist for any of
the "problematic" systems. If all current and foreseeable C99
implementations decide that all bits zero is a valid representation of
0.0 or NULL, then it might be sensible for C1x to just go ahead and
require it.
[toc] | [prev] | [next] | [standalone]
| From | Seebs <usenet-nospam@seebs.net> |
|---|---|
| Date | 2011-08-13 02:34 +0000 |
| Message-ID | <slrnj4bpbb.28b0.usenet-nospam@guild.seebs.net> |
| In reply to | #9316 |
On 2011-08-13, Gordon Burditt <gordonb.4sxmx@burditt.org> wrote: > IEEE floating point numbers have at least two different representations > for zero and negative zero. They can't *BOTH* be represented by > all-bits-zero. That's a very large chunk of implementations that > have at least one representation for zero that isn't all-bits-zero. That's distinct from "on which all-bits-zero doesn't represent zero", though, which is what calloc users usually care about. -s -- Copyright 2011, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated! I am not speaking for my employer, although they do rent some of my opinions.
[toc] | [prev] | [next] | [standalone]
| From | Ike Naar <ike@iceland.freeshell.org> |
|---|---|
| Date | 2011-08-12 11:13 +0000 |
| Message-ID | <slrn3vfsj4a2mf.qoi.ike@iceland.freeshell.org> |
| In reply to | #9247 |
On 2011-08-12, lolzy@live.nl <lolzy@live.nl> wrote: > * Why do you prefer malloc() over calloc() ? malloc allocates memory. calloc allocates memory and initializes it to all-bytes-zero. In your case, initializing the memory to all-bytes-zero is wasted effort since you initialize the memory yourself. > * Why not cast for C++ compatibility? You'll have to make up your mind if you want to write the program in C or in C++. They are different languages, and if you try to write code that is both C and C++ you may end up with something that is neither decent C nor decent C++. For instance, in a C++ program you would probably prefer std::vector over a malloc-ed array. > * Why shouldn't I use the register keyword for loop variables? Suppose the compiler honours the keyword, and reserves a register for the loop variable. That register now cannot be used for more useful purposes (e.g. as temporary storage for computing subexpressions) and you may end up with less efficient code.
[toc] | [prev] | [next] | [standalone]
| From | Eric Sosman <esosman@ieee-dot-org.invalid> |
|---|---|
| Date | 2011-08-12 07:24 -0400 |
| Message-ID | <j232j4$mmv$1@dont-email.me> |
| In reply to | #9247 |
On 8/12/2011 2:55 AM, lolzy@live.nl wrote:
> Thanks for all your comments. I still have some questions :) :
>
> * Why do you prefer malloc() over calloc() ?
This was explained elsethread.
> * Why not cast for C++ compatibility?
Why not use `#define STOP ;' and write STOP instead of ; at
the end of each statement, so you can change it to `#define STOP .'
for easier conversion to COBOL?
> * Why shouldn't I use the register keyword for loop variables?
Because the compilers know the trade-offs for their particular
machines better than you know them for all machines.
--
Eric Sosman
esosman@ieee-dot-org.invalid
[toc] | [prev] | [next] | [standalone]
| From | Joe Pfeiffer <pfeiffer@cs.nmsu.edu> |
|---|---|
| Date | 2011-08-12 09:27 -0600 |
| Message-ID | <1bd3ga3b1k.fsf@snowball.wb.pfeifferfamily.net> |
| In reply to | #9247 |
lolzy@live.nl writes: > Thanks for all your comments. I still have some questions :) : > > * Why do you prefer malloc() over calloc() ? Slightly faster if you're going to initialize the space to anything but 0. > * Why not cast for C++ compatibility? Because it isn't C++ > * Why shouldn't I use the register keyword for loop variables? There's no real reason not to, but every compiler I'm familiar with ignores it so there's no real point.
[toc] | [prev] | [next] | [standalone]
| From | Seebs <usenet-nospam@seebs.net> |
|---|---|
| Date | 2011-08-12 18:37 +0000 |
| Message-ID | <slrnj4aq0i.1i00.usenet-nospam@guild.seebs.net> |
| In reply to | #9247 |
On 2011-08-12, lolzy@live.nl <lolzy@live.nl> wrote: > Thanks for all your comments. I still have some questions :) : > * Why do you prefer malloc() over calloc() ? Because calloc() doesn't actually do anything more useful. > * Why not cast for C++ compatibility? Because trying to write code in two languages at once is stupid. If you want to call C code from C++, compile the C code as C, and call it from C++. C++ supports this. > * Why shouldn't I use the register keyword for loop variables? Because there's no reason to, and adding things that aren't useful makes it harder to read the program. -s -- Copyright 2011, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated! I am not speaking for my employer, although they do rent some of my opinions.
[toc] | [prev] | [next] | [standalone]
| From | Jens Gustedt <Jens.Gustedt@loria.fr> |
|---|---|
| Date | 2011-08-13 00:08 +0200 |
| Message-ID | <4E45A45F.1070202@loria.fr> |
| In reply to | #9247 |
Am 08/12/2011 08:55 AM, schrieb lolzy@live.nl: > * Why shouldn't I use the register keyword for loop variables? The register keyword is just a misnomer and has not much to do with the registers of your CPU. Its only purpose nowadays is that you are not allowed to take the address of such a variable such that it *may* be implemented more efficiently (e.g a CPU register or an immediate). So it is a request by you that you want to be warned if you ever use the address because you think that the implementation of that variable is critical. There is nothing bad in using "register" but most of the time it just doesn't change much. A good compiler finds these things out quite well by its own. But probably the same holds for const-qualifying variables or restrict-qualifying pointers. So why the "register" keyword is particularly frowned upon by many C programmers? Perhaps just because of its misnaming. Jens
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2011-08-12 15:26 -0700 |
| Message-ID | <lny5yy5ksb.fsf@nuthaus.mib.org> |
| In reply to | #9300 |
Jens Gustedt <Jens.Gustedt@loria.fr> writes:
> Am 08/12/2011 08:55 AM, schrieb lolzy@live.nl:
>> * Why shouldn't I use the register keyword for loop variables?
>
> The register keyword is just a misnomer and has not much to do with the
> registers of your CPU. Its only purpose nowadays is that you are not
> allowed to take the address of such a variable such that it *may* be
> implemented more efficiently (e.g a CPU register or an immediate).
>
> So it is a request by you that you want to be warned if you ever use the
> address because you think that the implementation of that variable is
> critical. There is nothing bad in using "register" but most of the time
> it just doesn't change much. A good compiler finds these things out
> quite well by its own.
>
> But probably the same holds for const-qualifying variables or
> restrict-qualifying pointers. So why the "register" keyword is
> particularly frowned upon by many C programmers? Perhaps just because of
> its misnaming.
The intent is that the register keyword should result in faster access
to an object. The fact that you can't take its address is a consequence
of that (because, particularly in early compilers, it was typically
implemented by storing the object in a CPU register, which has no
address).
C99 6.7.1p4:
A declaration of an identifier for an object with storage-class
specifier register suggests that access to the object be as
fast as possible. The extent to which such suggestions are
effective is implementation-defined
(The restriction on taking such an object's address is stated
elsewhere.)
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Jens Gustedt <Jens.Gustedt@loria.fr> |
|---|---|
| Date | 2011-08-13 15:11 +0200 |
| Message-ID | <4E4677E5.5010409@loria.fr> |
| In reply to | #9302 |
Hello Keith, Am 08/13/2011 12:26 AM, schrieb Keith Thompson: > Jens Gustedt <Jens.Gustedt@loria.fr> writes: >> ... such that it *may* be >> implemented more efficiently (e.g a CPU register or an immediate). > A declaration of an identifier for an object with storage-class > specifier register suggests that access to the object be as > fast as possible. The extent to which such suggestions are > effective is implementation-defined I don't really see what made you repeat what I think I said already. Do you think I explained badly? Sorry for that. Jens
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2011-08-13 11:38 -0700 |
| Message-ID | <ln4o1l5f87.fsf@nuthaus.mib.org> |
| In reply to | #9341 |
Jens Gustedt <Jens.Gustedt@loria.fr> writes:
> Am 08/13/2011 12:26 AM, schrieb Keith Thompson:
>> Jens Gustedt <Jens.Gustedt@loria.fr> writes:
>
>>> ... such that it *may* be
>>> implemented more efficiently (e.g a CPU register or an immediate).
>
>> A declaration of an identifier for an object with storage-class
>> specifier register suggests that access to the object be as
>> fast as possible. The extent to which such suggestions are
>> effective is implementation-defined
>
> I don't really see what made you repeat what I think I said already. Do
> you think I explained badly? Sorry for that.
Your explanation implies that the *main point* of ``register''
is to prevent taking the affected object's address. I disagreed.
It's more a matter of emphasis than semantics. It's true that the
only real semantic effect of ``register'' is to disable unary "&",
but that's really a side effect of the original purpose.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Jens Gustedt <Jens.Gustedt@loria.fr> |
|---|---|
| Date | 2011-08-13 21:03 +0200 |
| Message-ID | <4E46CA8A.3060407@loria.fr> |
| In reply to | #9353 |
Am 08/13/2011 08:38 PM, schrieb Keith Thompson: > Your explanation implies that the *main point* of ``register'' > is to prevent taking the affected object's address. I disagreed. > It's more a matter of emphasis than semantics. Ok. Yes, for me the main point of a keyword is its impact on what are conforming programs. > It's true that the only real semantic effect of ``register'' is to > disable unary "&", but that's really a side effect of the original > purpose. Ok, thanks for clarifying your POV. Jens
[toc] | [prev] | [next] | [standalone]
| From | "Herbert Rosenau" <os2guy@pc-rosenau.de> |
|---|---|
| Date | 2011-08-19 08:17 +0000 |
| Message-ID | <wmzsGguTDN6N-pn2-4ddndJcdm3UH@JUPITER.PC-ROSENAU.DE> |
| In reply to | #9300 |
On Fri, 12 Aug 2011 22:08:31 UTC, Jens Gustedt <Jens.Gustedt@loria.fr> wrote: > Am 08/12/2011 08:55 AM, schrieb lolzy@live.nl: > > * Why shouldn't I use the register keyword for loop variables? > > The register keyword is just a misnomer and has not much to do with the > registers of your CPU. Its only purpose nowadays is that you are not > allowed to take the address of such a variable such that it *may* be > implemented more efficiently (e.g a CPU register or an immediate). No. register is a relict from K&R1. At that time it was the istruction to the compiler to place the variable into a CPU-register if one is available. But since then there are more than 20 years gone. More moder compilers are more intelligent than the mayority of programmers using them. They can better decide which one should be in a register. So it will ignore the keyword. -- Tschau/Bye Herbert Visit http://www.ecomstation.de the home of german eComStation eComStation 2.0 ist da!
[toc] | [prev] | [next] | [standalone]
| From | Jens Gustedt <jens.gustedt@loria.fr> |
|---|---|
| Date | 2011-08-19 13:29 +0200 |
| Message-ID | <4E4E4915.2030403@loria.fr> |
| In reply to | #9545 |
Am 08/19/2011 10:17 AM, schrieb Herbert Rosenau: > On Fri, 12 Aug 2011 22:08:31 UTC, Jens Gustedt <Jens.Gustedt@loria.fr> > wrote: > >> Am 08/12/2011 08:55 AM, schrieb lolzy@live.nl: >>> * Why shouldn't I use the register keyword for loop variables? >> >> The register keyword is just a misnomer and has not much to do with the >> registers of your CPU. Its only purpose nowadays is that you are not >> allowed to take the address of such a variable such that it *may* be >> implemented more efficiently (e.g a CPU register or an immediate). > > No. How is that, "no"? Do you question the fact that one is not allowed to take the address of a variable with register storage class? > register is a relict from K&R1. At that time it was the istruction > to the compiler to place the variable into a CPU-register if one is > available. The naming as "register" is a relict, yes. > But since then there are more than 20 years gone. More moder compilers > are more intelligent than the mayority of programmers using them. They > can better decide which one should be in a register. So it will ignore > the keyword. With that opinion, you are not at all in allignement with the current C standard: C99> The intended use of the restrict qualifier (like the register storage C99> class) is to promote optimization, ... and AFAIR the new proposal C1x stays with this. As a the restrict keyword, register is a constract between the programmer and the compiler that helps to promote certain types of optimizations. Not more and not less. Jens
[toc] | [prev] | [next] | [standalone]
| From | Phil Carmody <thefatphil_demunged@yahoo.co.uk> |
|---|---|
| Date | 2011-08-19 14:35 +0300 |
| Message-ID | <871uwhhbwg.fsf@bazspaz.fatphil.org> |
| In reply to | #9545 |
"Herbert Rosenau" <os2guy@pc-rosenau.de> writes: > On Fri, 12 Aug 2011 22:08:31 UTC, Jens Gustedt <Jens.Gustedt@loria.fr> > wrote: > > > Am 08/12/2011 08:55 AM, schrieb lolzy@live.nl: > > > * Why shouldn't I use the register keyword for loop variables? > > > > The register keyword is just a misnomer and has not much to do with the > > registers of your CPU. Its only purpose nowadays is that you are not > > allowed to take the address of such a variable such that it *may* be > > implemented more efficiently (e.g a CPU register or an immediate). > > No. register is a relict from K&R1. At that time it was the istruction > to the compiler to place the variable into a CPU-register if one is > available. > > But since then there are more than 20 years gone. More moder compilers > are more intelligent than the mayority of programmers using them. They > can better decide which one should be in a register. So it will ignore > the keyword. I'm not a FORTRAN programmer, but the first thing that comes to mind is that the inability to take the address immediately implies that a variable cannot be aliased, which does permit more aggressive provably-correct optimisations. Not that I'd ever use 'register'. I frequently examine the code generated for my inner loops, and as I write in a fairly SSA-like way (I've spent too long looking at DJB's code!), I've never noticed any obvious inefficiencies that 'register' could possibly help with. (I've noticed apple's gcc absolutely refuse to use one of the FP registers, but that's a bug, and yes, I did try 'register' to fix that, but it failed.) Phil -- Humans will have advanced a long, long, way when religious belief has a cozy little classification in the DSM. -- David Melville (on r.a.s.f1)
[toc] | [prev] | [next] | [standalone]
| From | Michael Angelo Ravera <maravera@prodigy.net> |
|---|---|
| Date | 2011-08-12 02:54 -0700 |
| Message-ID | <52ee6f60-3059-4e2f-b68c-8ed5ba434d13@glegroupsg2000goo.googlegroups.com> |
| In reply to | #9220 |
One thing that you may want to do is to use the size as more of a suggestion than a firm number. Many hash functions work better when the main size is a prime number. You can pick a prime number that is close to the size requested or pick the first prime that is not less than the size requested. Primacy is not really a requirement when the size of the table gets really large. It would be exceedingly reasonable to keep a list of primes less than 256 and require that the main size have no factors less than 256. This would allow you easily to pick a prime number less 64K and give you a "size that isn't completely stupid" for larger tables. For sizes less than 256, you just scan the prime table upward to find the first prime. If you don't always use a realy cool hash function, then I agree that you might like to use the "object inspired" approach of storing a function pointer to a hash function.
[toc] | [prev] | [next] | [standalone]
| From | Rui Maciel <rui.maciel@gmail.com> |
|---|---|
| Date | 2011-08-13 23:11 +0100 |
| Message-ID | <4e46f9b9$0$31415$a729d347@news.telepac.pt> |
| In reply to | #9220 |
lolzy@live.nl wrote:
> Hello comp.lang.c,
>
> This is a hash table implementation I wrote, please comment. Thanks in
> advance.
Hi. Here are my 0.02€.
To start off, your code doesn't have many comments. Descriptive comments
are always nice to have, and help make the code easier to maintain.
> # START CODE
>
> /*
> * Hash table demo.
> * (C) Jori Koolstra - 19:40 11-8-2011
> */
>
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
>
>
> #define HASH_TABLE_SIZE
100
>
>
> #define HASH_TABLE_DELETE_ELEMENT_CHECK_FOR_ERRORS(t, n)
if
> (hash_table_delete_element(t, n) == 0) hash_table_fatal_error(0, n)
>
> #define HASH_TABLE_GET_STRING_FROM_TABLE_CHECK_FOR_ERRORS(t,
n, z) if
> ((z = hash_table_get_string_from_table(t, n)) == NULL)
> hash_table_fatal_error(1, n)
>
>
> struct table_data
> {
> char *uid;
> char *d;
Your variable names could be a bit more descriptive.
> };
>
>
> typedef struct table_data table_data;
>
>
>
> struct hash_table
> {
> table_data data;
> struct hash_table *x;
> };
>
>
> typedef struct hash_table hash_table;
>
>
>
> hash_table ** hash_table_create_table(int);
> hash_table * hash_table_get_string_from_table(hash_table **, char *);
> int hash_table_hash(char *);
> void hash_table_add_element(hash_table **, char *, char *);
> int hash_table_delete_element(hash_table **, char *);
> void hash_table_fatal_error(int, char *);
>
>
>
> int main(void)
> {
> hash_table **table;
> hash_table *y;
>
> table = hash_table_create_table(HASH_TABLE_SIZE);
>
> hash_table_add_element(table, "John", "Hello John!");
> hash_table_add_element(table, "Kim", "Hello Kim!");
> hash_table_add_element(table, "Jori", "Hello Jori!");
> hash_table_add_element(table, "Jack", "Hello Jack!");
>
> HASH_TABLE_DELETE_ELEMENT_CHECK_FOR_ERRORS(table, "Kim");
>
> HASH_TABLE_GET_STRING_FROM_TABLE_CHECK_FOR_ERRORS(table, "Jack", y);
>
> printf("%s\n", y->data.d);
>
>
>
>
> return 0;
> }
>
>
>
> /*
> * Print error and quit.
> */
A couple of things with the way you handled errors. First of all, it would
be nice that your error codes also supported the case where no error
occurred. This "everyting went ok" error code tends to be attributed to the
value 0, possibly due to the convenient way the if statement evaluates
expressions.
This makes it possible to write code such as:
<code>
int error(void)
{
(...)
if(no_error)
return 0
(...)
}
int main(void)
{
if(error)
{
// error is unequal to zero, hence this statement is executed
}
(...)
return 0;
}
> void hash_table_fatal_error(int t, char *el)
> {
> switch (t)
> {
> case 0:
> printf("ERROR: Could not delete element '%s' from hash table.\n",
> el);
> break;
> case 1:
> printf("ERROR: Could not find key '%s' in hash table.\n", el);
> break;
> }
Building from the previous suggestion, it would be better if, instead of
relying on magical, undocumented numbers, you defined your error messages
through an enum, attributing to each enumerator a descriptive name. For
example:
<code>
enum hash_error
{
ERR_OK = 0,
ERR_CANNOT_DELETE_ELEMENT,
ERR_CANNOT_FIND_KEY,
ERR_UNKNOWN // in case of an unexpected error
};
void hash_table_fatal_error(int t, char *el)
{
switch (t)
{
case ERR_OK:
break;
case ERR_CANNOT_DELETE_ELEMENT:
printf("ERROR: Could not delete element '%s' from hash table.
\n",el);
break;
case ERR_CANNOT_FIND_KEY:
printf("ERROR: Could not find key '%s' in hash table.\n", el);
break;
default:
printf("ERROR: unknown error\n");
return ERR_UNKNOWN;
break;
}
}
>
>
> exit(0);
> }
>
>
>
> /*
> * Create a hash table with size 'size' and return a hash table array.
> */
> hash_table ** hash_table_create_table(int size)
> {
what about if (size < 0) ? Probably it would be a good idea to throw an
assert().
> register int i = 0;
> hash_table **x;
>
>
> x = (hash_table **) calloc(size, sizeof(hash_table *));
It would be a good idea to test if calloc returned a null pointer, and
recover safely it it does.
> for (; i <= size; ++i)
> {
> x[i] = (hash_table *) calloc(1, sizeof(hash_table));
> }
>
> return x;
> }
>
>
>
> /*
> * Returns a specified hash table element from table 't' with key 's',
> * or NULL on failure.
> */
> hash_table * hash_table_get_string_from_table(hash_table **t, char *s)
> {
What if (t == NULL) ? And what if (*t == NULL) ? And what if (s == NULL)?
> int id = hash_table_hash(s);
> hash_table *q;
>
> if (t[id]->data.uid != NULL && strcmp(t[id]->data.uid, s) != 0)
> {
> if (t[id]->x != NULL)
> {
> /* Move trough linked list */
This is a bit nit-picking, but it would be nice if, instead of being forced
to run strcmp in each entry, you implemented a binary search tree for your
search terms, which would be built/updated with each call to
hash_table_add_element(). If that isn't possible then another alternative
would be to build a temporary list with all labels, test the 1st character
of each label with the 1st character of s and if it didn't matched then you
removed that entry from your list. Then, you reiterated for all subsequent
characters of the remaining entries until you were left with a complete
match. You would probably get considerable efficiency improvements with
this.
> for (q = t[id]->x; ; q = q->x)
> {
> if (strcmp(q->data.uid, s) == 0)
> {
> /* Found */
> return q;
> }
>
>
> if (q->x == NULL)
> {
> break;
> }
> }
> }
>
> return NULL;
> }
>
> return (t[id]->data.uid == NULL ? NULL : t[id]);
> }
>
>
>
> /*
> * Add 'data' to table 't' with key 'uid'.
> */
> void hash_table_add_element(hash_table **t, char *uid, char *data)
> {
Again, no sanity test is performed on the parameters. This is a nasty bug,
or even security problem, just waiting to happen.
> int hash = hash_table_hash(uid);
> hash_table *q = (hash_table *) calloc(1, sizeof(hash_table));
You need to test if calloc returned a null pointer.
> hash_table *c;
>
> q->data.uid = strdup(uid);
> q->data.d = strdup(data);
> q->x = NULL;
>
>
> c = t[hash];
>
>
> if (c->data.uid != NULL)
> {
> while (c->x != NULL)
> {
> c = c->x;
> }
>
> c->x = q;
You've just added a new entry to your hash table without checking if it was
already present.
> }
> else
> {
> t[hash]->data.uid = strdup(uid);
> t[hash]->data.d = strdup(data);
> }
> }
<snip/>
Hope this helps,
Rui Maciel
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2011-08-14 00:56 +0100 |
| Message-ID | <0.ef68068f67286c26b79e.20110814005609BST.87pqk8g92e.fsf@bsb.me.uk> |
| In reply to | #9372 |
Rui Maciel <rui.maciel@gmail.com> writes:
> lolzy@live.nl wrote:
<snip>
>> /*
>> * Returns a specified hash table element from table 't' with key 's',
>> * or NULL on failure.
>> */
>> hash_table * hash_table_get_string_from_table(hash_table **t, char *s)
>> {
<snip>
>> int id = hash_table_hash(s);
>> hash_table *q;
>>
>> if (t[id]->data.uid != NULL && strcmp(t[id]->data.uid, s) != 0)
>> {
>> if (t[id]->x != NULL)
>> {
>> /* Move trough linked list */
>
> This is a bit nit-picking, but it would be nice if, instead of being forced
> to run strcmp in each entry, you implemented a binary search tree for your
> search terms, which would be built/updated with each call to
> hash_table_add_element(). If that isn't possible then another alternative
> would be to build a temporary list with all labels, test the 1st character
> of each label with the 1st character of s and if it didn't matched then you
> removed that entry from your list. Then, you reiterated for all subsequent
> characters of the remaining entries until you were left with a complete
> match. You would probably get considerable efficiency improvements with
> this.
Are these suggestions generally applicable to hash tables, or are you
talking about this specific case where the OP's table is fixed size?
<snip>
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | lolzy@live.nl |
|---|---|
| Date | 2011-08-14 00:11 -0700 |
| Message-ID | <32e19d99-cc47-436c-977e-360f5fec3ce7@o11g2000yql.googlegroups.com> |
| In reply to | #9375 |
Thanks Rui for your comments. But most suggestions you posted were already changed by me. Doesn't strcmp() stop comparing when its clear what to return? Example: 2 string: "qqqqaaaa" "qqqqqqqq" Does strcmp() keeps comparing until '\0' or does it stop (in this case) at character 5? Thanks in advance!
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2011-08-14 11:57 +0100 |
| Message-ID | <0.32b847c73782b90bed92.20110814115756BST.87k4agfeff.fsf@bsb.me.uk> |
| In reply to | #9390 |
lolzy@live.nl writes: <snip> > Doesn't strcmp() stop comparing when its clear what to return? > Example: > > 2 string: > > "qqqqaaaa" > "qqqqqqqq" > > Does strcmp() keeps comparing until '\0' or does it stop (in this > case) at character 5? It can do what it likes or, to be less anthropomorphic, the author can chose to do what he or she likes. For example, it's possible that the strings can be compared in larger units than single characters. Since it is a frequently used function, implementations should take care to make it efficient. -- Ben.
[toc] | [prev] | [standalone]
Page 2 of 2 — ← Prev page 1 [2]
Back to top | Article view | comp.lang.c
csiph-web