Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

writing library functions and pointers?

2 views
Skip to first unread message

Martin

unread,
Jul 3, 2009, 5:15:49 AM7/3/09
to
Hi,

I have been trying to construct my own library of commonly used C
functions and have got a bit stuck and wonder if anyone could give me
any pointers?

I have set up a generic read function...

void read_file_float(FILE **fp, float **array, int size, char **argv)
{
if (fread(*array, sizeof (float), size, *fp) != size) {
fprintf(stderr, "Error reading in file: %s\n", *argv);
exit(1);
}
return;
}

which I call using the following in my code for example

FILE *fp;
float *array (which I allocate);
int numrows = something;
int numcols = something

read_file_float(&fp, &array, numrows * numcols, argv);

This works fine.

The problem comes when I do something similar but instead use fseek -
so in this instance i only want to read part of the file into the
array e.g. array[counter]

Now my call to the function would be

read_file_float(&fp, &array[some_counter]), 1, argv);

But I don't know how to declare this correctly.

Any thoughts?
Thanks in advance

Martin

unread,
Jul 3, 2009, 6:25:31 AM7/3/09
to
I think I have solved it.

function needs to be declared differently

void read_seg_float(FILE **fp, float *array, int size, char **argv)
{
if (fread(array, sizeof (float), size, *fp) != size) {


fprintf(stderr, "Error reading in file: %s\n", *argv);
exit(1);
}
return;
}

and then accessed

read_seg_float(&fp, &array[some_counter], size, argv);

Nobody

unread,
Jul 3, 2009, 7:22:52 AM7/3/09
to
On Fri, 03 Jul 2009 02:15:49 -0700, Martin wrote:

> I have been trying to construct my own library of commonly used C
> functions and have got a bit stuck and wonder if anyone could give me
> any pointers?
>
> I have set up a generic read function...
>
> void read_file_float(FILE **fp, float **array, int size, char **argv)

Why are you passing a FILE** rather than just a FILE*? And a float**
rather than just a float*?


Martin

unread,
Jul 3, 2009, 8:08:34 AM7/3/09
to

because I understood the way adjust passed copy of pointer was to use
a pointer to a pointer. I got that from the FAQ, perhaps I
misunderstood?

http://c-faq.com/ptrs/passptrinit.html

Stephen Sprunk

unread,
Jul 3, 2009, 10:15:37 AM7/3/09
to

You're not changing the caller's pointer, only the object that the
pointer is pointing to.

S

--
Stephen Sprunk "Stupid people surround themselves with smart
CCIE #3723 people. Smart people surround themselves with
K5SSS smart people who disagree with them." --Isaac Jaffe

Barry Schwarz

unread,
Jul 3, 2009, 1:05:47 PM7/3/09
to
On Fri, 3 Jul 2009 03:25:31 -0700 (PDT), Martin <mdek...@gmail.com>
wrote:

If you had compiled this with your calling function, you would see
that it is not correct. In the calling function, array is declared as
array of float*. Therefore, array[some_counter] is a particular
float*. Therefore, &array[some_counter] has type float**. Since the
second parameter in the definition of read_seg_float is a float*, this
is a constraint violation requiring a diagnostic. The compiler cannot
generate correct code for this even if it converts the value
&array[some_counter] to type float*.

--
Remove del for email

Barry Schwarz

unread,
Jul 3, 2009, 1:05:47 PM7/3/09
to
On Fri, 3 Jul 2009 02:15:49 -0700 (PDT), Martin <mdek...@gmail.com>
wrote:

>Hi,


>
>I have been trying to construct my own library of commonly used C
>functions and have got a bit stuck and wonder if anyone could give me
>any pointers?
>
>I have set up a generic read function...
>
>void read_file_float(FILE **fp, float **array, int size, char **argv)
>{
> if (fread(*array, sizeof (float), size, *fp) != size) {
> fprintf(stderr, "Error reading in file: %s\n", *argv);
> exit(1);
> }
> return;
>}
>
>which I call using the following in my code for example
>
>FILE *fp;
>float *array (which I allocate);
>int numrows = something;
>int numcols = something
>
>read_file_float(&fp, &array, numrows * numcols, argv);

The unnecessary & applied to fp is discussed elsethread.

array is an object. A such, it is located in memory and has an
address. It also contains a value. Since it is a pointer object,
that value happens to be the address of an area of memory suitable for
holding a number of floats. &array evaluates to the address of the
object.

In your function, you immediately dereference this value. The result
of this operation is the contents of the pointer object which is the
location where you want to store data. Note that nowhere in your
function do you attempt to modify the contents of the pointer object
itself. Consequently, there is no need to pass the address of the
pointer to the function. Thus, the & applied to array is just as
unnecessary as the one applied to fp.

