Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > use pointer as array, Weird

Reply
Thread Tools

use pointer as array, Weird

 
 
wy
Guest
Posts: n/a
 
      01-29-2013
This code can't be compiled without errors.
/* test.c */
#include <stdio.h>
typedef char data_str;

data_str _data_[3], *data = _data_;
void data_pointer_print(){
printf("_data_ address 1st: %p\n", _data_);
printf(" data address 2nd: %p\n", data );
}

extern data_str data[3];
int main(){
data_pointer_print();
printf(" data address 3rd: %p\n", data );
}

I understand the error "conflicting types for ‘data’".
But after splitting the code into several files,
it is compiled successfully, and have an undesired result.

/* data.h */
#include <stdio.h>
typedef char data_str;
extern void datastr_pointer_print();

/* data.c */
#include "data.h"
data_str _data_[3], *data = _data_;
void data_pointer_print(){
printf("_data_ address in data.c: %p\n", _data_);
printf(" data address in data.c: %p\n", data );
}

/* test.c */
#include "data.h"
extern data_str data[3];
int main(){
data_pointer_print();
printf(" data address in test.c: %p\n", data );
}

And this is the result:
$ gcc test.c data.c
$ ./a.out
_data_ address in data.c: 0x6009b8
data address in data.c: 0x6009b8
data address in test.c: 0x6009a0

Here we see, it's compiled with no errors and warnings,
and the data addresses in data.c and test.c are different.
Why are they different and
why don't c complier prohibit this kind of use?
 
Reply With Quote
 
 
 
 
Shao Miller
Guest
Posts: n/a
 
      01-29-2013
On 1/29/2013 02:27, wy wrote:
> This code can't be compiled without errors.
> /* test.c */
> #include <stdio.h>
> typedef char data_str;
>
> data_str _data_[3], *data = _data_;
> void data_pointer_print(){
> printf("_data_ address 1st: %p\n", _data_);
> printf(" data address 2nd: %p\n", data );
> }
>
> extern data_str data[3];
> int main(){
> data_pointer_print();
> printf(" data address 3rd: %p\n", data );
> }
>
> I understand the error "conflicting types for ‘data’".
> But after splitting the code into several files,
> it is compiled successfully, and have an undesired result.
>
> /* data.h */
> #include <stdio.h>
> typedef char data_str;
> extern void datastr_pointer_print();
>
> /* data.c */
> #include "data.h"
> data_str _data_[3], *data = _data_;
> void data_pointer_print(){
> printf("_data_ address in data.c: %p\n", _data_);
> printf(" data address in data.c: %p\n", data );
> }
>
> /* test.c */
> #include "data.h"
> extern data_str data[3];
> int main(){
> data_pointer_print();
> printf(" data address in test.c: %p\n", data );
> }
>
> And this is the result:
> $ gcc test.c data.c
> $ ./a.out
> _data_ address in data.c: 0x6009b8
> data address in data.c: 0x6009b8
> data address in test.c: 0x6009a0
>
> Here we see, it's compiled with no errors and warnings,
> and the data addresses in data.c and test.c are different.
> Why are they different and
> why don't c complier prohibit this kind of use?


Because you have lied to the C implementation. An array and a pointer
are different things; they are not interchangeable. test.c lies that
'data' has type 'data_str[3]'. The truth is that 'data' has type
'data_str *', as defined in data.c.

--
- Shao Miller
--
"Thank you for the kind words; those are the kind of words I like to hear.

Cheerily," -- Richard Harter
 
Reply With Quote
 
 
 
 
Bart van Ingen Schenau
Guest
Posts: n/a
 
      01-29-2013
On Tue, 29 Jan 2013 15:27:50 +0800, wy wrote:

> This code can't be compiled without errors.

<snip - code using different types for one file-scope object called
`data`>

> I understand the error "conflicting types for ‘data’". But after
> splitting the code into several files, it is compiled successfully, and
> have an undesired result.
>

<snip - same code, split over multiple files>
> Here we see, it's compiled with no errors and warnings, and the data
> addresses in data.c and test.c are different. Why are they different and
> why don't c complier prohibit this kind of use?


Different C files are processed completely independent of each other. For
all the compiler and linker are concerned, the two files can be processed
on different days on different machines. Until the linker gets his hands
on the object files, there is no indication that they belong together in
one application.

For that reason, by splitting the source over two files (without a common
header file with a declaration of `data`), you have taken away all
possibilities for the compiler to notice the conflict in the two
declarations of `data`.
Additionally, because the two declarations of `data` are not compatible
with each other, you have landed yourself squarely in the land of
Undefined Behaviour. The program can do whatever it likes and there is no
right or wrong any more.

Bart van Ingen Schenau
 
Reply With Quote
 
glen herrmannsfeldt
Guest
Posts: n/a
 
      01-29-2013
wy <(E-Mail Removed)> wrote:

