Домашнее задание: как в C получить подстроку массива, используя только указатели? - PullRequest
1 голос
/ 21 февраля 2010

Используя арифметику указателей, можно назначать символы из одного массива в другой. У меня вопрос: как это сделать, учитывая произвольные начальные и конечные точки?

int main(void)
{
    char string1[] = "something"; //[s][o][m][e][t][h][i][n][g][\0]
    int start = 2, count = 3;
    char string2[10] = {0};
    char *ptr1 = &string1[start];
    char *ptr2 = string2;
    while (*ptr2++ = *ptr1++) { } //but stop after 3 elements???
    printf("%s",&string2);
}

Существует какая-то арифметика указателей, которую мне не хватает для подсчета / проверки количества элементов в конкретном массиве. Я не хочу объявлять интеграл для подсчета цикла! Я хочу сделать все это с помощью указателей. Спасибо!

Ответы [ 4 ]

3 голосов
/ 21 февраля 2010

Когда вы пишете ptr1++;, это эквивалентно ptr1 = ptr1 + 1;.Добавление целого числа к указателю перемещает местоположение указателя в памяти на размер (в байтах) указанного типа.Если ptr1 является указателем char со значением 0x5678, то увеличение его на единицу делает его 0x5679, потому что sizeof(char) == 1.Но если ptr1 было Foo * и sizeof(Foo) == 12, то при увеличении указателя будет иметь значение 0x5684.

Если вы хотите указать на элемент, который находится на расстоянии 3 элементов от элементау вас уже есть указатель на, вы просто добавляете 3 к этому указателю.В своем вопросе вы написали:

char *ptr1 = &string1[start]; // array notation

То же самое, что и:

char *ptr1 = string1 + start; // pointer arithmetic

Вы можете переписать следующим образом:

int main(void)
{
    char string1[] = "something"; //[s][o][m][e][t][h][i][n][g][\0]
    int start = 2, count = 3;
    char string2[10] = {0};

    // Ensure there is enough room to copy the substring
    // and a terminating null character.
    assert(count < sizeof(string2));

    // Set pointers to the beginning and end of the substring.
    const char *from = string1 + start;
    const char *end = from + count;

    // Set a pointer to the destination.
    char *to = string2;

    // Copy the indicated characters from the substring,
    // possibly stopping early if the end of the substring
    // is reached before count characters have been copied.
    while (from < end && *from)
    {
        *to++ = *from++
    }

    // Ensure the destination string is null terminated
    *to = '\0';

    printf("%s",&string2);
}

Используя const и значимые имена переменных (from, to или src, dst вместо ptr1, ptr2) помогут вам избежать ошибок.Использование assert и обеспечение завершения строки нулем помогает избежать отладки ошибок сегмента и других странностей.В этом случае целевой буфер уже обнулен, но когда вы копируете части этого кода для использования в другой программе, это может быть не так.

2 голосов
/ 21 февраля 2010

Я обычно использую определенный набор имен переменных в этих ситуациях, называемый:

  • src - источник
  • dst - пункт назначения
  • end - конец источника (используется здесь) или пункта назначения

Итак:

int main(void)
{
    char string1[] = "something";
    int start = 2;
    int count = 3;
    char string2[10] = {0};

    const char *src = &string1[start];
    const char *end = &string1[start+count];
    char *dst = string2;

    assert(count < sizeof(string2);
    while (src < end)
        *dst++ = *src++;
    *dst = '\0';             // Null-terminate copied string!

    printf("%s",&string2);
    return(0);
}

Или, что более правдоподобно, упаковано как функция:

char *copy_substr(char *dst, const char *str, size_t start, size_t len)
{
    const char *src = str + start;
    const char *end = src + len;
    while (src < end)
        *dst++ = *src++;
    *dst = '\0';
    return(dst);
}

int main(void)
{
    char string1[] = "something";
    char *end;
    char string2[10] = {0};

    end = copy_substr(string2, string1, 2, 3);
    printf("%s",&string2);
    return(0);
}

Функция возвращает указатель на конец строки, что является условным и не дает заметного преимущества в примере, но имеет некоторые преимущества при построении фрагмента строки:

struct substr
{
    const char *str;
    size_t      off;
    size_t      len;
};

static struct substr list[] =
{
    { "abcdefghijklmnopqrstuvwxyz",  2, 5 },
    ...
    { "abcdefghijklmnopqrstuvwxyz", 18, 3 },
};

int main(void)
{
    char buffer[256];
    char *str = buffer;
    char *end = buffer + sizeof(buffer) - 1;
    size_t i;

    for (i = 0; i < 5; i++)
    {
         if (str + list[i].len >= end)
             break;
         str = copy_substr(str, list[i].str, list[i].off, list[i].len);
    }

    printf("%s\n", buffer);
    return(0);
}

Суть в том, что возвращаемое значение - указатель на NUL в конце строки - это то, что вам нужно для операций конкатенации строк. (В этом примере со строками, имеющими известную длину, вы могли бы выжить без этого возвращаемого значения без необходимости многократного использования strlen () или strcat (); в тех случаях, когда вызываемая функция копирует количество данных, которое не может быть определено вызывающим подпрограмма, указатель на конец еще более полезен.)

2 голосов
/ 21 февраля 2010
#include <stdio.h>

int main(void)
{
    char string1[] = "something"; //[s][o][m][e][t][h][i][n][g][\0]
    int start = 2, count = 3;
    char string2[10] = {0};
    char *ptr1 = &string1[start];
    char *stop = ptr1 + count;
    char *ptr2 = string2;
    while ((ptr1 < stop) && (*ptr2++ = *ptr1++));
    printf("%s",string2);
    return 0;
}
0 голосов
/ 21 февраля 2010

Чтобы получить размер (то есть количество элементов) в статическом массиве, вы обычно делаете

sizeof(string1) / sizeof(*string1)

, который разделит размер (в байтах) массива на размер (в байтах) каждого элемента, что даст вам количество элементов в массиве.

Но поскольку вы явно пытаетесь реализовать клон strcpy, вы можете просто разорвать цикл, если исходный символ *ptr1 равен '\0' (строки C заканчиваются нулем). Если вы хотите скопировать только N символов, вы можете разбить, если ptr1 >= string1 + start + count.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...