To answer your response in a subsequent message, yes you have
misunderstood slightly. Every argument passed to a function is passed
by value (as if a copy of the argument is passed to the function).
This is true whether the object is a number (integer or floating) or
an address. As a result of this, when the argument is a scalar
object, the function is incapable of modifying the value of the object
***in the calling function***. It is capable of modifying the local
copy that was passed but this is not the same as the original object.

Therefore, when the function needs to modify the object in the calling
function, one common technique is to pass the address of the object
(using the & operator). The function can then dereference this
address to get access to the object in the calling function.

However, this does not apply to your function because it us not
attempting to modify any of the arguments it is passed. fread never
updates the FILE* you pass it; it only updates the FILE the pointer
points to. Your code does not attempt to modify either copy of array,
only the memory that array in the calling function points to. While
the code you have will work, because you added the necessary
dereference operators, it is unnecessary. You can remove the &'s from
the calling statement and remove one * from every use of fp and array.

>
>This works fine.
>
>The problem comes when I do something similar but instead use fseek -
>so in this instance i only want to read part of the file into the
>array e.g. array[counter]
>
>Now my call to the function would be
>
>read_file_float(&fp, &array[some_counter]), 1, argv);
>
>But I don't know how to declare this correctly.

If you fix your function to expect a simple float* as described above,
this will magically be correct. If for some reason you have not
disclosed to us you really need the extra level of indirection, then
one method for reading a single value would be
float *x;
x = &array[some_counter];
read_file_float(&fp, &x, 1, argv);

Martin

unread,
Jul 3, 2009, 6:41:33 PM7/3/09
to
Hi thanks for all of the replies...

yes I guess a combination of me misunderstanding and because I
attempted to adapt the first function I wrote to allocate memory:

float *array;
int size = some_number;

void allocate_memory_float(float **array, int size, const unsigned int
line)
{
if (!(*array = (float *)calloc((size + 1), sizeof (float)))) {
check_errors("Error allocating enough memory for array", line);
}
return;
}

which I call:

allocate_memory_float(&array, size, __LINE__);

Which I think in this case as I am changing the array it is fine to
pass it like this? Just not for fread because I am not actually
changing anything?

Thanks again, the help really is appreciated.

Barry Schwarz

unread,
Jul 3, 2009, 7:05:12 PM7/3/09
to
On Fri, 3 Jul 2009 15:41:33 -0700 (PDT), Martin <mdek...@gmail.com>
wrote:

>Hi thanks for all of the replies...


>
>yes I guess a combination of me misunderstanding and because I
>attempted to adapt the first function I wrote to allocate memory:
>
>float *array;
>int size = some_number;
>
>void allocate_memory_float(float **array, int size, const unsigned int
>line)
>{
> if (!(*array = (float *)calloc((size + 1), sizeof (float)))) {

You don't need to cast the return from calloc. In fact, doing so can
cause the compiler to fail to warn you about some undefined behavior.

You also don't need the parentheses around size+1.

While using calloc does not cause any problems, it also doesn't
provide a portable benefit. It WILL set every byte of the allocated
memory to all-bits-zero. However, there is NO guarantee this
represents 0.0f or any other valid float value.

By the way, for messages posted to Usenet it is better to use spaces
than tabs to perform indenting. It eliminates problems due the
variety of newsreaders used.

> check_errors("Error allocating enough memory for array", line);
> }
> return;
>}
>
>which I call:
>
>allocate_memory_float(&array, size, __LINE__);
>
>Which I think in this case as I am changing the array it is fine to
>pass it like this? Just not for fread because I am not actually
>changing anything?

While this approach will work, the more common C idiom would be

#include <stdlib.h>
float* allocate_memory_float(int size, const unsigned int line)
{
float *ptr;
if (!(ptr = malloc((size + 1) * sizeof *ptr))) {


check_errors("Error allocating enough memory for array",
line);
}

return ptr;
}

which is called with
array = allocate_memory_float(size, __LINE__);

If you were willing to do the error checking in the calling function,
the entire body of allocate_memory_float could be replaced with
return malloc((size + 1) * sizeof *ptr);
which would basically eliminate the need for a separate function
entirely.

Martin

unread,
Jul 3, 2009, 7:26:48 PM7/3/09
to
Thanks a lot Barry I will adopt the form you suggested instead. Two
more questions if you have a moment

1.Do you have a suggestion for how I would write a function for the
allocation of my structure?

Currently I set a structure up

typedef struct {
int something;
int something_else;
} control;

in my .h file and then I allocate it in my code