(snip)

> /* data.h */
> #include <stdio.h>
> typedef char data_str;
> extern void datastr_pointer_print();
>
> /* data.c */
> #include "data.h"
> data_str _data_[3], *data = _data_;
> void data_pointer_print(){
> printf("_data_ address in data.c: %p\n", _data_);
> printf(" data address in data.c: %p\n", data );


Add here:

printf(" data address in data.c: %p\n", &data );


> }
>
> /* test.c */
> #include "data.h"
> extern data_str data[3];
> int main(){
> data_pointer_print();
> printf(" data address in test.c: %p\n", data );
> }


(snip)

> Here we see, it's compiled with no errors and warnings,


The two are compiled separately and, separately, are
legal C. The linker doesn't test for the error for
mostly historical reasons.

> and the data addresses in data.c and test.c are different.
> Why are they different and
> why don't c complier prohibit this kind of use?


It isn't legal C, but the compiler can't tell.

-- glen
 
Reply With Quote
 
wy
Guest
Posts: n/a
 
      01-29-2013
On 01/29/2013 05:45 PM, glen herrmannsfeldt wrote:

> The two are compiled separately and, separately, are
> legal C. The linker doesn't test for the error for
> mostly historical reasons.
>
> It isn't legal C, but the compiler can't tell.
>


I see.
It's the linker's fault.
The compiler tests(tries to compile) source code,
and the linker links object code while not testing source or object.
Thank you.
 
Reply With Quote
 
James Kuyper
Guest
Posts: n/a
 
      01-29-2013
On 01/29/2013 06:57 AM, wy wrote:
> On 01/29/2013 05:45 PM, glen herrmannsfeldt wrote:
>
>> The two are compiled separately and, separately, are
>> legal C. The linker doesn't test for the error for
>> mostly historical reasons.
>>
>> It isn't legal C, but the compiler can't tell.
>>

>
> I see.
> It's the linker's fault.


No. The linker is not required by the C standard to generate a
diagnostic, so the linker is not at fault. This means that the object
file format might not even provide enough information to allow the
linker to diagnose such problems. Even if the linker does have the
needed capabilities, the standard does not require a conforming compiler
to give the linker the needed information, so the compiler is also not
at fault.

Finally, this was a deliberate decision by the C committee to allow
implementation of C even on systems with fairly simple-minded linkers.
Keep in mind that linkers were generally a lot less capable back when C
was first standardized. This decision is part of a pattern in the C
standard, to avoid imposing requirements that are hard to meet on some
platforms. As a result, C can be easily and efficiently implemented on
almost every platform, and as a further result, C has been implemented
on almost every platform. You can argue with the C committee's goals -
other languages impose more requirements on implementations, allowing
them to impose fewer requirements on developers. However, this decision
is consistent with the goals of C, and so the standard is also not at
fault. The only fault is in the source code itself.
--
James Kuyper
 
Reply With Quote
 
James Kuyper
Guest
Posts: n/a
 
      01-29-2013
One side issue:

On 01/29/2013 02:27 AM, wy wrote:
> This code can't be compiled without errors.
> /* test.c */
> #include <stdio.h>
> typedef char data_str;
>
> data_str _data_[3], *data = _data_;


"-- All identifiers that begin with an underscore and either an
uppercase letter or another underscore are always reserved for any use.
-- All identifiers that begin with an underscore are always reserved for
use as identifiers with file scope in both the ordinary and tag name
spaces." (7.1.3p1).

_data_ has file scope, and is in the ordinary name space.

"If the program declares or defines an identifier in a
context in which it is reserved (other than as allowed by 7.1.4), or
defines a reserved identifier as a macro name, the behavior is
undefined." (7.1.3p2)

Therefore, your program has undefined behavior. Don't use such names.
--
James Kuyper
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      01-29-2013
James Kuyper <(E-Mail Removed)> writes:
> One side issue:
> On 01/29/2013 02:27 AM, wy wrote:
>> This code can't be compiled without errors.
>> /* test.c */
>> #include <stdio.h>
>> typedef char data_str;
>>
>> data_str _data_[3], *data = _data_;

>
> "-- All identifiers that begin with an underscore and either an
> uppercase letter or another underscore are always reserved for any use.
> -- All identifiers that begin with an underscore are always reserved for
> use as identifiers with file scope in both the ordinary and tag name
> spaces." (7.1.3p1).
>
> _data_ has file scope, and is in the ordinary name space.
>
> "If the program declares or defines an identifier in a
> context in which it is reserved (other than as allowed by 7.1.4), or
> defines a reserved identifier as a macro name, the behavior is
> undefined." (7.1.3p2)
>
> Therefore, your program has undefined behavior. Don't use such names.


Yes, but it's very unlikely that the use of a reserved identifier causes
the symptoms. (James, you know that, but the OP might not.)

