2015-08-14 5 views
2

Я использую базовый класс и пытаюсь получить доступ к массиву элементов-членов в функции-члене. Я запутался, потому что массив строк инициализируется и получает освобожден без каких-либо ошибок памяти (Dr. Memory), если я только называю set_words, print_words и del_words функции:Доступ к массиву строк из функции члена класса

class XWORD { 
    public: 
    int vdir; 
    int len; 
    int wid; 
    int nWords; 
    char ** arr; 
    char ** words;  
    ... 
}; 

void XWORD::set_words(int istart, int inWords, char* iwords[]) { 
    int w = istart; 
    int k = 0; 
    this->nWords = inWords; 

    this->words = new char* [this->nWords]; 
    for (w=istart; w<(istart+this->nWords); w++) { 
     this->words[k] = new char [1+strlen(iwords[w])]; 
     this->words[k] = strcpy(this->words[k], iwords[w]); 
     k++; 
    } 
} 

void XWORD::del_words() { 
    int w = 0; 
    for (w=0; w<(this->nWords); w++) { 
     delete[] this->words[w]; 
    } 
    delete[] this->words; 
} 

void XWORD::print_words() { 
    int w = 0; 
    for (w=0; w<(this->nWords); w++) { 
     printf("\n%s",this->words[w]); 
    } 
    printf("\n"); 
} 

Однако, когда я называю такую ​​функцию, Я получаю ошибку памяти д-р, приведенная ниже (init_arr и del_arr только не дает ошибки либо):

void XWORD::add_word_to_arr(int iw, int iy, int ix) { 
    int k = 0; 
    int y = iy; 
    int x = ix; 

    for (k=0; k<(int)strlen(this->words[iw]); k++) { 
     this->arr[y][x] = this->words[iw][k]; 
     if (this->vdir) { 
      y++; 
     } else { 
      x++; 
     } 
    } 
} 

int main(int argc, char * argv[]) { 
    XWORD x; 
    x.set_words(1, argc-1, argv); 
    x.init_arr(10,10); 

    x.add_word_to_arr(0, x.len/2, x.wid/2); // WITHOUT THIS, NO ERRORS 
    x.print_words(); 

    x.del_words(); 
    x.del_arr(); 
} 
ошибка

доктор памяти:

c:\MinGW\WORKSPACE\cpp\xword>make runmem 
drmemory -brief -batch bin/test.exe 
~~Dr.M~~ Dr. Memory version 1.8.0 
~~Dr.M~~ Running "bin/test.exe" 
~~Dr.M~~ 
~~Dr.M~~ Error #1: UNADDRESSABLE ACCESS beyond heap bounds: reading 4 byte(s) 
~~Dr.M~~ # 0 XWORD::add_word_to_arr    [../mingwrt-4.0.3-1-mingw32-src/src/libcrt/crt/crt1.c:301] 
~~Dr.M~~ # 1 main         [../mingwrt-4.0.3-1-mingw32-src/src/libcrt/crt/crt1.c:302] 
~~Dr.M~~ Note: refers to 0 byte(s) beyond last valid byte in prior malloc 
~~Dr.M~~ 
~~Dr.M~~ Error #2: UNADDRESSABLE ACCESS: reading 1 byte(s) 
~~Dr.M~~ # 0 replace_strlen      [d:\drmemory_package\drmemory\replace.c:375] 
~~Dr.M~~ # 1 XWORD::add_word_to_arr    [../mingwrt-4.0.3-1-mingw32-src/src/libcrt/crt/crt1.c:301] 
~~Dr.M~~ # 2 main         [../mingwrt-4.0.3-1-mingw32-src/src/libcrt/crt/crt1.c:302] 
~~Dr.M~~ 
~~Dr.M~~ Error #3: LEAK 0 bytes 
~~Dr.M~~ # 0 replace_operator_new_array    [d:\drmemory_package\common\alloc_replace.c:2638] 
~~Dr.M~~ # 1 XWORD::set_words       [../mingwrt-4.0.3-1-mingw32-src/src/libcrt/crt/crt1.c:301] 
~~Dr.M~~ # 2 main          [../mingwrt-4.0.3-1-mingw32-src/src/libcrt/crt/crt1.c:302] 
~~Dr.M~~ 
~~Dr.M~~ Error #4: LEAK 40 direct bytes + 110 indirect bytes 
~~Dr.M~~ # 0 replace_operator_new_array    [d:\drmemory_package\common\alloc_replace.c:2638] 
~~Dr.M~~ # 1 XWORD::init_arr       [../mingwrt-4.0.3-1-mingw32-src/src/libcrt/crt/crt1.c:301] 
~~Dr.M~~ # 2 main          [../mingwrt-4.0.3-1-mingw32-src/src/libcrt/crt/crt1.c:302] 
~~Dr.M~~ 
~~Dr.M~~ ERRORS FOUND: 
~~Dr.M~~  2 unique,  2 total unaddressable access(es) 
~~Dr.M~~  0 unique,  0 total uninitialized access(es) 
~~Dr.M~~  0 unique,  0 total invalid heap argument(s) 
~~Dr.M~~  0 unique,  0 total GDI usage error(s) 
~~Dr.M~~  0 unique,  0 total handle leak(s) 
~~Dr.M~~  0 unique,  0 total warning(s) 
~~Dr.M~~  2 unique,  2 total, 150 byte(s) of leak(s) 
~~Dr.M~~  0 unique,  0 total,  0 byte(s) of possible leak(s) 
~~Dr.M~~ Details: C:\Users\jesse\AppData\Roaming\Dr. Memory\DrMemory-test.exe.4512.000\results.txt 
~~Dr.M~~ WARNING: application exited with abnormal code 0xc0000005 
make: *** [runmem] Error 5 