control *c;
if ((c = (control *)malloc(sizeof (control))) == NULL) {
check_errors("Structure: Not allocated enough memory", __LINE__);
}

I don't know how I would set this up a library function

2. Also more a general question, currently I set up one big sturcture
and pass this around throughout my code. Do you suggest many smaller
structures would be better in practice? Does it matter? I guess the
later way reduces what is sent through the code, but does this
practically matter?

Thanks again

Morris Keesan

unread,
Jul 3, 2009, 9:49:39 PM7/3/09
to
On Fri, 03 Jul 2009 19:26:48 -0400, Martin <mdek...@gmail.com> wrote:

> control *c;
> if ((c = (control *)malloc(sizeof (control))) == NULL) {
> check_errors("Structure: Not allocated enough memory", __LINE__);
> }
>
> I don't know how I would set this up a library function

control *allocate_control(void)
{
control *c;
if ((c = malloc(sizeof (control))) == NULL) {


check_errors("Structure: Not allocated enough memory", __LINE__);

return c;
}


>
> 2. Also more a general question, currently I set up one big sturcture
> and pass this around throughout my code. Do you suggest many smaller
> structures would be better in practice? Does it matter? I guess the
> later way reduces what is sent through the code, but does this
> practically matter?

A common way of dealing with this is to pass a pointer to the structure,
instead of passing the structure itself. This is usually more efficient
than passing the entire structure. When passing by reference like this,
a useful habit to get into is to use the "const" attribute in the function
declaration, when the function is not intended to modify the contents of
the structure. This has the benefits of
* Giving useful information to the human reader of the code.
* Giving useful information to optimizing compilers
* Generating a compile-time error message if someone tries to
change the implementation of the function in a way which would
modify the contents of the structure.

E.g.
int do_something_with_my_structure (const my_structure *p);

Barry Schwarz

unread,
Jul 4, 2009, 12:24:07 AM7/4/09
to
On Fri, 3 Jul 2009 16:26:48 -0700 (PDT), Martin <mdek...@gmail.com>
wrote:

>Thanks a lot Barry I will adopt the form you suggested instead. Two


>more questions if you have a moment
>
>1.Do you have a suggestion for how I would write a function for the
>allocation of my structure?
>
>Currently I set a structure up
>
>typedef struct {
> int something;
> int something_else;
>} control;
>
>in my .h file and then I allocate it in my code
>
>control *c;
>if ((c = (control *)malloc(sizeof (control))) == NULL) {
> check_errors("Structure: Not allocated enough memory", __LINE__);
>}
>
>I don't know how I would set this up a library function

What is different between this effort and the one you used for array?
Why put it in a function at all? It's just a single statement.

But whatever you do, LOSE THE CAST.

>
>2. Also more a general question, currently I set up one big sturcture
>and pass this around throughout my code. Do you suggest many smaller
>structures would be better in practice? Does it matter? I guess the
>later way reduces what is sent through the code, but does this
>practically matter?

As Morris mentioned, one approach is to pass a pointer to the struct
instead of the struct itself. If the function being called should not
change any of the contents of the struct, then define the parameter as
a pointer to const struct.

While several smaller structures will probably consume slightly more
space, if the contents of the structure can be logically
compartmentalized the greater granularity could make coding easier.
For example, if function-1 should update some members of the structure
but not others, if those members are in a separate structure you could
pass only that portion and be assured that a coding error will not
cause function-1 to update an unintended portion.

Morris Keesan

unread,
Jul 4, 2009, 10:56:55 AM7/4/09
to
On Sat, 04 Jul 2009 00:24:07 -0400, Barry Schwarz <schw...@dqel.com>
wrote:

> On Fri, 3 Jul 2009 16:26:48 -0700 (PDT), Martin <mdek...@gmail.com>
> wrote:

...


>> control *c;
>> if ((c = (control *)malloc(sizeof (control))) == NULL) {
>> check_errors("Structure: Not allocated enough memory", __LINE__);

...


>
> But whatever you do, LOSE THE CAST.

This is a sufficiently common idiom that it's worth explaining WHY
the cast should be removed.

Casting the return value of malloc to the type of the left-hand-side was
never necessary, in any version of C I've ever used. But before the
introduction of the (void *) type, when malloc returned a (char *), some
versions of lint would complain about the assignment, which got people
into the habit of adding the cast to eliminate this spurious warning.

The disadvantage of the cast is that, if you accidentally omit including
a proper declaration for malloc (usually by forgetting to include
<stdlib.h>),
inserting the cast will hide this from you, because a cast is an explicit
directive to the compiler saying that you know what you're doing. Without
the cast, the implicitly (int) return value from malloc is incompatible
with
whatever pointer type is on the left hand side of the assignment operator,
and
the compiler is required to issue an error message, informing you of this.

Kaz Kylheku

unread,
Jul 4, 2009, 2:25:13 PM7/4/09
to
On 2009-07-04, Morris Keesan <kee...@alum.bu.edu> wrote:
> On Sat, 04 Jul 2009 00:24:07 -0400, Barry Schwarz <schw...@dqel.com>
> wrote:
>
>> On Fri, 3 Jul 2009 16:26:48 -0700 (PDT), Martin <mdek...@gmail.com>
>> wrote:
> ...
>>> control *c;
>>> if ((c = (control *)malloc(sizeof (control))) == NULL) {
>>> check_errors("Structure: Not allocated enough memory", __LINE__);
> ...
>>
>> But whatever you do, LOSE THE CAST.
>
> This is a sufficiently common idiom that it's worth explaining WHY
> the cast should be removed.

I'm a long-time C programmer, and I don't agree that it should be removed.

> Casting the return value of malloc to the type of the left-hand-side was
> never necessary, in any version of C I've ever used. But before the
> introduction of the (void *) type, when malloc returned a (char *), some

The void * type was imitated from C++, which doesn't have the gratuitous hole
that void * can be converted to another pointer-to-object type without a cast:

double x;
void *pv = &x;
int *pi = pv; /* diagnostic required in C++, not in C. */
*pi = 0;

This bit of nonsense should be removed from the C language.

What it means is that you can write some unsafe conversions in C,
but there is no /required/ diagnosis for them. You may be able to find
such a diagnostic in your compiler anyway, in which case it's a good
idea to turn it on. Even if you don't have the diagnostic, it's still
a good idea to put in the cast.

void * conversions can easily go awry. For instance, in generic containers,
callbacks and such.

void fun(void *);

{
struct foo args;
/* ... */
callback_interface(fun, &args);
/* ... */
}

/* ... */

void fun(void *pvargs)
{
struct bar *args = pvargs; /* oops! */
}

Ideally we should get a diagnostic here. The conversion is unsafe, and in fact
it's wrong. The callback arguments are being converted to the wrong type.

Now you can add a (struct bar *) cast here just to make the diagnostic go away,
while leaving the bug.

But you are still better off because the conversion is flagged with explicit
syntax.

Some maintainer could perform a review of the code for the validity of such
conversions.

It will help that maintainer to have the assurance that all such conversions
are either identified by cast syntax, or else are diagnosed.

It's very hard to look for bad conversions in a C program, if they happen
by ordinary ``a = b'' assignment.

> versions of lint would complain about the assignment, which got people
> into the habit of adding the cast to eliminate this spurious warning.

The point of using lint is to obtain such spurious warnings.

You investigate whether or not the potentially unsafe conversion is in fact
safe, and then you fix the code, if necessary, or add a cast to give the
conversion blessing.

The cast serves as a marker which identifies the conversion, so that
you, or some other maintainer, can still find it.

> The disadvantage of the cast is that, if you accidentally omit including
> a proper declaration for malloc (usually by forgetting to include
><stdlib.h>),

In what cases is malloc not properly declared, yet <stdlib.h> has been
included? :)

> inserting the cast will hide this from you

Only if you use an old compiler that doesn't warn that you are calling a
function that hasn't been declared.

Note that as of C99 (which was a decade ago), calls to undeclared functions
are a violation of syntax.

See 6.5.1 paragraph 2, and the clarifying footnote.

An identifier which has not been declared is simply not a primary expression,
and thus a syntax error.

Martin

unread,
Jul 4, 2009, 5:56:09 PM7/4/09
to
Thanks for all of that explanation, it is appreciated.

Regarding the casting - I guess it was something I adapted from
someone's code and I hadn't really given it a lot of consideration if
I am honest. I am slightly confused after reading the last post
(perhaps this is over my level of C coding). I shall re read the posts
again, but I guess I shall remove the casting for the moment?

I agree the allocation of my structure is only a few lines. But it
occurred to me the other day I am constantly copying and pasting bits
of code I do a lot from one piece of code to another, e.g. allocating
memory for arrays etc. So I remembered what the person who taught me
suggested about make my own libraries and thought I would try and
construct a library of things I commonly do to tidy up my code and
make my working process a bit more efficient. Allocating structures
was also something I found I did a lot, so I thought I would make a
function for this too.

Sorry I also think I wasn't clear with my question regarding
structures. At the moment I allocate one control structure and fill
this up with things I need to pass through the code, i.e. things I
read in from the cmd line, things Im moving btw functions etc and then
pass it around like this...

int main(int argc, char **argv)
{

some_function(c);

}

void some function (control *c)
{

}

and so on.

I just wondered if this was bad practice and whether it was better to
allocate lots of structures, or if it even matters. E.g.

int main(int argc, char **argv)
{

some_function(c, sp, se);

}
void some function (control *c, some_parameters *sp, something_else
*se)
{

}

where some_parameters and something_else are both structures. Perhaps
the latter way is more readable for others? My code is really only
read by me...but always happy to try and improve its readability

Thanks again!

Martin

Morris Keesan

unread,
Jul 5, 2009, 12:00:05 PM7/5/09
to
On Sat, 04 Jul 2009 17:56:09 -0400, Martin <mdek...@gmail.com> wrote:

> Regarding the casting - I guess it was something I adapted from
> someone's code and I hadn't really given it a lot of consideration if
> I am honest. I am slightly confused after reading the last post
> (perhaps this is over my level of C coding). I shall re read the posts
> again, but I guess I shall remove the casting for the moment?

Don't be in a hurry to remove the cast, especially not if you don't
really understand why you're doing it. As you've seen, experts disagree
about this. Make your own decisions. People feel strongly in both
directions. Personally, I prefer not to use the cast, but I don't
bother removing it from existing code.

Martin

unread,
Jul 5, 2009, 4:04:21 PM7/5/09
to
On Jul 5, 5:00 pm, "Morris Keesan" <kee...@alum.bu.edu> wrote:

Ok thanks - I have been researching it now and I think I understand
the issue now. I thought this page explained it well
http://everything2.com/title/Casting%2520the%2520return%2520value%2520of%2520malloc%2528%2529
if that helps anyone else.

Eric Sosman

unread,
Jul 5, 2009, 5:22:23 PM7/5/09
to

The treatment on that page is at best superficial, and
contains at least three minor errors:

1) It incorrectly states that the Ten Commandments say to
cast the value of malloc(). This is probably due to an
over-hasty mis-reading of the Third Commandment, but
could possibly be a complete misunderstanding of the
Fourth.

2) It incorrectly claims that a void* can be "converted to
any other pointer type." The writer has overlooked the
fact that void* cannot (portably) be converted to or
from a function pointer type.

