-3

I'm overloading operator= in my class and I need to determine whether my string contains digits or not. Unfortunately, I need to use C arrays (char*) mainly, for 2 reasons:

  • Training
  • Using std::string will require me to change 100+ lines of code.

    When trying to pass a value from a char* array to my function, I get a really nice SEGFAULT and unfortunately I'm not sure why. Probably I'm not using correctly my pointer.

I was searching for hours but I wasn't able to find a solution to this problem. Anyways, here's the code. If you need further information regarding this matter, please let me know in the comments.

 #include <limits.h>
    #include <iostream>
    #include <string.h>
    #include <exception>

    struct notdig : public std::exception {
    virtual const char* what() const throw() override{
      return "int_huge: isdig(*(o + i)) returned false. \nYour declaration of an int_huge integer should contain decimal digits.";
    }
}; 
class int_huge {
    private:
        char* buffera;                   
        char* bufferb;
        int numDig(unsigned long long int number){ //Gets the number of digits
            int i = 0;
            while(number){
                number /= 10;
                i++;
            }
            return i;
        }
        inline bool isdig( char character ) { //Checks whether character is digit or not
            return ( '0' <= character && character <= '9' );
        }
    public:
        int_huge(){
           this->buffera = "0"; 
           this->bufferb = "0";
        }
        void operator=(char* operand){
            for (int i  = 0; i < strlen(operand); i++){
                if (!isdig(operand[i])){
                    throw notdig();
                }
            }
            if (strlen(operand) >= numDig(ULLONG_MAX)){
                if (strlen(operand) - numDig(ULLONG_MAX)){ //Equivalent with if (strlen(operand) != numDig(ULLONG_MAX)
                    int i = 0;
                    while (i < strlen(operand)-numDig(ULLONG_MAX)){
                        this->buffera[i] = operand[i];
                        i++;
                    }
                    this->bufferb = operand + i;
                } else {
                    this->buffera[0] = operand[0];
                    this->bufferb = operand + 1;
                }
            } else {
                this->buffera = "0";
                this->bufferb = operand;
            }
        }
    };




    int main() {
        int_huge object;
        try {
            object = "90";
        } catch (std::exception &e) {
            std::cout << e.what();
        }
    }

Disassembler results:

