Home > database >  Simple C code returns different outputs on different compilers
Simple C code returns different outputs on different compilers

Time:08-20

I wrote a program in C that receives 2 strings as input and checks if one is the inverse of the other.

  #include<iostream>
  #include<string>
  
  int main() {
    std::string word, word_inv;
    std::cin >> word; 
    std::cin >> word_inv;
    
    int n=word.size(), i=0;
    while(word_inv[i] == word[n-i-1])
      i  ;
      
    std::cout << ((i == n) ? "YES" : "NO") << std::endl;
  }

However it gives different results on different compilers. For the input tests: code edoc it prints "YES" on my computer but "NO" in the other.

I ran g -std=c 17 -Wshadow -Wall -o bin/program src/prog.cpp -fsanitize=address -fsanitize=undefined -D_GLIBCXX_DEBUG -g and this is the output:

/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c  /v1/string:3341:30: runtime error: addition of unsigned offset to 0x00016ce73760 overflowed to 0x00016ce7375f
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c  /v1/string:3341:30 in 
=================================================================
==7114==ERROR: AddressSanitizer: stack-buffer-underflow on address 0x00016ce7375f at pc 0x000102f8d750 bp 0x00016ce73730 sp 0x00016ce73728
READ of size 1 at 0x00016ce7375f thread T0
    #0 0x102f8d74c in main 41A.cpp:12
    #1 0x188a8542c in start 0x0 (libdyld.dylib:arm64e 0x1842c)

Address 0x00016ce7375f is located in stack of thread T0 at offset 31 in frame
    #0 0x102f8d428 in main 41A.cpp:6

  This frame has 2 object(s):
    [32, 56) 'word' (line 7) <== Memory access at offset 31 underflows this variable
    [96, 120) 'word_inv' (line 7)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C   exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-underflow 41A.cpp:12 in main
Shadow bytes around the buggy address:
  0x00702d9ee690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x00702d9ee6a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x00702d9ee6b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x00702d9ee6c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x00702d9ee6d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x00702d9ee6e0: 00 00 00 00 00 00 00 00 f1 f1 f1[f1]00 00 00 f2
  0x00702d9ee6f0: f2 f2 f2 f2 00 00 00 f3 f3 f3 f3 f3 00 00 00 00
  0x00702d9ee700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x00702d9ee710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x00702d9ee720: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x00702d9ee730: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==7114==ABORTING

CodePudding user response:

while(word_inv[i] == word[n-i-1])

This condition requires word_inv[i] to not be equal to word[n-i-1] for the loop to end. What if that doesn't happen while the indices are within bounds?

What happens then is that the program tries to read memory out of bounds with undefined behavior as a result, which means that anything could happen. It could look like it's working while compiling with one compiler but not another. The apparent look that it's working could also stop the next day even if you use the same compiler as the day before.

You need to make sure that i and n-i-1 are within bounds. If they are not, exit the loop.


Something that might help while developing these things is to use the bounds checking accessors. Many standard classes that provides operator[], like std::string, also provide the member function at. Using that in your program would look like this:

while (word_inv.at(i) == word.at(n - i - 1)) i  ;

This will throw an exception (independent of which compiler you use) and the exception message for at is often very informative.

CodePudding user response:

When do you stop checking characters in each word?

If you have CODE and EDOC, then you check char 0 against char 3, then 1 v 2, then 2 v 1, then 3 v 0, then 4 v -1... oops, you've overflowed your strings. I assume you run it in release build that has no bounds checking on tyhe computer where it works and you are accidentally seeing garbage being compared to different garbage that stops the check.

  • Related