3) It speaks of malloc() returning its value "on the stack"
of the Alpha CPU, and conjures up the bogeyman of having
the wrong amount of stack space reserved. For functions
as simple as malloc(), Alpha didn't use the stack at all.
(Bad things would happen, but not the bad things that
the page describes.)

Finally, the danger that the page warns of ("You'll silence
the compiler's complaint about failing to include <stdlib.h>")
is largely irrelevant nowadays. All C99 compilers are obliged
to produce a diagnostic if an undeclared function is mentioned,
casts or no casts, many C90 compilers (not so obliged) do so out
of the goodness of their hearts, and every lint-ish tools I've
ever seen has done so. The error, if made, will be caught right
rapidly.

I happen to agree with the page's recommendation, but I
like to think my reasons are better than those it offers.

--
Eric Sosman
eso...@ieee-dot-org.invalid

lovecrea...@gmail.com

unread,
Jul 6, 2009, 9:47:26 AM7/6/09
to
On Jul 5, 2:25 am, Kaz Kylheku <kkylh...@gmail.com> wrote:
> I'm a long-time C programmer, and I don't agree that it should be removed.
>
> The void * type was imitated from C++, which doesn't have the gratuitous hole
> that void * can be converted to another pointer-to-object type without a cast:
>
> double x;
> void *pv = &x;
> int *pi = pv; /* diagnostic required in C++, not in C. */
>
> What it means is that you can write some unsafe conversions in C,
> but there is no /required/ diagnosis for them. You may be able to find
> such a diagnostic in your compiler anyway, in which case it's a good
> idea to turn it on. Even if you don't have the diagnostic, it's still
> a good idea to put in the cast.
>

h&s sec.6.4.1:
"in c++ a cast must be used to convert a pointer of type void* to
another kind of pointer. You can also use the cast in c, but it is not
required in an assignment"

h&s sec.16.1:
"the cast (T*) is not strictly necessary in standard c because malloc
returns a pointer of type void* and the implicit conversion on
assignment to objptr is allowed."

k&r errata #142:
"It's not necessary (given that coercion of void * to ALMOSTANYTYPE *
is automatic), and possibly harmful if malloc, or a proxy for it,
fails to be declared as returning void *. The explicit cast can cover
up an unintended error. On the other hand, pre-ANSI, the cast was
necessary, and it is in C++ also."

