2
#include <iostream>
#include <cstring>

using namespace std;

int main(){

    char a[] = "abc";
    char b[2];
    for(int i = 0,k = 2;i < 3;i++,k--){
        b[k] = a[i];
        cout << i << " " << k << endl;
    }
    if(strcmp(a,b) == 0){
        cout << "palindrome";
    }else{
        cout << "no palindrome" << endl;
    }
    cout << "a: " << a << endl;
    cout << "b: " << b << endl;

    return 0;
}

output:

0 2 

1 1

2 0

no palindrom

a: abc

b: cbabc

I don't understand why b array ends up with 5 elements, when the array holds only 3. Additionally, the loop loops only 3 times and this is the output I get.... A mystery.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
Grimnack
  • 29
  • 1
  • 5
  • 1
    Pro tip: https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems – πάντα ῥεῖ Dec 21 '20 at 22:07
  • 2
    You write out of bounds of the arrays. `char b[2]` means it has valid indices 0 and 1 but you start by writing to `b[2]` – M.M Dec 21 '20 at 22:11

4 Answers4

2

You have an out-of-bounds array access and also need to be conscious of null-terminating your strings!

Specifically, char b[2]; gives you an array with exactly 2 chars, so only b[0] and b[1] are valid. You also need to account for the null character that should terminate all C-style strings. So to hold "cba" for example you need 4 elements. You can also see this if you print sizeof(a) (should be 4: 'a', 'b', 'c', '\0').

Basically, your program elicits undefined behavior (UB). The simple fix is to make b bigger (the same size as a, which is 4 in this case). The more complete answer is to manage your array lengths more carefully and look at the safer "n" versions of the C manipulation functions such as strncmp

Edit: to be complete, you have 2 sourced of UB. The first is in line b[k] = a[i] when k == 2 because again you have only allocated b[0] and b[1]. The second is when you call strcmp since b has not been properly null-terminated and strcmp will happily read past the array bounds, which it doesn't know.

Luis
  • 1,210
  • 2
  • 11
  • 24
  • 2
    `strncmp` is not “safer” than `strcmp`, just different. And don’t get me started on `strncpy` Nevertheless, +1. – Pete Becker Dec 21 '20 at 22:31
  • fair point! A gun with a safety is still really effective at shooting feet! – Luis Dec 21 '20 at 22:41
  • 1
    It’s not that. For example, `strcpy` will always put a nul terminator at the end of the copy. `strncpy` will quietly leave off the nul terminator if it hits the character count. That’s not a safety; it’s just a different way to corrupt your data. – Pete Becker Dec 21 '20 at 23:00
1

b is not terminated by a null character (\0), so any string operation on it (like strcmp, or even just printing it with cout runs over until it happens to hit such a character somewhere in the memory. In other words, you are witnessing undefined behavior.

Mureinik
  • 297,002
  • 52
  • 306
  • 350
1

Ok, when debugging this you can check for address of b[2].

In gdb:

(gdb) p &b[1]
$8 = 0x7fffffffdfe3 "\377abc"

See? If b was null terminated it would start with '\0', but it doesn't, you tell the compiler to use 2 spaces for b. When asked the debugger what's the address of last b character b[1], it not only tells the address, it also shows the char* value represented. As b is a non null terminated (my compiler didn't initialize it), it will continue beyond the boundaries of b!. Suspiciously enough the string of characters finishes with 'a''b''c''\0'. Let's check address of a[0]:

(gdb) p &a[0]
$9 = 0x7fffffffdfe4 "abc"

See? The a field pointed by b is contiguous to a. Now you are making two mistakes here:

  1. You are not properly initializing b.
  2. b reserves 2 slots of memory. If you want to check palindromes of a fixed size of 3 characters you should reserve 4 slots like you did for the null terminated string "abc".

Try changing b declaration from:

char b[2];

To:

char b[] = "xyz";

Your initialization code will set the palindrome as a function of a, so it would do what you intend to.

CPPDVL
  • 61
  • 3
1

Strictly speaking you have undefined behaviour and any observed behaviour (wrong or seemingly partially correct) is explained by that.
For details and solutions see the other answers.
End of answer.

Now lets look at a speculation on why you might in your environment end up with specifically the output you observe.

Assumption, the memory for your arrays

char a[] = "abc";
char b[2]

looks like an often seen habit of linkers of how to arrange variables:

b[0] non-initialised
b[1] non-initialised
a[0] = 'a'
a[1] = 'b'
a[2] = 'c'
a[3] = '\0' 

Note the four (not three) elements of a and the terminator 0.

Your loop, right in the first iteration, attempts to write to the non-existing b[2]. This is already what causes undefined behaviour. Clean discussion ends here.
Let's continue speculating.

Your loop unintentionally writes one place beyond the existing b[1] and ends up clobbering a[0]. By chance it writes the value which happens to be already there, so no change there.

Your loop continues to write, now to existing entries of b.
The speculated result is

b[0] = 'c
b[1] = 'b' 
a[0] = 'a' = 'a'
a[1] = 'b'
a[2] = 'c'
a[3] = '\0' 

and the loop ends.

Then you try to output a and b.
This is done by outputting all characters found consecutively from the start of the arrays, until a terminator 0 is found.

For a this (luckily in case of the "a") is "abc\0", all from a.
For b this is "bc" from b, followed (on the search for a 0) by "abc\0" from a.
Note that the seemingly correct "a" already is incorrectly from a, not from b.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54