C Pointer problem

M

Markus

Hi,

I can't understand why this code causes a "memory read exception" at
int x=**a;

void pass(int** a)
{
int x=**a;
}
void main()
{
int arr[2][2]={{1,2},{3,4}};
pass(arr);
}
The assignment of int int x=**a in the main function works.

Tanks for your help,
Markus
 
R

Richard Heathfield

Markus said:
Hi,

I can't understand why this code causes a "memory read exception" at
int x=**a;

void pass(int** a)
{
int x=**a;
}
void main()

main returns int.

If you can't even get the entry point right, what chance do you stand with
pointers?
{
int arr[2][2]={{1,2},{3,4}};
pass(arr);

pass() takes int **.

arr's value is taken as the address of its first element. Its first element
is an int[2] array, so the address of its first element has type int
(*)[2], which is not the same as int **.
 
D

Default User

Markus said:
Hi,

I can't understand why this code causes a "memory read exception" at
int x=**a;

void pass(int** a)
{
int x=**a;
}
void main()
{
int arr[2][2]={{1,2},{3,4}};
pass(arr);
}
The assignment of int int x=**a in the main function works.

Did this really compile without any warnings or errors?



Brian
 
R

Richard Heathfield

Default User said:
Markus said:
Hi,

I can't understand why this code causes a "memory read exception" at
int x=**a;

void pass(int** a)
{
int x=**a;
}
void main()
{
int arr[2][2]={{1,2},{3,4}};
pass(arr);
}
The assignment of int int x=**a in the main function works.

Did this really compile without any warnings or errors?

No.

foo.c:2: warning: no previous prototype for `pass'
foo.c: In function `pass':
foo.c:3: warning: unused variable `x'
foo.c: At top level:
foo.c:6: warning: function declaration isn't a prototype
foo.c:6: warning: return type of `main' is not `int'
foo.c: In function `main':
foo.c:8: warning: passing arg 1 of `pass' from incompatible pointer type
 
D

Default User

Richard said:
Default User said:


No.

Of course. It was a nudge to the OP to let him know that the compiler
doesn't issue diagnostics (just) because it doesn't like you.




Brian
 
J

jacob navia

Richard Heathfield a écrit :
foo.c:2: warning: no previous prototype for `pass'
foo.c: In function `pass':
foo.c:3: warning: unused variable `x'
foo.c: At top level:
foo.c:6: warning: function declaration isn't a prototype
foo.c:6: warning: return type of `main' is not `int'
foo.c: In function `main':
foo.c:8: warning: passing arg 1 of `pass' from incompatible pointer type

lcc-win32 has:
D:\lcc\mc66\test>lcc -A tw2.c
Warning tw2.c: 3 x is assigned a value that is never used
Warning tw2.c: 6 old-style function definition for 'main'
Warning tw2.c: 6 missing prototype for 'main'
Warning tw2.c: 6 'void main()' is a non-ANSI definition
Warning tw2.c: 8 assignment of pointer to array 2 of int to pointer to
pointer to int
0 errors, 5 warnings

Basically all warnings are the same, with wording differences.
The first warning of gcc however, is not clear to me:

foo.c:2: warning: no previous prototype for `pass'

Why is that an eror?

The function definition is correct, and it wasn't
previously used. That looks correct to me.
 
J

Jordan Abel

Richard Heathfield a écrit :

lcc-win32 has:
D:\lcc\mc66\test>lcc -A tw2.c
Warning tw2.c: 3 x is assigned a value that is never used
Warning tw2.c: 6 old-style function definition for 'main'

While void main() is a lot of things (none of them good), it is not an
old-style definition. Warning on empty brackets might make sense for
a declaration without a definition, it does NOT make sense for
a definition.
 
P

Peter Nilsson

Jordan said:
While void main() is a lot of things (none of them good), it is not an
old-style definition.

int main() is part of an an old style definition, or at least a
definition
that's been in style for a long time. ;)
Warning on empty brackets might make sense for
a declaration without a definition, it does NOT make sense for
a definition.

Compare...

int foo() { }

int main()
{
foo(42); /* no diagnostic required */
return 0;
}

With...

int foo(void) { }

int main()
{
foo(42); /* diagnostic required */
return 0;
}
 
B

Barry Schwarz

Hi,

I can't understand why this code causes a "memory read exception" at
int x=**a;

Because you lied to the compiler. Arrays are not pointers and
pointers are not arrays.
void pass(int** a)
{
int x=**a;

a is a pointer to pointer to int. To do determine the value of the
int itself, the following steps are required:

1 - Determine the value of a. It was passed "by value" to the
function using whatever calling convention is appropriate for your
system so this pretty straight forward.

2 - Use this value as the address of a pointer to int.

3 - Determine the value of this pointer.

4 - Use this value as the address of the int.

5 - Determine the value of the int.

6 - Store this value in x. (Not part of determining the value
but the concluding step in the initialization.)
}
void main()

int main(void) please.
{
int arr[2][2]={{1,2},{3,4}};
pass(arr);

Since this statement has a syntax error and will not compile cleanly,
why did you bother to execute the code at all. If you did not see the
mandatory diagnostic, you need to up the warning level on your
compiler.

You pass a 2D array to the function. In this context, the array name
evaluates to the address of the first element with type pointer to
first element. In other words, this is identical to coding
pass(&arr[0]);

arr[0] is itself an array of 2 int. A pointer to arr[0] has type
pointer to array of 2 int, written as int (*)[2].

The syntax error is because an int(*)[2] is incompatible with an
int**. There is no implicit conversion between the two.

Apparently on your system the two pointer types have similar
representations (not uncommon). pass will take the value and treat it
as the address of a pointer to int (steps 1 and 2 above).

Undefined behavior #1. Since the address is actually the
address of arr[0] which is an array of 2 int, it is really the address
of arr[0][0], a normal int. You have no idea if the alignment of this
int is suitable for a pointer to int.

Undefined behavior #2. pass will attempt to use the value at
this location as the address of an int (steps 3 and 4 above). You
have no idea if the value of the int that is actually at that location
is a valid address. It could be an invalid address or even a trap
representation.

Undefined behavior #3. pass will attempt to extract the int
value at this "address" (step 5 above). Since the value at this new
address was an int and not an address to begin with, attempting to
dereference it should be an obvious no-no. This is probably where
your system decided enough was enough.
}
The assignment of int int x=**a in the main function works.

There is no a in main. Perhaps you meant
int x = **arr;

This would work because the compiler know arr is a 2D array. *arr is
identical to arr[0] which is a1D array. **arr is identical to *arr[0]
which is identical to arr[0][0] which is the first int in the 1D array
and a perfectly valid value to assign to the int x.
Tanks for your help,
Markus


Remove del for email
 
J

Jordan Abel

int main() is part of an an old style definition, or at least a
definition
that's been in style for a long time. ;)


Compare...

int foo() { }

int main()
{
foo(42); /* no diagnostic required */

False. The standard makes a very clear distinction between empty
brackets in a declaration not part of a definition, and empty brackets
that _are_ in part of a definition. And the latter is EXACTLY equivalent
to (void).
 
J

jacob navia

Jordan Abel a écrit :
While void main() is a lot of things (none of them good), it is not an
old-style definition. Warning on empty brackets might make sense for
a declaration without a definition, it does NOT make sense for
a definition.
Interesting. Maybe you know where in the standard that is
stated?

Thanks


You did not answer my question.

jacob
 
L

lovecreatesbeauty

Richard said:
arr's value is taken as the address of its first element. Its first element
is an int[2] array, so the address of its first element has type int
(*)[2], which is not the same as int **.

But following the char **argv (right? or char *argv[]) can be regarded
as char argv[][]. No runtime errors occur upon it, just an early
compiling warning is issued.

#include <stdio.h>

int
/*main(int argc, char *argv[]) */ /* a */
/*main(int argc, char **argv) */ /* b */
main(int argc, char argv[][]) /* c */
{
char ch = **argv;
printf("ch: %c", ch);
}

/* a */
$ gcc -W -Wall -std=c99 -pedantic test.c
test.c: In function `main':
test.c:4: warning: unused parameter `argc'
$ ./a.out
ch: .$

/* b */
$ gcc -W -Wall -std=c99 -pedantic test.c
test.c: In function `main':
test.c:5: warning: unused parameter `argc'
$ ./a.out
ch: .$

/* c */
$ gcc -W -Wall -std=c99 -pedantic test.c
test.c:6: warning: array type has incomplete element type
test.c:7: warning: second argument of `main' should be `char **'
test.c: In function `main':
test.c:6: warning: unused parameter `argc'
$ ./a.out
ch: i$
$
 
R

Richard Heathfield

jacob navia said:
Richard Heathfield a écrit :

lcc-win32 has:
Basically all warnings are the same, with wording differences.

Hardly surprising, for such a short program.

The first warning of gcc however, is not clear to me:

foo.c:2: warning: no previous prototype for `pass'

Why is that an eror?

Who said it was an error? It's a diagnostic message, that's all. Compilers
are free to provide any diagnostic messages they like, whenever they like,
so long as they provide at least one diagnostic message if the input source
contains any syntax errors or constraint violations.

In this case, the message appears because I asked for it, with the
-Wstrict-prototypes flag to gcc.

I really, really should not be having to explain this Janet and John stuff
to a C compiler writer.
 
J

jacob navia

Richard Heathfield a écrit :
jacob navia said:




Who said it was an error? It's a diagnostic message, that's all. Compilers
are free to provide any diagnostic messages they like, whenever they like,
so long as they provide at least one diagnostic message if the input source
contains any syntax errors or constraint violations.

In this case, the message appears because I asked for it, with the
-Wstrict-prototypes flag to gcc.

You did NOT nclude the flags you passed to the compiler in that message,
so I could not know them.

Now it is clear.
 
R

Richard Heathfield

lovecreatesbeauty said:
Richard said:
arr's value is taken as the address of its first element. Its first
element is an int[2] array, so the address of its first element has type
int (*)[2], which is not the same as int **.

But following the char **argv (right? or char *argv[]) can be regarded
as char argv[][].

No, there is no such type as char argv[][].
No runtime errors occur upon it, just an early
compiling warning is issued.

"just"? What planet are you on? Your compiler is saying:

* * * *
* * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * *
* * * * * * * *

LLL OOOOO OOOOO KKK KKK !!!
LLL OOO OOO OOO OOO KKK KKK !!!
LLL OOO OOO OOO OOO KKK KKK !!!
LLL OOO OOO OOO OOO KKKKKK !!!
LLL OOO OOO OOO OOO KKK KKK
LLLLLL OOO OOO OOO OOO KKK KKK !!!
LLLLLL OOOOO OOOOO KKK KKK !!!

* * * *
* * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * *
* * * * * * * *


and you're saying it's "just" a warning?

If you want to learn to be a real C programmer, one vital step on that road
is to crank up your warning level to the max. Once you've done that, learn
to *understand* the diagnostic messages your compiler produces.

In this case, the compiler is saying "WTH is char [][]?" and it is quite
right, for there is no such type. And if there is no such type, your code
should not refer to such a type, should it?

SO FIX YOUR CODE!
 
R

Richard Heathfield

jacob navia said:
Richard Heathfield a écrit :

You did NOT nclude the flags you passed to the compiler in that message,
so I could not know them.

So what? You should still know about the circumstances in which messages can
be produced, and you should still know that "diagnostic message" is not the
same as "error". That was all the information you needed to have at your
disposal to avoid asking the question "Why is that an eror?"

Let me get this straight - you didn't actually /write/ the core code for
lcc-win32, did you? Because if you did, I think it's time for me to start
warning people not to use it.
 
F

Flash Gordon

lovecreatesbeauty said:
Richard said:
arr's value is taken as the address of its first element. Its first element
is an int[2] array, so the address of its first element has type int
(*)[2], which is not the same as int **.

But following the char **argv (right? or char *argv[]) can be regarded
as char argv[][].

No. At least, no more than you can regard a hungry alligator as a fluffy
toy. You might stoke it, it might not do anything, but it might take a
very large bite out of you.
> No runtime errors occur upon it, just an early
compiling warning is issued.

#include <stdio.h>

int
/*main(int argc, char *argv[]) */ /* a */
/*main(int argc, char **argv) */ /* b */
main(int argc, char argv[][]) /* c */
{
char ch = **argv;
printf("ch: %c", ch);
}

/* a */
$ gcc -W -Wall -std=c99 -pedantic test.c
test.c: In function `main':
test.c:4: warning: unused parameter `argc'
$ ./a.out
ch: .$

/* c */
$ gcc -W -Wall -std=c99 -pedantic test.c
test.c:6: warning: array type has incomplete element type

The above compiler warning says your program is not valid C. The
compiler could (and IMHO should) have aborted compilation at that point.
However, the standard only requires a diagnostic and then allows the
compiler to do anything it wants.
test.c:7: warning: second argument of `main' should be `char **'

Again, the compiler is telling you that you have got it wrong. How many
times do you have to be told something is wrong before you believe it?
test.c: In function `main':
test.c:6: warning: unused parameter `argc'
$ ./a.out
ch: i$
$

Here, if you actually look, you will see the program output is *not*
what you get from the correct program. So obviously with you three times
is not the charm.

Arrays are not pointers and pointers are not arrays. In particular, as
Richard clearly explained, a char ** parameter and a 2D array are
completely different beasts.
 
J

jaysome

Richard Heathfield a écrit :

lcc-win32 has:
D:\lcc\mc66\test>lcc -A tw2.c
Warning tw2.c: 3 x is assigned a value that is never used
Warning tw2.c: 6 old-style function definition for 'main'
Warning tw2.c: 6 missing prototype for 'main'
Warning tw2.c: 6 'void main()' is a non-ANSI definition
Warning tw2.c: 8 assignment of pointer to array 2 of int to pointer to
pointer to int
0 errors, 5 warnings

And even the Microsoft VC++ compiler, cranked up all the way to
warning level 4, complains:

stdc.c(8) : warning C4047: 'function' : 'int ** ' differs in levels of
indirection from 'int [2][2]'
stdc.c(8) : warning C4024: 'pass' : different types for formal and
actual parameter 1

Conspicuously missing is the warning about "void main()". Microsoft
likes to accommodate those who would tempt to risk the wretched wrath
of undefined behavior. Keep in mind that the VC++ compiler only runs
on Windows, though.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,677
Members
48,796
Latest member
Greg L.

Latest Threads

Top