Strange problem after changing local variable of caller in the callee !!!

N

neojia

hi,

I encountered such a strange problem and worked on it for days.
Although I can see what is going on in my code, I still cannot provide
a perfect solution for this problem.

The problem happens when you are going to assign the return value of
function F2 to a local variable V1 of function F1. F1 calls F2. And V1
will points another chunk of memory by calling function F3, which will
be called inside F2. The return value is actrually assigned to the
original address before calling F2.

I think compiler should allow callee to modify the caller's local
variable as they want and also use it as a left-hand variable of the
assignment of the return function.

The following chunk of code also has the same problem.

Does anyone encounter such problem before? Is there any perfect
solution?

Thanks,
Neo


#include <stdio.h>
#include <malloc.h>
#include <string.h>

unsigned long * Root[1];

struct STR {

int size;

unsigned long * data[1];

};

void bar() {

if (Root[0] != NULL) {
*Root[0] = (unsigned long)malloc(sizeof(struct STR) + 5 *
sizeof(char *));

printf("Root changes to 0x%[%08lx]\n", *Root[0]);
}

}

unsigned long * foo() {

unsigned long * ch = (unsigned long *)malloc(sizeof(32));

bar();

if (ch != NULL) {
printf("foo returns address 0x[%08lx]\n", ch);
return ch;
}

}

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

struct STR * str_ptr;

int len = 5, i = 0;

str_ptr = malloc(sizeof(struct STR) + 5 * sizeof(char *));

memset(str_ptr, 0, sizeof(struct STR) + 5 * sizeof(char *));

Root[0] = &str_ptr;

for (i = 0; i < len; i++) {
unsigned long * tmp = NULL;
printf("************** Loop %d***************\n", i);
printf("Before calling foo(), str_ptr points 0x[%08lx]\n",
str_ptr);
printf("Before calling foo(), str_ptr->data[%d] points to
%08lx\n", i, str_ptr->data);
tmp = &(str_ptr->data);
printf("Before calling foo(), str_ptr->data[%d] address is
%08lx\n", i, tmp);
str_ptr->data = foo();
printf("After calling foo(), str_ptr points 0x[%08lx]\n",
str_ptr);
printf("After calling foo(), str_ptr->data[%d] points to
%08lx\n", i, str_ptr->data);
printf("After calling foo(), data at address %08lx is %08lx\n",
tmp, *tmp);
}
 
R

Rod Pemberton

hi,

I encountered such a strange problem and worked on it for days.
Although I can see what is going on in my code, I still cannot provide
a perfect solution for this problem.

The problem happens when you are going to assign the return value of
function F2 to a local variable V1 of function F1. F1 calls F2. And V1
will points another chunk of memory by calling function F3, which will
be called inside F2. The return value is actrually assigned to the
original address before calling F2.

I'm confused by this. But, I think I understand from your program.
I think compiler should allow callee to modify the caller's local
variable as they want and also use it as a left-hand variable of the
assignment of the return function.

The following chunk of code also has the same problem.

Does anyone encounter such problem before? Is there any perfect
solution?

I think the main problem is the way you defined Root.

Try out the following modified version of your program to see if it works
correctly.

Rod Pemberton

//----
#include <stdio.h>
#include <malloc.h>
#include <string.h>

//no array unsigned long * Root[1];
unsigned long * Root;

struct STR {

int size;

unsigned long * data[1];

};

void bar() {

//no array if (Root[0] != NULL) {
if (Root != NULL) {
//no cast *Root[0] = (unsigned long)malloc(sizeof(struct STR) + 5 *
sizeof(char *));
Root = malloc(sizeof(struct STR) + 5 * sizeof(char *));
//extra % printf("Root changes to 0x%[%08lx]\n", *Root[0]);
printf("Root changes to 0x[%08lx]\n", (unsigned long)Root);
}

}

unsigned long * foo() {

unsigned long * ch = (unsigned long *)malloc(sizeof(32));

bar();

if (ch != NULL) {
//cast printf("foo returns address 0x[%08lx]\n", ch);
printf("foo returns address 0x[%08lx]\n", (unsigned long)ch);
//misplaced return ch;
}
return ch;

}

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

struct STR * str_ptr;

int len = 5, i = 0;

str_ptr = malloc(sizeof(struct STR) + 5 * sizeof(char *));

memset(str_ptr, 0, sizeof(struct STR) + 5 * sizeof(char *));

//cast,no array,no address Root[0] = &str_ptr;
Root = (unsigned long *)str_ptr;

for (i = 0; i < len; i++) {
unsigned long * tmp = NULL;
printf("************** Loop %d***************\n", i);
//cast printf("Before calling foo(), str_ptr points 0x[%08lx]\n",
str_ptr);
printf("Before calling foo(), str_ptr points 0x[%08lx]\n", (unsigned
long)str_ptr);
//cast printf("Before calling foo(), str_ptr->data[%d] points to
%08lx\n", i, str_ptr->data);
printf("Before calling foo(), str_ptr->data[%d] points to %08lx\n",
i, (unsigned long)str_ptr->data);
//cast tmp = &(str_ptr->data);
tmp = (unsigned long *)&(str_ptr->data);
//cast printf("Before calling foo(), str_ptr->data[%d] address is
%08lx\n", i, tmp);
printf("Before calling foo(), str_ptr->data[%d] address is %08lx\n",
i, (unsigned long)tmp);
str_ptr->data = foo();
//cast printf("After calling foo(), str_ptr points 0x[%08lx]\n",
str_ptr);
printf("After calling foo(), str_ptr points 0x[%08lx]\n", (unsigned
long)str_ptr);
//cast printf("After calling foo(), str_ptr->data[%d] points to
%08lx\n", i, str_ptr->data);
printf("After calling foo(), str_ptr->data[%d] points to %08lx\n",
i, (unsigned long)str_ptr->data);
//cast printf("After calling foo(), data at address %08lx is
%08lx\n", tmp, *tmp);
printf("After calling foo(), data at address %08lx is %08lx\n",
(unsigned long)tmp, *tmp);
}