0x4019b4    push   %ebp
0x4019b5    mov    %esp,%ebp
0x4019b7    push   %ebx
0x4019b8    sub    $0x24,%esp
0x4019bb    mov    0xc(%ebp),%eax
0x4019be    mov    %eax,(%esp)
0x4019c1    call   0x401350 <strlen>
0x4019c6    mov    %eax,%ebx
0x4019c8    movl   $0xffffffff,0x4(%esp)
0x4019d0    movl   $0xffffffff,0x8(%esp)
0x4019d8    mov    0x8(%ebp),%eax
0x4019db    mov    %eax,(%esp)
0x4019de    call   0x40195c <int_huge::numDig(unsigned long long)>
0x4019e3    cmp    %eax,%ebx
0x4019e5    setae  %al
0x4019e8    test   %al,%al
0x4019ea    je     0x401aa8 <int_huge::int_huge(char*)+244>
0x4019f0    mov    0xc(%ebp),%eax
0x4019f3    mov    %eax,(%esp)
0x4019f6    call   0x401350 <strlen>
0x4019fb    mov    %eax,%ebx
0x4019fd    movl   $0xffffffff,0x4(%esp)
0x401a05    movl   $0xffffffff,0x8(%esp)
0x401a0d    mov    0x8(%ebp),%eax
0x401a10    mov    %eax,(%esp)
0x401a13    call   0x40195c <int_huge::numDig(unsigned long long)>
0x401a18    cmp    %eax,%ebx
0x401a1a    setne  %al
0x401a1d    test   %al,%al
0x401a1f    je     0x401a8d <int_huge::int_huge(char*)+217>
0x401a21    movl   $0x0,-0xc(%ebp)
0x401a28    mov    0xc(%ebp),%eax
0x401a2b    mov    %eax,(%esp)
0x401a2e    call   0x401350 <strlen>
0x401a33    mov    %eax,%ebx
0x401a35    movl   $0xffffffff,0x4(%esp)
0x401a3d    movl   $0xffffffff,0x8(%esp)
0x401a45    mov    0x8(%ebp),%eax
0x401a48    mov    %eax,(%esp)
0x401a4b    call   0x40195c <int_huge::numDig(unsigned long long)>
0x401a50    sub    %eax,%ebx
0x401a52    mov    %ebx,%edx
0x401a54    mov    -0xc(%ebp),%eax
0x401a57    cmp    %eax,%edx
0x401a59    seta   %al
0x401a5c    test   %al,%al
0x401a5e    je     0x401a7d <int_huge::int_huge(char*)+201>
0x401a60    mov    0x8(%ebp),%eax
0x401a63    mov    (%eax),%edx
0x401a65    mov    -0xc(%ebp),%eax
0x401a68    add    %eax,%edx
0x401a6a    mov    -0xc(%ebp),%ecx
0x401a6d    mov    0xc(%ebp),%eax
0x401a70    add    %ecx,%eax
0x401a72    movzbl (%eax),%eax
0x401a75    mov    %al,(%edx)        ;This is where the compiler stops. Probably due to SIGSEGV.
0x401a77    addl   $0x1,-0xc(%ebp)
0x401a7b    jmp    0x401a28 <int_huge::int_huge(char*)+116>
0x401a7d    mov    -0xc(%ebp),%edx
0x401a80    mov    0xc(%ebp),%eax
0x401a83    add    %eax,%edx
0x401a85    mov    0x8(%ebp),%eax
0x401a88    mov    %edx,0x4(%eax)
0x401a8b    jmp    0x401aba <int_huge::int_huge(char*)+262>
0x401a8d    mov    0x8(%ebp),%eax
0x401a90    mov    (%eax),%eax
0x401a92    mov    0xc(%ebp),%edx
0x401a95    movzbl (%edx),%edx
0x401a98    mov    %dl,(%eax)
0x401a9a    mov    0xc(%ebp),%eax
0x401a9d    lea    0x1(%eax),%edx
0x401aa0    mov    0x8(%ebp),%eax
0x401aa3    mov    %edx,0x4(%eax)
0x401aa6    jmp    0x401aba <int_huge::int_huge(char*)+262>
0x401aa8    mov    0x8(%ebp),%eax
0x401aab    movl   $0x4030d2,(%eax)
0x401ab1    mov    0x8(%ebp),%eax
0x401ab4    mov    0xc(%ebp),%edx
0x401ab7    mov    %edx,0x4(%eax)
0x401aba    nop
0x401abb    add    $0x24,%esp
0x401abe    pop    %ebx
0x401abf    pop    %ebp
0x401ac0    ret
  • Can you show the code that calls the assignment operator? – NathanOliver Feb 06 '17 at 19:10
  • 1) Did you try stepping through your code, with a debugger, while observing values of the variables? 2) Please provide [mcve] (note you **don't** need to provide all of your code - just manufacture minimal, but complete example). – Algirdas Preidžius Feb 06 '17 at 19:12
  • 5
    Post a [mcve], not a portion of a larger program (where the larger program is the one controlling the whole show). – PaulMcKenzie Feb 06 '17 at 19:12
  • 2
    @drescherjm if that were the case, you'd expect the crash to happen on the `strlen` before the first iteration occurs. But of course OP may be misdiagnosing the problem. – Mark Ransom Feb 06 '17 at 19:16
  • *Unfortunately, I need to use C arrays* -- Doesn't preclude you from using something shorter like -- `if (!std::all_of(o, o + strlen(o), ::isdigit)) throw;` – PaulMcKenzie Feb 06 '17 at 19:17
  • Ok .I'll provide Minimal, Complete and Verifiable example. Give me a moment. – Collier Demetrios Feb 06 '17 at 19:25
  • Using descriptive variable names will help you, us, and everybody who reads your code. – user4581301 Feb 06 '17 at 19:44
  • Ok. I just edited my post. Now it is as Minimal, Complete, and Verifiable example as possible. I'll post the output from the disassembly tool too. – Collier Demetrios Feb 06 '17 at 19:44
  • user4581301 Ok I'll edit my post right away – Collier Demetrios Feb 06 '17 at 19:45
  • What's a `notdig`? – user4581301 Feb 06 '17 at 19:50
  • `void operator=(char* o)` accepts a `char *`. ` o = "90";` provides a [string literal](http://en.cppreference.com/w/cpp/language/string_literal), which may not be in writable storage and is a `const char *` to prevent you from trying to change it. Ditto these guys in the constructor: `this->a = "0"; this->b = "0";` – user4581301 Feb 06 '17 at 19:52
  • user4581301 Something I forgot to include. Give me a second. – Collier Demetrios Feb 06 '17 at 20:03
  • @DemetriosCollier *Unfortunately, I need to use C arrays (char*)* -- You know, this is the reason for why you're having these troubles. Your code contains absolutely no arrays. All it has are character pointers. A pointer is not an array. A `char` array would be `char x[ ] = "abcd";` – PaulMcKenzie Feb 06 '17 at 20:11
  • PaulMcKenzie I see. Thanks for pointing that out. I was confused. I thought that char* o was an array and a pointer to the first character of that array. Stupid me. – Collier Demetrios Feb 06 '17 at 21:09

1 Answers1

3

In

MyClass(){
   this->a = "0";
   this->b = "0";
}

and in main at

o = "90";

string literals, which may not be in writable storage, are assigned to non-constant pointers to char. The compiler should be warning you about this or outright refusing to compile if the compiler supports the C++11 or newer standards.

Where this blows up is in operator= here:

this->a[i] = o[i];

and here:

this->a[0] = o[0];

as the program attempts to write storage that cannot be written.

Solution:

Use std::string. If that is off the table, and it sounds like it is, don't use string literals. Allocate a buffer in writable memory with new, copy the literal into the buffer, and then assign the buffer. And also remember that the buffers are of a fixed size. Trying to place a larger string in the buffer will end in disaster.

This will be a memory management headache and a potential Rule of Three horror show, so be careful.

Community
  • 1
  • 1
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • Thanks a lot, for your help :) Can you point out something for me ? Can writing storage that can not be written, cause Unexpected Behavior? – Collier Demetrios Feb 06 '17 at 20:29
  • @DemetriosCollier It will certainly cause [Undefined Behaviour](http://en.cppreference.com/w/cpp/language/ub) in a C program. C++ is even harsher on type checking so I'd expect the same. Can't cite chapter and verse to you, though. – user4581301 Feb 06 '17 at 20:37
  • Ah. Ok now I get it. Thanks a lot. – Collier Demetrios Feb 07 '17 at 13:05