2013-04-16 2 views
4

У меня проблема с кодом на C.C ссылка ушла после цикла

int split(char* source, char*** target, char* splitChar) { 
    int i; 
    int currentLength; 
    int splitCharPosition; 
    char* currentSubstring = source; 
    int splitCount = charcount(source, splitChar) + 1; 

    *target = (char**) malloc(splitCount * sizeof(char**)); 
    for(i=0;i<splitCount;i++) { 
     splitCharPosition = indexOf(currentSubstring, splitChar); 
     substring(currentSubstring, target[i], 0, splitCharPosition); 
     currentLength = strlen(currentSubstring); 
     substring(currentSubstring, &currentSubstring, splitCharPosition + 1, curr entLength-splitCharPosition); 
    } 
    return splitCount; 
} 

Проблема заключается в том, что если я использую отладчик, указатель на splitChar установлен в 0х0 после первого запуска для цикла. Кто-нибудь знает, почему он установлен на 0x0?

EDIT:

int indexOf(char* source, char* template) { 
int i; 
int j; 
int index; 
for (i = 0; source[i]; i++) { 
    index = i; 
    for (j = 0; template[j]; j++) { 
     if (source[i + j] != template[j]) { 
      index = -1; 
      break; 
     } 
    } 
    if (index != -1) { 
     return index; 
    } 
} 
return -1; 
} 

EDIT2:

int charcount(char* source, const char* countChar) { 
int i; 
int count = 0; 
for(i=0;source[i];i++) { 
    if(source[i] == countChar[0]) { 
     count++; 
    } 
} 
return count; 
} 

EDIT3:

char* substring(char* source, char** target, int start, int length) { 
    *target = (char*) malloc(length + 1); 
    strncpy(*target, source + start, length); 
    target[length] = '\0'; 
    return *target; 
} 

EDIT4: Я просто заметил, что если добавить

char* sndfpgjps = splitChar; 

моему коду split() он не удаляет ссылку. Кто-нибудь знает, почему?

+2

Вы получаете upvote за упоминание, что вы использовали отладчик, прежде чем прийти сюда, чтобы опубликовать свой вопрос. – austin

+0

Что делает indexOf()? – Otaia

+1

Это может быть только я, но я чувствую, что этот код слишком сложный для того, чего он пытается достичь. – user1751547

ответ

2

У меня есть некоторые оговорки относительно кода, но это работает чисто под valgrind (никаких утечек, без злоупотреблений). Я оставил подфункции в основном неизменными, за исключением того, что постоянные строки отмечены константой. Код в split() был упрощен. Как я отметил в комментарии, я предлагаю написать основную функцию split(), чтобы у вас был локальный char **string_list;, который вы выделяете и заполняете. Затем, когда вы собираетесь вернуться, вы назначаете *target = string_list;. Это облегчит вам понимание того, что происходит. Тройная косвенность противна. Вы можете обосновать это здесь (просто), но свести к минимуму время, затрачиваемое на тройные указатели. Пересмотр принимает эту стратегию.

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

extern int split(const char *source, char ***target, const char *splitStr); 

static int 
indexOf(const char *source, const char *template) 
{ 
    int i; 
    int j; 
    int index; 
    for (i = 0; source[i]; i++) 
    { 
     index = i; 
     for (j = 0; template[j]; j++) 
     { 
      if (source[i + j] != template[j]) 
      { 
       index = -1; 
       break; 
      } 
     } 
     if (index != -1) 
      return index; 
    } 
    return -1; 
} 

static int 
charcount(const char *source, const char *countChar) 
{ 
    int count = 0; 
    for (int i = 0; source[i]; i++) 
    { 
     if (source[i] == countChar[0]) 
      count++; 
    } 
    return count; 
} 

static char * 
substring(const char *source, int start, int length) 
{ 
    char *target = (char *)malloc(length + 1); 
    if (target != 0) 
    { 
     memmove(target, source + start, length); 
     target[length] = '\0'; 
    } 
    return target; 
} 

int 
split(const char *source, char ***target, const char *splitStr) 
{ 
    int splitCount = charcount(source, splitStr) + 1; 
    char **result = (char **)malloc(splitCount * sizeof(*result)); 

    if (result == 0) 
     return -1; 

    int splitLength = strlen(splitStr); 
    char **next = result; 
    const char *currentSubstring = source; 

    for (int i = 0; i < splitCount; i++) 
    { 
     int splitCharPosition = indexOf(currentSubstring, splitStr); 
     if (splitCharPosition < 0) 
      break; 
     *next++ = substring(currentSubstring, 0, splitCharPosition); 
     currentSubstring += splitCharPosition + splitLength; 
    } 
    *next++ = substring(currentSubstring, 0, strlen(currentSubstring)); 
    *target = result; 
    return (next - result);  /* Actual number of strings */ 
} 

static void print_list(int nstrings, char **strings) 
{ 
    for (int i = 0; i < nstrings; i++) 
    { 
     if (strings[i] != 0) 
      printf("%d: <<%s>>\n", i, strings[i]); 
    } 
} 

static void free_list(int nstrings, char **strings) 
{ 
    for (int i = 0; i < nstrings; i++) 
     free(strings[i]); 
    free(strings); 
} 

