> > On 01/03/2010 04:54 AM, Andris Kalnozols wrote: > > > Since multiple assignments are legal and evaluated from right > > to left, one could expect the following to work: > > > > pcptr->code = pcptr = nop; > > On 01/03/2010 11:17 AM, Andrew Haley replied: > > I wouldn't. :-) OK, the problem was found, not by Valgrind nor Coverity, but the old fashioned method of inserting debugging statements in the code. There are no coding nor compilation errors with either pcptr = pcptr->code = nop; or pcptr->code = pcptr = nop; As long as the pointers are actually pointing to something, this is typical linked list processing. There is a problem, however, when a global pointer structure is the assignment target of a function which itself may modify the global pointer. The included test program gives the following output when compiled with gcc {3,4}.X: ./gcc-bug Take the suspect branch [1] or stay `safe' [2]? 1 Initialize `pcptr' with NULL [1] or malloc [2]? 1 main: [init] pcptr = (nil) Taking branch 1. fnc_A: pcptr = 0x8fff008 pcptr->code = (nil) Segmentation fault ./gcc-bug Take the suspect branch [1] or stay `safe' [2]? 1 Initialize `pcptr' with NULL [1] or malloc [2]? 2 main: [init] pcptr = 0x8c39008 Taking branch 1. fnc_A: pcptr = 0x8c39018 pcptr->code = (nil) main: pcptr = 0x8c39018 (0x8c39018 <- should be this) pcptr->code = (nil) (0x8c39018 <- should be this) Needless to say, the nature and duration of this behavior through the 3.X and current versions of gcc certainly seems to go against the Principle of Least Astonishment. ------ Andris ...................................................................... #include <stdio.h> #include <stdlib.h> #include <errno.h> #define PC struct pc typedef unsigned short word; PC { /* typical linked list structure */ word op; PC *code; }; PC *pcptr; /* global structures */ PC *pcptr_copy; int get_data(char *prompt) { char buf[40]; int result, *result_ptr; result_ptr = &result; do { fputs(prompt, stdout); fgets(buf, sizeof(buf), stdin); sscanf(buf, "%i", result_ptr); } while (result != 1 && result != 2); return(result); } PC *fnc_A(void) { if ((pcptr = pcptr_copy = (PC *)malloc(sizeof(PC))) == NULL) { perror("malloc(3) failed"); exit(1); } pcptr->code = NULL; printf("fnc_A: \tpcptr = %p\n", (void *)pcptr); printf(" \tpcptr->code = %p\n", (void *)pcptr->code); return(pcptr); } int main(int argc, char *argv[]) { int init_choice, branch; branch = get_data("Take the suspect branch [1] or stay `safe' [2]? "); init_choice = get_data("Initialize `pcptr' with NULL [1] or malloc [2]? "); puts(""); if (init_choice == 1) pcptr = NULL; else { if ((pcptr = (PC *)malloc(sizeof(PC))) == NULL) { perror("malloc(3) failed"); exit(1); } } printf("main: [init] pcptr = %p\n\n", (void *)pcptr); printf("Taking branch %i.\n", branch); if (branch == 1) /* * This branch works fine with the gcc-2.95 and HP-UX compilers. * If compiled with gcc {3,4}.X, however, the results are: * * > SEGFAULT if `pcptr' is initialized to NULL * or * > Corrupted value of `pcptr->code' if `pcptr' * is initialized to a non-NULL address. * * The newer compilers appear to incorrectly handle the case * where the assignment target is a global pointer reference * (pcptr->code) and the value being assigned is returned by * a function which can modify the global pointer itself. */ pcptr->code = fnc_A(); else { /* * All compilers can handle this branch correctly * regardless of how `pcptr' is initialized. */ PC *tmp_ptr; tmp_ptr = fnc_A(); pcptr->code = tmp_ptr; } printf("main: \tpcptr = %p", (void *)pcptr); printf("\t(%p <- should be this)\n", (void *)pcptr_copy); printf(" \tpcptr->code = %p", (void *)pcptr->code); printf("\t(%p <- should be this)\n", (void *)pcptr_copy); return(0); }