Re: Can I Trust Pointer Arithmetic In Re-Allocated Memory?




Keith Thompson <kst-u@xxxxxxx> wrote in message
news:lnk65fydta.fsf@xxxxxxxxxxxxxxxxxx
"Bill Reid" <hormelfree@xxxxxxxxxxxxxxxx> writes:
Barry Schwarz <schwarzb@xxxxxxxxx> wrote in message
news:k93od21vgd6n6fhrg6tooem3r5j06ejrq4@xxxxxxxxxx
On Fri, 11 Aug 2006 03:54:19 GMT, "Bill Reid"
<hormelfree@xxxxxxxxxxxxxxxx> wrote:

Bear with me, as I am not a "professional" programmer, but I was
working on part of program that reads parts of four text files into
a buffer which I re-allocate the size as I read each file. I read
some
of the items from the bottom up of the buffer, and some from the
top down, moving the bottom items back to the new re-allocated
bottom on every file read.

I don't quite follow this description.

Yeah, it's a little confusing, and not that relevant to what I'm
asking...the
bottom line is I want to separately sort two parts of a list...

Then when I've read all four files, I sort the top and bottom items
separately using qsort(), which takes a pointer to a list of items,
and
write the two sorted lists to two new files.

Problem is, I worry that if I just supply a pointer to the first item
in the bottom list to qsort(), it might point out to bozo-land during
the sort because I thought that dynamically re-allocated memory
is not necessarily contiguous. So I've done a little two step where

The block of memory whose non-NULL address is returned from
malloc/realloc/calloc is guaranteed to be contiguous.

OK, that's the answer, I was just plain wrong that the memory
might not be contiguous...I've probably only read that guarantee
about 100000000000 times but just forgot it.

I think I got that confused with the idea that the re-allocated
block may have a different location than the original malloc, which
would mean...

One thing that I found a little confusing in your original message is
that you talked about "re-allocated" memory, but you didn't mention
the "realloc" function. The more specific your description, the more
likely it is that we can help.

Well, OK, maybe, here's canonical specificity:

/* now re-allocate memory for the instrument strings */
if((curr_instrs=(instr_strs *)
realloc(curr_instrs,num_instrs*sizeof(instr_strs)))==NULL) {
printf("Not enough memory for instruments buffer\n");
goto CloseFiles;
}

Does that help you help me?

[...]

OK, so this should be completely legal and flawless:

/* sort the symbol list alphabetically */
qsort((void *)curr_instrs,num_symbols,128,sort_alpha_list);

then...

/* sort the no-symbol list alphabetically */
qsort((void
*)curr_instrs+num_symbols,num_no_symbols,128,sort_alpha_list);

Um, no.

By "legal and flawless" I DID mean "100% guaranteed functional",
not "pleasing to thine eyes"...

Don't be afraid of whitespace. I put blanks around most operator
symbols, and after every comma. If I have to split something across
lines, that's ok. So I'd write your qsort call as:

qsort((void *)curr_instrs + num_symbols,
num_no_symbols,
128,
sort_alpha_list);

That's the way YOU'D do it, I do it differently, and since I'm the only
one reading it (except in this one rare instance, or occasionally I'll post
some code somewhere on the net), I can read it just fine, and of
course it compiles all the same...

The third argument, 128, is a "magic number". It's very difficult to
tell what it means or whether it's even correct. Define a constant:
#define WHATEVER 128

In qsort(), it's basically 128 (character) bytes.

I've actually got "128" defined globally (and I do mean globally, for
several hundred thousand lines of code) for the purposes of reading
and writing strings of certain lengths. And those damned defines
have managed to screw me up royally several times, including a
really irritating "intermittent" problem I had when I first wrote this
particular section of code. So lately I've been using them less
and less...

so you only need to change it in one place (but pick a better name, of
course).

Even at file scope right now I'm more comfortable with the way it
is...

The first argument to qsort is:

(void *)curr_instrs + num_symbols

You can't do pointer arithmetic on a void* value. (Some compilers may
allow it; if you're using gcc, try "-ansi -pedantic -Wall -W", or
replace "-ansi" with "-std=c99").

Then how does qsort() do it? I'm assuming now that it must just
use pointer arithmetic internally, because it doesn't seem to want or
recognize my typedef of a 128-character string:

typedef char instr_strs[128];
instr_strs *curr_instrs;

If you're trying to get the address pointed to by curr_instrs plus an
offset of num_symbols bytes, you'll need to to the arithmetic using
char*:

qsort((char*)curr_instrs + num_symbols,
/* other args */);

assuming that curr_instrs isn't already a char*.

Nope, a pointer to the first of many 128-character strings, as above, so
are you saying the pointer cast should be (instr_strs *)? I have no problem
with that, as long as it works, and I must stress again at this point that
the current code:

/* sort the symbol list alphabetically */
qsort((void *)curr_instrs,num_symbols,128,sort_alpha_list);

Has worked flawlessly for months now; it's part of a particular section
of code that downloads about 3/4 meg of raw data from the net every
day at a specific time, parses out about 100,000 data items, and writes
them to a custom database in a matter of seconds.

The only reason I asked the original question was because I went
back and reviewed the code and wondered if I could shave a few
more milliseconds off the execution time...

Note that I didn't
cast the expression to void*; any pointer-to-object type can be
converted to void*, or vice versa.

Yeah, I noticed that, I just use (void *) because that's what
I thought qsort() wanted, and it definitely WORKS that way
(I've used qsort() dozens of times EXACTLY that way without
problems).

Now to get back to this:

If you're trying to get the address pointed to by curr_instrs plus an
offset of num_symbols bytes, you'll need to to the arithmetic using
char*:

qsort((char*)curr_instrs + num_symbols,
/* other args */);

I think I see what you're saying, maybe...and maybe not...

If curr_instrs is pointer to a 128-character string type, wouldn't
curr_instrs+num_symbols then point to a location offset from
curr_instrs by (num_symbols*128 bytes)? And if so, what's
the point of cast (char *) if qsort() already works by sorting
some specified number of sequences of some specified
number of character bytes?

I thought I had the answer to my original question, and then it
slipped away from me...

---
William Ernest Reid



.



Relevant Pages

  • Re: Can I Trust Pointer Arithmetic In Re-Allocated Memory?
    ... a buffer which I re-allocate the size as I read each file. ... of the items from the bottom up of the buffer, ... I worry that if I just supply a pointer to the first item ... The block of memory whose non-NULL address is returned from ...
    (comp.lang.c)
  • Re: Pointer initialization.
    ... >When you pass a pointer to qsort, ... and convert it to one of these 1-megabyte-wide "void *"s. ... and use those to compare the desired "zorg"s. ...
    (comp.lang.c)
  • Re: function called through a non-compatible type
    ... > If I don't remember wrong, according to ANSI-C from 99 a void pointer ... many or even most present-day systems, such conversions can ... the compiler wouldn't complain when you passed it to qsort(). ...
    (comp.lang.c)
  • Re: newbie-A line from K&R that puzzles me
    ... > This is very odd indeed, assuming qsort() and strcmp ... > - qsortis being asked to sort an array of length zero. ... > - It is incorrect to use strcmp() as the fourth argument ... >> with two pointer arguments. ...
    (comp.lang.c)
  • Re: Passing function pointer as argument to a function???
    ... >>> qsort is expecting a pointer to a function which returns an int and ... fcmp has the wrong type for a pointer to be used as an argument to ...
    (comp.lang.c)