Кроме того, когда я печатаю «% s» this->words[0] в функции add_word_to_arr, это null. Тем не менее он может быть напечатан с print_words();.

Вызова EXE является:

bin\test.exe a123 b123 

init_arr() является:

void XWORD::init_arr(int ilen, int iwid) { 
    int y = 0; 
    int x = 0; 
    this->len = ilen; 
    this->wid = iwid; 

    this->arr = new char* [this->len]; 
    for (y=0; y<(this->len); y++) { 
     this->arr[y] = new char [1+this->wid]; 
     for (x=0; x<(this->wid); x++) { 
      this->arr[y][x] = BLANK; 
     } 
     this->arr[y][x] = (char) NULL; 
    } 
} 
+2

Я предлагаю вам использовать 'std :: vector ' и оставить ручное управление памятью в одиночку. – NathanOliver

+0

Спасибо, я собирался сделать это изначально, но я запутался в двухзначной векторной нотации –

+0

Вам не нужен 2d-вектор. Строка 'std :: string' заменяет символ' char * ', и обертывание его в' std :: vector' избавляется от наличия массива 'char *'. – NathanOliver

ответ

0

Ok init_arr(10,10) инициализирует массив из 10 символьных массивов размера 11 (10 символов + 1 оконечных NULL).

Затем вы вызываете add_word_to_arr(0, 5, 5), чтобы скопировать первое слово (первый параметр командной строки) в массив, начиная с позиции 5,5. Так что если это слово составляет не менее 7 символов с vdir == 0 или 6 с vdir! = 0, вы записываете прошедший зарезервированный массив. Я мог бы успешно работать с параметрами «foo» и «bar», но не с «feefoobar».

Вы должны вычислить длину самого длинного слова и использовать его в вызове int_arr

Во всяком случае, это выглядит, что вы проверить vdir значения без его инициализации в любом месте. Это приводит к неопределенному поведению, так как вы не можете знать, какая ветвь (горизонтальная или вертикальная) будет следовать, и вы действительно должны переписать add_word_to_arr таким образом:

void XWORD::add_word_to_arr(int iw, int iy, int ix) { 
    int k = 0; 
    int y = iy; 
    int x = ix; 

    for (k=0; k<(int)strlen(this->words[iw]); k++) { 
     this->arr[y][x] = this->words[iw][k]; 
     if (this->vdir) { 
      if (++y >= this.len) return; // refuse to write past allocated mem 
     } else { 
      if (++x > this.wid) return; // refuse to write past allocated mem 
     } 
    } 
} 

Я выполнил его под отладчиком после принуждая виртуальному каталогу значение раз в 0 и один раз до 1, и он никогда не обращался к ранее выделенной памяти. Но я должен был представить некоторый код, так как вы не дали Minimal Compilable Verifiable example

+0

Хотелось бы, чтобы это было легко ...! Выше я показал exe из make-файла: 'bin \ test.exe a123 b123' - поэтому самым длинным словом является только« 4 + null = 5'. Фактически программа работает без ошибок, когда не используется Dr. Memory. Однако, когда я работаю под Dr.M, возникает ошибка. Я подозреваю, что это как-то связано с публично-частными членами, может быть, даже с ошибкой в ​​Dr.M ...? –

+0

@JesseKnight Я не смог воспроизвести после устранения оставшейся неопределенной причины поведения. См. Мое редактирование –

0

Оказывается, я был запущен мой инструмент Доктор памяти от моего makefile и забыл включить $(ARGS) в моей make runmem цели:

FLAGS = -Wall -std=c++11 -pedantic 
LIBS = -Iinclude 
SRC = src/xword.cpp 
TGT = bin/test.exe 
ARGS = human prothean vorcha krogan asari salarian turian batarian quarian 

all: 
    g++ $(SRC) -o $(TGT) $(FLAGS) $(LIBS) 

run: 
    $(TGT) $(ARGS) 

runmem: 
    drmemory -batch $(TGT) **$(ARGS)** # was missing 

Это почему argv был пуст во время инкапсулированных заклинаний Dr.Memory, и почему код во время них только сбой.

После добавления $(ARGS) к makefile здесь ничего не произошло, и никаких сообщений об ошибках/утечках не сообщалось.

Извините за погоню за гусями, и спасибо за помощь - , пожалуйста, дайте мне знать, если это своего рода избыточное сообщение, которое необходимо удалить.