--
Keith Thompson (The_Other_Keith) http://www.velocityreviews.com/forums/(E-Mail Removed) <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
wy
Guest
Posts: n/a
 
      01-29-2013
> Finally, this was a deliberate decision by the C committee to allow
> implementation of C even on systems with fairly simple-minded linkers.


It's surprising!
After I change the declaration of data in test.c to
'extern short data;',
still no errors and warning occur.
And result is
'
_data_ address in data.c: 0x6009c0
data address in data.c: 0x6009c0
data address in test.c: 0x9c0
',
as expected.
Thanks for you detailed explanation.
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      01-29-2013
wy <(E-Mail Removed)> writes:
> This code can't be compiled without errors.
> /* test.c */
> #include <stdio.h>
> typedef char data_str;
>
> data_str _data_[3], *data = _data_;
> void data_pointer_print(){
> printf("_data_ address 1st: %p\n", _data_);
> printf(" data address 2nd: %p\n", data );
> }
>
> extern data_str data[3];
> int main(){
> data_pointer_print();
> printf(" data address 3rd: %p\n", data );
> }
>
> I understand the error "conflicting types for ‘data’".
> But after splitting the code into several files,
> it is compiled successfully, and have an undesired result.
>
> /* data.h */
> #include <stdio.h>
> typedef char data_str;
> extern void datastr_pointer_print();
>
> /* data.c */
> #include "data.h"
> data_str _data_[3], *data = _data_;
> void data_pointer_print(){
> printf("_data_ address in data.c: %p\n", _data_);
> printf(" data address in data.c: %p\n", data );
> }
>
> /* test.c */
> #include "data.h"
> extern data_str data[3];
> int main(){
> data_pointer_print();
> printf(" data address in test.c: %p\n", data );
> }
>
> And this is the result:
> $ gcc test.c data.c
> $ ./a.out
> _data_ address in data.c: 0x6009b8
> data address in data.c: 0x6009b8
> data address in test.c: 0x6009a0
>
> Here we see, it's compiled with no errors and warnings,
> and the data addresses in data.c and test.c are different.
> Why are they different and
> why don't c complier prohibit this kind of use?


To some extent, your code conflates two different things: the often
confusing relationship between arrays and pointers in C (explained
in section 6 of the comp.lang.c FAQ, <http://www.c-faq.com/>),
and conflicting definitions of external objects in different
translation units.

Here's a simpler test case that demonstrates the latter problem:

==> foo.c <==
int x = 42;

==> bar.c <==
#include <stdio.h>

extern float x;

int main(void) {
printf("x = %g\n", x);
return 0;
}

This prints the garbage value that results from treating the int object
x as if it were a float object; I get "x = 5.88545e-44".

The way to avoid that is to declare anything that needs to be visible in
multiple translation unit in header files, guaranteeing that they all
see consistent declarations:

==> foo.h <==
#ifndef H_FOO_H
extern int x;
#endif /* H_FOO_H */

==> foo.c <==
#include "foo.h"

int x = 42;

==> bar.c <==
#include <stdio.h>
#include "foo.h"

int main(void) {
printf("x = %g\n", x);
return 0;
}

There's a single external *declaration* of x, visible to both foo.c
and bar.c. foo.c contains the *definition* of x. Since foo.c
includes foo.h, the compiler can check that the definition and
declaration are consistent.

And now -- well, actually, the program still compiles and prints
a garbage value:

x = 4.85447e-270

but only because I didn't fix the format in the printf() call (it
misbehaves for different reasons). (Why the different result?
A float argument, when passed to printf, is promoted to double.
Since int is 32 bits on my system and double is 64, the printed
result is probably composed of the 32 bits of the int value 42 plus
32 bits of stack garbage.)

At least some compilers will warn about that; gcc warns:

bar.c:5:5: warning: format ‘%g’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat]

And when we change the "%g" to "%d", the output is correct:

x = 42

When I started writing this, I intended to show how careful use
of header files addresses this problem -- and it does. I also
accidentally showed that you *still* have to be very careful
when writing C code. Headers let certain errors be caught by the
compiler rather than by the linker, but other errors still have to
be caught by the programmer (compilers won't necessarily diagnose
format string errors).

--
Keith Thompson (The_Other_Keith) (E-Mail Removed) <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Pointer to pointer or reference to pointer A C++ 7 07-05-2011 07:49 PM
Pointer to pointer Vs References to Pointer bansalvikrant@gmail.com C++ 4 07-02-2009 10:20 AM
use pointer and not use pointer, which is faster to access data? shuisheng C++ 4 09-26-2006 08:05 AM
passing the address of a pointer to a func that doesnt recieve a pointer-to-a-pointer jimjim C Programming 16 03-27-2006 11:03 PM
Pointer-to-pointer-to-pointer question masood.iqbal@lycos.com C Programming 10 02-04-2005 02:57 AM



Advertisments