Nick Keighley

unread,
Jul 7, 2009, 3:52:34 AM7/7/09
to
On 6 July, 14:47, "lovecreatesbea...@gmail.c0m"

so? Are you pointing out they disagree with Kaz?
He's probably aware of this

Morris Keesan

unread,
Jul 8, 2009, 2:10:45 PM7/8/09
to
On Mon, 06 Jul 2009 09:47:26 -0400, lovecrea...@gmail.c0m
<lovecrea...@gmail.com> wrote:

> k&r errata #142:


> On the other hand, pre-ANSI, the cast was

> necessary [from malloc return to other pointer types]

I've seen this claim many times, but I've never used any compiler for
which the cast was necessary, nor seen any definition of the language
in which it was necessary, including, among others, the language
definition as published in K&R 1, or the (almost identical) C Language
documentation in the version 6 or 7 Unix documentation.

Eric Sosman

unread,
Jul 8, 2009, 2:30:46 PM7/8/09
to

Since K and R themselves make the claim, a "willing suspension
of disbelief" seems appropriate ...

--
Eric....@sun.com

Keith Thompson

unread,
Jul 8, 2009, 2:32:31 PM7/8/09
to

K&R1 Appendix A 14.4, "Explicit pointer conversions" (page 210):

Certain conversions involving pointers are permitted but have
implementation-defined aspects. They are all specified by means
of an explicit type-conversion operator ([sections] 7.2 and 8.7).