//missing return(0);
return(0);
}
 
A

Al Balmer

//no array if (Root[0] != NULL) {
if (Root != NULL) {
//no cast *Root[0] = (unsigned long)malloc(sizeof(struct STR) + 5 *
sizeof(char *));
Root = malloc(sizeof(struct STR) + 5 * sizeof(char *));
//extra % printf("Root changes to 0x%[%08lx]\n", *Root[0]);
printf("Root changes to 0x[%08lx]\n", (unsigned long)Root);
}
Please don't use "//" comments in newsgroups.
 
P

Pedro Graca

void bar() {

if (Root[0] != NULL) {
*Root[0] = (unsigned long)malloc(sizeof(struct STR) + 5 *
sizeof(char *));

Oops! I think ...
Shouldn't that be the opposite?

if (Root[0] == NULL) {
/* malloc */
/* printf */
}
printf("Root changes to 0x%[%08lx]\n", *Root[0]);
}
<snip>
 
K

Keith Thompson

Pedro Graca said:
void bar() {

if (Root[0] != NULL) {
*Root[0] = (unsigned long)malloc(sizeof(struct STR) + 5 *
sizeof(char *));

Oops! I think ...
Shouldn't that be the opposite?

if (Root[0] == NULL) {
/* malloc */
/* printf */
}
printf("Root changes to 0x%[%08lx]\n", *Root[0]);
}
<snip>

No. If Root[0] is equal to NULL, assigning to *Root[0] invokes
undefined behavior. (I don't know anything about the context of the
code; in particular, there shouldn't be a cast on the result of
malloc().)
 
P

Pedro Graca

Keith said:
Pedro Graca said:
if (Root[0] != NULL) {
*Root[0] = (unsigned long)malloc(sizeof(struct STR) + 5 *
sizeof(char *));

Oops! I think ...
Shouldn't that be the opposite?

if (Root[0] == NULL) {
/* malloc */
<snip>

No. If Root[0] is equal to NULL, assigning to *Root[0] invokes
undefined behavior.

Yes, of course. *smacks head* Thank you for pointing my mistake.
I failed to notice that Root[0] was being dereferenced on the malloc line.
 
C

CBFalconer

.... snip ...

The following chunk of code also has the same problem.

Does anyone encounter such problem before? Is there any perfect
solution?

#include <stdio.h>
#include <malloc.h>

Nobody seems to have pointed out that there is no such standard
include file as malloc.h. To access malloc, realloc, free etc use
<stdlib.h>

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
Also see <http://www.safalra.com/special/googlegroupsreply/>
 
C

Chris Torek

[snipped to just the code]

This code does not even compile, much less have much meaning:
#include <stdio.h>
#include <malloc.h>

Not a standard header. (Presumably you mean to include said:
#include <string.h>

unsigned long * Root[1];

Ok, "Root" is an array with one element. The only element, Root[0],
has type "pointer to unsigned long".
struct STR {
int size;
unsigned long * data[1];
};
void bar() {

if (Root[0] != NULL) {
*Root[0] = (unsigned long)malloc(sizeof(struct STR) + 5 *
sizeof(char *));

This does not do all that much useful, since unsigned long is 8 bytes
while pointers are 4 bytes -- or, perhaps, unsigned long is 4 bytes
while pointers are 8 bytes, depending on your machine.
printf("Root changes to 0x%[%08lx]\n", *Root[0]);

"%[" is not a valid printf() format specifier.
}
}

unsigned long * foo() {
unsigned long * ch = (unsigned long *)malloc(sizeof(32));

The cast is unnecessary, and will mask the mistake of not including
<stdlib.h>.

sizeof(32) is sizeof(int), and int is only 2 bytes (or is it 4?)
while long is 8 bytes (or is it 4?). "ch" should probably point
to the first of at least one "unsigned long"s:

unsigned long *ch = malloc(sizeof *ch);
bar();
if (ch != NULL) {
printf("foo returns address 0x[%08lx]\n", ch);

%lx prints an "unsigned long" but "ch" has type "pointer to
unsigned long". Either set *ch to something and print that,
or use the %p format and print a value of type "void *":

printf("foo returns address %p\n", (void *)ch);
return ch;
}
}

If ch==NULL, foo() "falls off the end" and fails to return a
value. If the non-returned value is used by the caller, the
behavior is undefined.
int main(int argc, char ** argv) {
struct STR * str_ptr;
int len = 5, i = 0;

str_ptr = malloc(sizeof(struct STR) + 5 * sizeof(char *));

This call is OK. It looks like you intend to use the "struct hack",
however; the struct hack is, at least formally speaking, not valid
C.
memset(str_ptr, 0, sizeof(struct STR) + 5 * sizeof(char *));

This call is not *wrong* per se, but not particularly useful.
Root[0] = &str_ptr;

This line does not compile: &str_ptr has type "pointer to pointer
to struct STR", while Root[0] has type "pointer to long". A diagnostic
is required (after which compilation may terminate, and if you use
the right compiler flags, it probably will).

If the compiler produces an executable despite the diagnostic, the
behavior of that executable is not defined.
for (i = 0; i < len; i++) {
unsigned long * tmp = NULL;
printf("************** Loop %d***************\n", i);

This part is OK.
printf("Before calling foo(), str_ptr points 0x[%08lx]\n",
str_ptr);

%lx prints an unsigned long, but str_ptr has type "pointer to
struct STR". The effect is undefined.
printf("Before calling foo(), str_ptr->data[%d] points to
%08lx\n", i, str_ptr->data);


Again, %lx is the wrong format. In addition, str_ptr->data is
valid only if i==0 and only if it has been properly initialized. It
was set to all-zero-bytes by the memset(), which is not necessarily
a valid pointer value (and not necessarily NULL either, although
in practice it will be).
tmp = &(str_ptr->data);


"tmp" has type "unsigned long *", as does str_ptr->data, so this
means that &str_ptr->data has type "unsigned long **", rather
than type "unsigned long *". This line also does not compile and
a diagnostic is required (a single diagnostic suffices to cover
both of the errors that require one, but in practice you will get
one for each such error).
printf("Before calling foo(), str_ptr->data[%d] address is
%08lx\n", i, tmp);

Again, %lx is the wrong format.
str_ptr->data = foo();


If foo() "falls off the end", its return value is unpredictable and
the behavior here is not defined.
printf("After calling foo(), str_ptr points 0x[%08lx]\n",
str_ptr);
printf("After calling foo(), str_ptr->data[%d] points to
%08lx\n", i, str_ptr->data);
printf("After calling foo(), data at address %08lx is %08lx\n",
tmp, *tmp);


Again, the wrong format (%lx) is used to print pointers. (At least
*tmp has type unsigned long, though.)

The biggest problems here are all the type mismatches. Because
the types do not match, the effect of the code is unpredictable,
and it will in fact misbehave on a number of real machines (especially
those with multiple pointer formats, such as a PR1ME or Eclipse;
but even on such rare machines as the x86-64 you may find yourself
in trouble).

Fixing the type mismatches will help a great deal; then the main
remaining bug is that sizeof(32) is sizeof(int). If you use the
following idiom for malloc():

ptr = malloc(N * sizeof *ptr);
/* with "N *" being optional */

you will avoid this error.
 
N

neojia

Rod,

Thanks. But in fact, my purpose is to modify the local variable
(pointer) in the caller in the callee. So if the code changed to

/* cast,no array,no address Root[0] = &str_ptr; */
Root = (unsigned long *)str_ptr;

Root will only have the value of str_ptr. So it will not change the
value of that local variable str_ptr.

Thanks,
Neo
 
N

neojia

I don't think it should be a problem in this program at least since the
Root[0] points to the address of local variable str_ptr at caller's
stack.


Thanks,
Neo
 
N

neojia

It seems that I need to clarify my problem here.

My problem is the value of the str_ptr ( a local variable of the caller
function) will be changed through a global variable in the callee
function. And also the callee function will have some other return
value , which should be assigned to a member of the structure pointed
by str_ptr.

If you can compile/execute my code, you will get the idea. Since there
is nothing assigned to the new address of the member of str_ptr,
instead, the orginal address stores the return value of the callee
function.

I even tried the volatile keyword, but still does not work.

Thanks,
Neo
 
N

neojia

All,

I modified my program a little bit by getting rid of all the warning
messages.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef unsigned long cell;

typedef union cellOrPointer {

cell cell;
cell *cellp;
cell **cellpp; /* For global roots */

} cellOrPointer;


union cellOrPointer Roots[1];

struct STR {

int size;

unsigned long * data[1];
};

void bar() {

*(Roots[0].cellpp) = (cell *)malloc(sizeof(struct STR) + 5 *
sizeof(char *));

printf("Root changes to 0x[%p]\n", *(Roots[0].cellpp));
}

unsigned long * foo() {

cell * ch = (cell *)malloc(sizeof(32));

bar();

if (ch != NULL) {
printf("foo returns address 0x[%p]\n", ch);
return ch;
}
else {
return NULL;
}
}

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

// Declare str_ptr as a volatile pointer pointing to struct STR
struct STR * str_ptr;

int len = 5, i = 0;

str_ptr = malloc(sizeof(struct STR) + 5 * sizeof(char *));

memset(str_ptr, 0, sizeof(struct STR) + 5 * sizeof(char *));

Roots[0].cellp = (cell *)&(str_ptr);

for (i = 0; i < len; i++) {
cell * tmp = NULL;
printf("************** Loop %d***************\n", i);
printf("Before calling foo(), str_ptr points 0x[%p]\n",
str_ptr);
printf("Before calling foo(), str_ptr->data[%d] points to
%p\n", i, str_ptr->data);
tmp = (cell *)&(str_ptr->data);
printf("Before calling foo(), str_ptr->data[%d] address is
%p\n", i, tmp);
str_ptr->data = foo();
/* ---------------------------------------------
tmp2 = foo();
str_ptr->data = tmp2;
--------------------------------------------- */
printf("After calling foo(), str_ptr points 0x[%p]\n",
str_ptr);
printf("After calling foo(), str_ptr->data[%d] points to %p
<--- It should be update, but not!!\n", i, str_ptr->data);
printf("After calling foo(), data at address %p is
0x[%08lx]\n", tmp, (cell)(*tmp));
/*
------------------------------------------------------------------------------------
* The data on the original address before calling foo() is
changed. So it seems
* that the complier "cached" the address in a register.
*
---------------------------------------------------------------------------------
*/
}

return (0);
}
 
N

neojia

Acturally, I just find a temporary workaround for this problem.

I first use gcc to compile the source code into .s file. Then add one
line intot he assembly code to fix this problem, since the compiler put
the variable into ESI before the function call foo.

call printf
addl $16, %esp
movl -12(%ebp), %esi
movl -20(%ebp), %ebx
call foo
movl -12(%ebp), %esi <--- I add this line to make a
workaround.
movl %eax, 4(%esi,%ebx,4)
subl $8, %esp
pushl -12(%ebp)
pushl $.LC6
call printf

This is absolutely not the perfect solution for this problem, I only
want to clarify the problem I encountered!

Thanks,
Neo
 
B

Ben Bacarisse

Roots[0].cellp = (cell *)&(str_ptr);

I don't think this does what you think it does. You probably don't want
the & in there. I say "probably" because with so many "tricks" being
played, who knows? Every time you use a pointer cast or a union like
yours you might be hiding a catastrophic error (and you certainly
obfuscate the code). Better to treat each variable as having the most
natural type for the rôle it plays and to "break" the type system with a
cast only when necessary and in as few places as possible.
 
R

Richard G. Riley

void bar() {

if (Root[0] != NULL) {
*Root[0] = (unsigned long)malloc(sizeof(struct STR) + 5 *
sizeof(char *));

Oops! I think ...
Shouldn't that be the opposite?

if (Root[0] == NULL) {
/* malloc */
/* printf */
}
printf("Root changes to 0x%[%08lx]\n", *Root[0]);
}
<snip>

Most certainly not :) Although the horrible mixture of array and
pointer syntax leaves a very nasty looking code block. Root[0] is a
pointer to a pointer by the looks of it. So his code is first testing
that a pointer IS being pointed to and then storing the pointer to the
new memory there.
 
D

Default User

I don't think it should be a problem in this program at least since
the Root[0] points to the address of local variable str_ptr at
caller's stack.

You don't think WHAT should be a problem? Did you notice how everyone
else was providing nice quotes so you could see who said what?
Reference the information below.



Brian
 
C

Chris Torek

I modified my program a little bit by getting rid of all the warning
messages.

But not the undefined behavior... However, this code is better as
it let me guess at what you intended.
typedef unsigned long cell;

typedef union cellOrPointer {
cell cell;
cell *cellp;
cell **cellpp; /* For global roots */
} cellOrPointer;

So a "cellOrPointer" can contain either:

- a single cell (an "unsigned long"), or
- a pointer of type "cell *", which therefore can point to
zero or more "cell"s, or
- a pointer of type "cell **", which can therefore point to
zero or more "cell *"s.

These are the only possibilities.
union cellOrPointer Roots[1];

So Roots is an array with one element, namely Roots[0], which
can contain any of those three things.
struct STR {
int size;
unsigned long * data[1];
};

The structure "struct STR" is intended to work with the "struct hack".
It contains one int "size" followed by at least one, and presumably
more, pointers of type "unsigned long *" (not "cell *", but "cell"
is an alias for "unsigned long" so this is OK, if bad style).
void bar() {

*(Roots[0].cellpp) = (cell *)malloc(sizeof(struct STR) + 5 *
sizeof(char *));

Hence, when bar() is called, Roots[0] must be initialized, and its
"cellpp" member must be the active member, which must point to a
valid "cell *" pointer. This "cell *" pointer is made to point
to the first of some number of bytes that are rather unrelated to
the number of bytes in some set of "cell *"s.

(The cast on the call to malloc() is not required but harmless.
The size passed to malloc() is not very useful.)
printf("Root changes to 0x[%p]\n", *(Roots[0].cellpp));

Undefined behavior: %p requires a value of type (void *). (No
diagnostic is required, because printf() is variadic.)
unsigned long * foo() {

cell * ch = (cell *)malloc(sizeof(32));

"ch" has type "pointer to cell", so it should point to (the first of)
one or more "cell"s, aka "unsigned long"s. The call to malloc()
asks for sizeof(int) "cell"s. If sizeof(int) is 2, but
sizeof(unsigned long) is 4, the resulting pointer will not be
useful.

I suspect this is not the number you want.

foo() has the same constraints as bar() since foo() calls bar().
if (ch != NULL) {
printf("foo returns address 0x[%p]\n", ch);

(Needs "void *" cast as before.)
return ch;
}
else {
return NULL;
}
}

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

// Declare str_ptr as a volatile pointer pointing to struct STR
struct STR * str_ptr;

int len = 5, i = 0;

str_ptr = malloc(sizeof(struct STR) + 5 * sizeof(char *));

The call to malloc() supplies suspicious arguments.
memset(str_ptr, 0, sizeof(struct STR) + 5 * sizeof(char *));

The call to memset() is not a good idea.
Roots[0].cellp = (cell *)&(str_ptr);

The behavior of this assignment is not defined. &str_ptr has
type "pointer to struct STR". This (valid) value is converted
to a new and different value of type "pointer to cell", and
assigned to Roots[0].cellp.

More code was here; I snipped it. Let me just say that it calls
foo(), which I noted earlier has the same constraints as bar();
they are not met since Roots[0].cellpp is not the active member
of the union.

I rewrote the entire thing. Consider the following carefully.
Note the lack of casts, except for printf "%p" arguments. The
function bar() is still not well-designed: the "magic constant"
5 appears out of nowhere, and if the malloc() call is used, the
result is not checked and not initialized. Fortunately the
malloc() never fires (bar() is now effectively a no-op).

No member of the union except "struct STR **strpp" is ever used
either, so the union could be removed entirely.

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct STR {
int size;
unsigned long * data[1]; /* actually longer ("size" elements) */
};

typedef unsigned long cell;

typedef union cellOrPointer {
cell cell;
cell *cellp;
cell **cellpp;
struct STR **strpp;
} cellOrPointer;


union cellOrPointer Roots[1];

void bar() {
struct STR *tmp;

assert(Roots[0].strpp != NULL);

if (*Roots[0].strpp == NULL) {
tmp = malloc(sizeof *tmp + 5 * sizeof tmp->data[0]);
*Roots[0].strpp = tmp;
/* XXX should initialize tmp->len and tmp->data[] a la main() */
printf("Root changes to %p\n", (void *)tmp);
}
}

unsigned long * foo() {
cell * ch = malloc(32 * sizeof *ch);

bar();
if (ch != NULL)
printf("foo returns address %p\n", (void *)ch);
return ch;
}

int main(void) {
struct STR * str_ptr;
int len = 5, i = 0;

str_ptr = malloc(sizeof *str_ptr + len * sizeof str_ptr->data[0]);
if (str_ptr == NULL) {
printf("malloc failed, aborting\n");
exit(EXIT_FAILURE);
}

str_ptr->size = len;
for (i = 0; i < len; i++)
str_ptr->data = NULL;
Roots[0].strpp = &str_ptr;

for (i = 0; i < len; i++) {
cell **tmp;

printf("************** Loop %d***************\n", i);
printf("Before calling foo(), str_ptr = %p\n", (void *)str_ptr);
printf("Before calling foo(), str_ptr->data[%d] = %p\n",
i, (void *)str_ptr->data);
tmp = &str_ptr->data;
printf("Before calling foo(), &str_ptr->data[%d] = %p\n",
i, (void *)tmp);
str_ptr->data = foo();
printf("After calling foo(), str_ptr = %p\n", (void *)str_ptr);
printf("After calling foo(), str_ptr->data[%d] = %p\n",
i, (void *)str_ptr->data);
printf("After calling foo(), address at address %p is %p\n",
(void *)tmp, (void *)*tmp);
}
return (0);
}
 
R

Rod Pemberton

All,

I modified my program a little bit by getting rid of all the warning
messages.
<snip>

FYI, I'm only seeing the problem with GCC. It works correctly for
OpenWatcom. The problem seems to be in bar(). If it is commented out, the
program appears to work correctly. And, returns results similar to Chris
Torek's changes.

Rod Pemberton
void bar() {

*(Roots[0].cellpp) = (cell *)malloc(sizeof(struct STR) + 5 *
sizeof(char *));

printf("Root changes to 0x[%p]\n", *(Roots[0].cellpp));
}
 
B

Barry Schwarz

hi,

I encountered such a strange problem and worked on it for days.
Although I can see what is going on in my code, I still cannot provide
a perfect solution for this problem.

If you get rid of the undefined behavior, things may improve.
The problem happens when you are going to assign the return value of
function F2 to a local variable V1 of function F1. F1 calls F2. And V1
will points another chunk of memory by calling function F3, which will
be called inside F2. The return value is actrually assigned to the
original address before calling F2.

I think compiler should allow callee to modify the caller's local
variable as they want and also use it as a left-hand variable of the
assignment of the return function.

The following chunk of code also has the same problem.

Does anyone encounter such problem before? Is there any perfect
solution?

Thanks,
Neo


#include <stdio.h>
#include <malloc.h>
#include <string.h>

unsigned long * Root[1];

What is the point of a 1 element array?
struct STR {

int size;

Why so much vertical white space? Do you really have a 90 line
monitor?
unsigned long * data[1];

};

void bar() {

if (Root[0] != NULL) {
*Root[0] = (unsigned long)malloc(sizeof(struct STR) + 5 *
sizeof(char *));

printf("Root changes to 0x%[%08lx]\n", *Root[0]);

Isn't the first % incorrect? %[ is not a valid format.
}

}

unsigned long * foo() {

unsigned long * ch = (unsigned long *)malloc(sizeof(32));

You should not cast the return from malloc when it is being assigned
to a pointer.
bar();

if (ch != NULL) {
printf("foo returns address 0x[%08lx]\n", ch);

This invokes undefined behavior. Your %08lx promises printf that the
corresponding argument is an unsigned long. You are passing a
pointer.
return ch;
}

}

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

struct STR * str_ptr;

int len = 5, i = 0;

str_ptr = malloc(sizeof(struct STR) + 5 * sizeof(char *));

memset(str_ptr, 0, sizeof(struct STR) + 5 * sizeof(char *));

Setting pointer variables to all bits 0 is not necessarily the same as
setting them to NULL.
Root[0] = &str_ptr;

This should have produced a diagnostic. &str_ptr is a pointer to
pointer to struct. Root[0] is a pointer to unsigned long. There is
no implicit conversion between the two types.
for (i = 0; i < len; i++) {
unsigned long * tmp = NULL;
printf("************** Loop %d***************\n", i);
printf("Before calling foo(), str_ptr points 0x[%08lx]\n",
str_ptr);

UB again.
printf("Before calling foo(), str_ptr->data[%d] points to
%08lx\n", i, str_ptr->data);


And again.
tmp = &(str_ptr->data);


data contains all bits zero. tmp probably does also.
printf("Before calling foo(), str_ptr->data[%d] address is
%08lx\n", i, tmp);

Another diagnostic required. str_ptr->data is an array of pointer to
unsigned long. data is the i-th pointer. &(str_ptr->data) is a
pointer to pointer to unsigned long. tmp is a pointer to unsigned
long. There is no implicit conversion between them.
str_ptr->data = foo();
printf("After calling foo(), str_ptr points 0x[%08lx]\n",
str_ptr);


If you want to print out pointer values, use %p and cast the pointer
to void*.
printf("After calling foo(), str_ptr->data[%d] points to
%08lx\n", i, str_ptr->data);
printf("After calling foo(), data at address %08lx is %08lx\n",
tmp, *tmp);


tmp still contains all bits zero. Attempting to dereference it
probably invokes undefined behavior.


Remove del for email
 
B

Ben Bacarisse

Roots[0].cellp = (cell *)&(str_ptr);

The behavior of this assignment is not defined. &str_ptr has type
"pointer to struct STR".

I don't think so. The original code does not compile (STR is not a
typedef'd name) so who knows what was intended but str_ptr is declared as
STR * str_ptr so &str_ptr is surely a pointer to pointer to (struct) STR,
no?

(This does not invaidate anything you have said here -- just pointing out
another problem.)
 

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
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top