int main(void) 
{ 
    const char source[] = "This is a string; it is really!"; 
    char **strings; 
    int nstrings; 

    nstrings = split(source, &strings, " "); 
    printf("Splitting: <<%s>> on <<%s>>\n", source, " "); 
    print_list(nstrings, strings); 
    free_list(nstrings, strings); 

    nstrings = split(source, &strings, "is"); 
    printf("Splitting: <<%s>> on <<%s>>\n", source, "is"); 
    print_list(nstrings, strings); 
    free_list(nstrings, strings); 

    return 0; 
} 

Заметим, что во втором примере, charcount() возвращает 6, но есть только четыре строки. Это привело к поздней корректировке исходного кода. (Вы могли бы realloc()result, так что это точно правильный размер, но, вероятно, не стоит беспокоиться о том, если расхождение действительно отмечено - скажем, «более 10 записей».) Обработка ошибок не идеальна; он не получает доступ к недопустимой памяти после сбоя, но также не останавливается на попытке выделить. Также он не сообщает о сбоях в распределении отдельных строк - он делает это для отказа выделить массив указателей.

я бы, вероятно, избежать тройной указатель, создавая структуру:

typedef struct StringList 
{ 
    size_t  nstrings; 
    char  **strings; 
} StringList; 

Вы можете передать указатель на один из них в split(), и в функции полезности, такие как free_list() и print_list(). Функция free_list() затем изменит структуру так, чтобы оба элемента были обнулены после того, как данные, на которые указывает структура, освобождены.

Я также был бы соблазн использовать другую реализацию indexOf():

int indexOf(const char *haystack, const char *needle) 
{ 
    const char *pos = strstr(haystack, needle); 
    if (pos != 0) 
     return (pos - haystack); 
    return -1; 
} 
0

Посмотрите, может ли ваш отладчик также поддерживать точки останова данных, т. Е. Перерыв, если какое-либо место в памяти было изменено. Затем поместите один на фактический адрес splitChar, а другой - по адресу, на который он указывает. (Так как вы не указали, является ли указатель нулевым или указывает на нуль.) См., Где он ломается. Возможно, это совершенно несвязанное место; что указывает на переполнение буфера.

Кроме того, вы можете сделать хотя бы splitChar указатель на const. Вы действительно не хотите его модифицировать, верно? Лучшая идея, сделайте ее символом, а не указателем, поскольку его имя предполагает, что есть только один символ, на котором вы разбиваете, а не строку.

+0

Пробовал это с помощью const, но он все равно не работает. Имя может быть выбрано неправильно, я также хочу разделить на String. – lutzb

0

Первый вызов substring не выглядит правильно:

substring(currentSubstring, target[i], 0, splitCharPosition); 

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

substring(currentSubstring, &((*target)[i]), 0, splitCharPosition); 

Сначала необходимо чтобы получить значение, которое указывает целевые точки на (*target), а затем индексировать его и передать адрес этого расположения массива.

+0

Вы правы. Это то, что я хотел сделать. Но, к сожалению, это не решает мою проблему. – lutzb

1

Я не знаю, что подстрока делает, ни того, что подпись имеет, но в строке

substring(currentSubstring, target[i], 0, splitCharPosition); 

мишени [я] определена только для I == 0. Я считаю, что вы хотите написать

substring(currentSubstring, (*target)[i], 0, splitCharPosition); 
+0

Вы правы. Это то, что я хотел сделать. Но, к сожалению, это не решает мою проблему. – lutzb

+0

Пожалуйста, разместите код для подстроки. –

+0

Это правильный диагноз одной проблемы. Я предлагаю написать основную функцию 'split()', чтобы у вас был локальный 'char ** string_list;', который вы выделяете и заполняете. Затем, когда вы собираетесь вернуться, вы назначаете «* target = string_list;». Это облегчит вам понимание того, что происходит. Тройная косвенность противна. Вы можете обосновать это здесь (просто), но свести к минимуму время, затрачиваемое на работу с тройными указателями, как показано. –

3

Эта строка: -

substring(currentSubstring, &currentSubstring, splitCharPosition + 1, curr entLength-splitCharPosition); 

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

Было бы гораздо лучше, чтобы написать

currentSubString += splitCharPosition + 1; 

Я не думаю, что это проблема, но это проблема.

Кроме того, поскольку вы используете функции библиотеки C, такие как strlen(), почему вы не используете strtok или еще лучше, strtok_r?

+0

Есть ли функция библиотеки C, которая может разделять данную строку на строки? – lutzb

+1

В стандартной библиотеке C нет функции, которая разбивает строку на основе строки разделителя, такой как вы хотите. Ближайший подход - это функция 'strtok()', но она разбивается на любой из отдельных символов, а не на символьную строку (и имеет ряд других проблем - избегайте 'strtok()' когда вы можете, используйте ' strtok_r() 'или' strtok_s() ', если вы не можете). Одна из причин заключается в том, что стандартная библиотека C избегает распределения памяти почти везде (за исключением 'malloc()', 'realloc()', 'calloc()' и 'free()', конечно). TR 24731-2 изменит это; он не был принят в C2011. –

Смежные вопросы