Some compilers may have been lax enough to permit, for example,
converting from char* (the result type of malloc) to int* without a
cast, but K&R1 strongly implies that a cast was required. (There was
no void* type.)

An example on the next page is:

extern char *alloc();
double *dp;

dp = (double *) alloc(sizeof(double));
*dp = 22.0 / 7.0;

Going back to 7.14, (page 191), the description of assignment is
somewhat ambiguous:

In the simple assignment with =, the value of the expression
replaces that of the object referred to by the lvalue. If both
operands have arithmetic type, the right operand is converted to
the type of the left preparatory to the assignment.

Note that it doesn't mention implicit pointer conversion. But later
in the same section:

The compilers currently allow a pointer to be assigned to an
integer, an integer to a pointer, and a pointer to a pointer of
another type. The assigment is a pure copy operation, with no
conversion. This usage is nonportable, and may produce pointers
which cause addressing exceptions when used. However, it is
guaranteed that assignment of the constant 0 to a pointer will
produce a null pointer distinguishable from a pointer to any
object.

My reading of this is that implicit pointer conversion is not
permitted by the language, but compilers allow it anyway.

(Of course a lot of this changed with the 1989/1990 and 1999
C standards.)

--
Keith Thompson (The_Other_Keith) ks...@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"

Ben Bacarisse

unread,
Jul 9, 2009, 8:17:47 AM7/9/09
to
"Morris Keesan" <kee...@alum.bu.edu> writes:

You've had some references, but there are also real examples. I
worked on one machine that was (16-bit) word-addressed. To represent
character pointers, the pointer to the containing word was shifted
left by 1 and the bottom bit was used to pick which byte was being
addressed.

When compiling:

int i;
char *ip = (char *)&i;
double *dp = (double *)malloc(sizeof *dp);

the casts generated code (a shift left and a shift right). You might
wonder why the compiler could not just generate this code without the
cast but remember this is K&R C where there are no function
prototypes. Faced with:

use_pointer(malloc(16));

the compiler has no idea what type of pointer the function expects.
Some of the Unix utilities were a challenge to port.

--
Ben.

Morris Keesan

unread,
Jul 9, 2009, 10:16:01 PM7/9/09
to
On Thu, 09 Jul 2009 08:17:47 -0400, Ben Bacarisse <ben.u...@bsb.me.uk>
wrote:

> "Morris Keesan" <kee...@alum.bu.edu> writes:
>
>> On Mon, 06 Jul 2009 09:47:26 -0400, lovecrea...@gmail.c0m
>> <lovecrea...@gmail.com> wrote:
>>
>>> k&r errata #142:
>>> On the other hand, pre-ANSI, the cast was
>>> necessary [from malloc return to other pointer types]
>>
>> I've seen this claim many times, but I've never used any compiler for
>> which the cast was necessary, nor seen any definition of the language
>> in which it was necessary, including, among others, the language
>> definition as published in K&R 1, or the (almost identical) C Language
>> documentation in the version 6 or 7 Unix documentation.
>
> You've had some references, but there are also real examples. I
> worked on one machine that was (16-bit) word-addressed. To represent
> character pointers, the pointer to the containing word was shifted
> left by 1 and the bottom bit was used to pick which byte was being
> addressed.
>
> When compiling:
>
> int i;
> char *ip = (char *)&i;
> double *dp = (double *)malloc(sizeof *dp);
>
> the casts generated code (a shift left and a shift right). You might
> wonder why the compiler could not just generate this code without the
> cast but remember this is K&R C where there are no function
> prototypes.

This has nothing to do with function prototypes. If malloc were
properly declared as returning a (char *), then the
conversion-by-assignment which would be generated by

double *dp = malloc(sizeof *dp);

should be the same conversion generated by the cast, i.e. conversion
of (char *) to (double *). If malloc were not declared, the conversion
would have been the (incorrect) (int) to (double *). Are you saying
that on this word-addressed machine, assigning a (char *) to a (double *)
without a cast did not generate the code to do a type conversion?

I also worked on a word-addressed machine (a Honeywell Level 6), where
(char *) was twice as large as all other pointers, because the C compiler
implemented (char *) as an (int *) plus an extra word indicating high or
low byte. As you say below, some code was a "challenge to port", since
so many people wrote code which assumed that all pointers had identical
representations. But even on that machine, assigning a (char *) to
any other data-pointer type caused the correct conversion to occur
(assuming that the (char *) were properly aligned), just as if the
conversion were done by a cast before the assignment.

> Faced with:
>
> use_pointer(malloc(16));
>
> the compiler has no idea what type of pointer the function expects.
> Some of the Unix utilities were a challenge to port.

Yes, and in this case, the result of malloc would have to be
converted to the correct pointer type by an explicit cast, but this
is totally different from the case of an assignment, where the
left-hand side of the assignment operator imposes a type which must
be converted to (except in the cases cited by Keith Thompson, for
which thanks, Keith).

Keith Thompson

unread,
Jul 9, 2009, 10:41:57 PM7/9/09
to

(Note again that we're talking about K&R C, not modern C.)

As I posted recently, K&R1 doesn't say that this will result in a
conversion. Page 192:

The compilers currently allow a pointer to be assigned to an
integer, an integer to a pointer, and a pointer to a pointer of
another type. The assigment is a pure copy operation, with no
conversion. This usage is nonportable, and may produce pointers
which cause addressing exceptions when used. However, it is
guaranteed that assignment of the constant 0 to a pointer will
produce a null pointer distinguishable from a pointer to any
object.

And for scalar initialization (page 198):

The initial value of the object is taken from the expression; the
same conversions as for assignment are performed.

If


double *dp = malloc(sizeof *dp);

worked properly on a system where double* and char* were of different
sizes, then apparently the compiler behaved differently from what K&R1
described.

> I also worked on a word-addressed machine (a Honeywell Level 6), where
> (char *) was twice as large as all other pointers, because the C compiler
> implemented (char *) as an (int *) plus an extra word indicating high or
> low byte. As you say below, some code was a "challenge to port", since
> so many people wrote code which assumed that all pointers had identical
> representations. But even on that machine, assigning a (char *) to
> any other data-pointer type caused the correct conversion to occur
> (assuming that the (char *) were properly aligned), just as if the
> conversion were done by a cast before the assignment.

Again this differs from the description in K&R1.

>> Faced with:
>>
>> use_pointer(malloc(16));
>>
>> the compiler has no idea what type of pointer the function expects.
>> Some of the Unix utilities were a challenge to port.
>
> Yes, and in this case, the result of malloc would have to be
> converted to the correct pointer type by an explicit cast, but this
> is totally different from the case of an assignment, where the
> left-hand side of the assignment operator imposes a type which must
> be converted to (except in the cases cited by Keith Thompson, for
> which thanks, Keith).

Right. In modern C with prototypes, function arguments are implicitly
converted to the parameter type as if by assignment. In K&R C, the
parameter types weren't visible to the caller, so the only conversions
that occured were float to double, char and short to int, and array to
pointer. (Modern C behaves similarly when no prototype is visible or
for variadic functions.)

Ben Bacarisse

unread,
Jul 10, 2009, 6:55:28 AM7/10/09
to
"Morris Keesan" <kee...@alum.bu.edu> writes:

It did not and the legalistic reason may have been because that is
what is mandated by K&R (I think Keith has posted the reference
elsewhere in this thread) but I suspect the reason compiler writer
were happy to go along with this is that is gives pointers a simple
interface with the old un-prototyped functions: initialisation,
assignment and parameter passing are all conversion free copy
operations.

> I also worked on a word-addressed machine (a Honeywell Level 6), where
> (char *) was twice as large as all other pointers, because the C compiler
> implemented (char *) as an (int *) plus an extra word indicating high or
> low byte. As you say below, some code was a "challenge to port", since
> so many people wrote code which assumed that all pointers had identical
> representations. But even on that machine, assigning a (char *) to
> any other data-pointer type caused the correct conversion to occur
> (assuming that the (char *) were properly aligned), just as if the
> conversion were done by a cast before the assignment.
>
>> Faced with:
>>
>> use_pointer(malloc(16));
>>
>> the compiler has no idea what type of pointer the function expects.
>> Some of the Unix utilities were a challenge to port.
>
> Yes, and in this case, the result of malloc would have to be
> converted to the correct pointer type by an explicit cast, but this
> is totally different from the case of an assignment, where the
> left-hand side of the assignment operator imposes a type which must
> be converted to (except in the cases cited by Keith Thompson, for
> which thanks, Keith).

Yes, it is a separate case, but I was trying to flesh out the logic
behind K&R's view that assignment does no conversion. My feeling is
that this is done to give pointers a simple model -- all conversions
must be explicit and all duplication operations (assignment,
initialastion and argument passing) are plain copies.

There is (was?) another way open: assignment and initialisation could
convert (along with the other know-type case: function return) and a
promotion be added to convert all pointer arguments to the widest and
most general pointer type. Of course, in many cases this would
perform unnecessary operations. Anything between these two seems to
me to be an unsatisfactory mixed bag.

In the other case where the right type is known, function return, K&R
give explicit advice: use a cast as this "represents the safest course
for the future" (sec 6.5 p 134).

--
Ben.

Richard Bos

unread,
Jul 14, 2009, 3:16:33 PM7/14/09
to
Kaz Kylheku <kkyl...@gmail.com> wrote:

> On 2009-07-04, Morris Keesan <kee...@alum.bu.edu> wrote:
> > Casting the return value of malloc to the type of the left-hand-side was
> > never necessary, in any version of C I've ever used. But before the
> > introduction of the (void *) type, when malloc returned a (char *), some
>
> The void * type was imitated from C++, which doesn't have the gratuitous hole
> that void * can be converted to another pointer-to-object type without a cast:
>
> double x;
> void *pv = &x;
> int *pi = pv; /* diagnostic required in C++, not in C. */
> *pi = 0;
>
> This bit of nonsense should be removed from the C language.

The nonsense is in C++. It implies that assigning one way is dangerous,
while the other way is safe. This is clearly misinformation. It's not
either way which is dangerous or safe, but a wrong combination of both.
Therefore, the cast should be required on both, or on neither. Demanding
it on only one is misleading.
What's more, requiring the cast on the side that malloc() is on is
doubly bogotic, since assigning the return value from malloc() to _any_
object pointer is safe by design.

> What it means is that you can write some unsafe conversions in C,

> void * conversions can easily go awry. For instance, in generic containers,
> callbacks and such.
>
> void fun(void *);
>
> {
> struct foo args;
> /* ... */
> callback_interface(fun, &args);
> /* ... */
> }

> void fun(void *pvargs)


> {
> struct bar *args = pvargs; /* oops! */
> }

Yes, but how do you know that the oops is in _that_ line? Maybe fun
should have been called with a bar * instead, and the real error was
passing it a foo * in the first place. So the cast should have been
here, as well:

callback_interface(fun, (void *)&args);

> > versions of lint would complain about the assignment, which got people
> > into the habit of adding the cast to eliminate this spurious warning.
>
> The point of using lint is to obtain such spurious warnings.
>
> You investigate whether or not the potentially unsafe conversion is in fact
> safe, and then you fix the code, if necessary, or add a cast to give the
> conversion blessing.

Except that you know damned well that the solution employed in most
cases will be to add the cast _without_ investigation, or even without
running lint. Old C code, or written by C++ programmers, is all too
often riddled with such spurious casts, which only get in the way of
checking for _real_ points of danger.

Richard

Morris Keesan

unread,
Jul 22, 2009, 3:39:12 PM7/22/09
to
On Thu, 09 Jul 2009 22:41:57 -0400, Keith Thompson <ks...@mib.org> wrote:

> As I posted recently, K&R1 doesn't say that this will result in a
> conversion. Page 192:
>
> The compilers currently allow a pointer to be assigned to an
> integer, an integer to a pointer, and a pointer to a pointer of
> another type. The assigment is a pure copy operation, with no
> conversion. This usage is nonportable, and may produce pointers
> which cause addressing exceptions when used. However, it is
> guaranteed that assignment of the constant 0 to a pointer will
> produce a null pointer distinguishable from a pointer to any
> object.

I meant to reply to this a couple of weeks ago. Thanks for posting
this citation, Keith. I stand corrected on my earlier claim that
casting the return value of malloc was never required.

0 new messages