Как правильно реализовать strcpy в c? - PullRequest
3 голосов
/ 21 июня 2020

Согласно этому: strcpy vs strdup , strcpy может быть реализован с помощью al oop, они использовали это while(*ptr2++ = *ptr1++). Я пытался сделать то же самое:

#include <stdio.h>
#include <stdlib.h>
int main(){
    char *des = malloc(10);
    for(char *src="abcdef\0";(*des++ = *src++););
    printf("%s\n",des);
}

Но это ничего не печатает и нет ошибок. Что пошло не так?

Большое спасибо за ответы, я немного поиграл и решил, как лучше всего спроектировать l oop, чтобы увидеть, как идет копирование побайтно. Это кажется лучшим:

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

int main(){
    char *des = malloc(7);
    for(char *src="abcdef", *p=des; (*p++=*src++); printf("%s\n",des));
}

Ответы [ 6 ]

3 голосов
/ 21 июня 2020

В этом l oop

for(char *src="abcdef\0";(*des++ = *src++););

изменяется указатель назначения des. Таким образом, после l oop он не указывает на начало скопированной строки.

Обратите внимание, что явный завершающий нулевой символ '\0' является избыточным в строковом литерале.

L oop может выглядеть следующим образом:

for ( char *src = "abcdef", *p = des; (*p++ = *src++););

А затем после l oop

puts( des );

и

free( des );

вы могли бы написать отдельная функция, похожая на strcpy следующим образом

char * my_strcpy( char *des, const char *src )
{
    for ( char *p = des; ( *p++ = *src++ ); );

    return des;
}

И называть ее как

puts( my_strcpy( des, "abcdef" ) )'
free( des );
3 голосов
/ 21 июня 2020

Вы увеличиваете des, поэтому, естественно, в конце цикла он будет указывать за конец строки, вывод на печать составляет неопределенное поведение , вы должны вернуть его в начало of des.

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

int main(){
    int count = 0;
    char *des = malloc(10);

    if(des == NULL){
       return EXIT_FAILURE; //or otherwise handle the error
    }

    // '\0' is already added by the compiler so you don't need to do it yourself
    for(char *src="abcdef";(*des++ = *src++);){
        count++; //count the number of increments
    }
    des -= count + 1; //bring it back to the beginning
    printf("%s\n",des);
    free(dest); //to free the allocated memory when you're done with it
    return EXIT_SUCCESS;
}

Или создайте указатель на начало des и распечатайте это вместо этого.

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

int main(){
 
    char *des = malloc(10);

    if(des == NULL){
       return EXIT_FAILURE; //or otherwise handle the error
    }

    char *ptr = des;
    for(char *src="abcdef";(*des++ = *src++);){} //using {} instead of ;, it's clearer

    printf("%s\n",ptr);
    free(ptr) // or free(dest); to free the allocated memory when you're done with it
    return EXIT_SUCCESS;

}
2 голосов
/ 21 июня 2020

printf("%s\n",des); - это неопределенное поведение (UB), поскольку он пытается печатать, начиная с конца строки, записанной в выделенную память.

Скопируйте строку

Сохраните исходный указатель, проверьте его и освободите, когда закончите.

const char *src = "abcdef\0"; // string literal here has 2 ending `\0`, 
char *dest = malloc(strlen(src) + 1);  // 7

char *d = dest;
while (*d++ = *src++);
printf("%s\n", dest);
free(dest);

Скопируйте строковый литерал

const char src[] = "abcdef\0"; // string literal here has 2 ending `\0`, 
char *dest = malloc(sizeof src);  // 8

for (size_t i = 0; i<sizeof src; i++) {
  dest[i] = src[i];
}

printf("%s\n", dest);
free(dest);
1 голос
/ 21 июня 2020

Но при этом ничего не выводится и нет ошибки. Что пошло не так?

des больше не указывает на начало строки после выполнения (*des++ = *src++). Фактически, des указывает на один элемент за символом NUL, который после этого завершает строку.

Таким образом, если вы хотите напечатать строку с помощью printf("%s\n",des), он вызывает неопределенное поведение.

Вам необходимо сохранить адресное значение «начального» указателя (указывающего на первый char объект выделенного фрагмента памяти) во временный указатель «держателя». Возможны разные способы.

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

int main (void) {
    char *des = malloc(sizeof(char) * 10);
    if (!des)
    {
        fputs("Error at allocation!", stderr);
        return 1;
    }

    char *tmp = des;

    for (const char *src = "abcdef"; (*des++ = *src++) ; );
    des = temp;

    printf("%s\n",des);

    free(des);
}

Альтернативы:

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

int main (void) {
    char *des = malloc(sizeof(char) * 10);
    if (!des)
    {
        fputs("Error at allocation!", stderr);
        return 1;
    }

    char *tmp = des;

    for (const char *src = "abcdef"; (*des++ = *src++) ; );

    printf("%s\n", tmp);

    free(tmp);
}

или

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

int main (void) {
    char *des = malloc(sizeof(char) * 10);
    if (!des)
    {
        fputs("Error at allocation!", stderr);
        return 1;
    }

    char *tmp = des;

    for (const char *src = "abcdef"; (*tmp++ = *src++) ; );

    printf("%s\n", des);

    free(des);
}

Примечания:

  • "abcdef\0" - Явное \0 не требуется. Он добавляется автоматически во время перевода. Используйте "abcdef".

  • Всегда проверяйте возврат функции управления памятью, если выделение выполнено успешно, проверяя возвращенный указатель на нулевой указатель.

  • Укажите указатели на строковый литерал с помощью const, чтобы избежать непреднамеренных попыток записи.

  • Используйте sizeof(char) * 10 вместо простого 10 в вызове mallo c. Это гарантирует размер записи при изменении типа.

  • int main (void) вместо int main (void). Первый соответствует стандарту, второй - нет.

  • Всегда free() динамически выделяемая память, поскольку вам больше не нужна выделенная память. В приведенном выше примере это будет избыточным, но если ваша программа станет больше, а пример сфокусирован частично, вы должны немедленно free() ненужную память.

1 голос
/ 21 июня 2020

Вам просто нужно запомнить исходный выделенный указатель.

Не программировать в main. Используйте functions .


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

size_t strSpaceNeedeed(const char *str)
{
    const char *wrk = str;
    while(*wrk++);
    return wrk - str;
}

char *mystrdup(const char *str)
{
    char *wrk;
    char *dest = malloc(strSpaceNeedeed(str));

    if(dest)
    {
        for(wrk = dest; *wrk++ = *str++;);
    }   
    return dest;
}

int main(){
    printf("%s\n", mystrdup("asdfgfd"));
}

или даже лучше

size_t strSpaceNeedeed(const char *str)
{
    const char *wrk = str;
    while(*wrk++);
    return wrk - str;
}

char *mystrcpy(char *dest, const char *src)
{
    char *wrk = dest;
    while((*wrk++ = *src++)) ;
    return dest;
}

char *mystrdup(const char *str)
{
    char *wrk;
    char *dest = malloc(strSpaceNeedeed(str));

    if(dest)
    {
        mystrcpy(dest, str);
    }   
    return dest;
}

int main(){
    printf("%s\n", mystrdup("asdfgfd"));
}
1 голос
/ 21 июня 2020

Вы выделяете целевой буфер des и правильно копируете исходную строку на место. Но поскольку вы увеличиваете des для каждого копируемого символа, вы переместили des из начала строки в конец. Когда вы go печатаете результат, вы печатаете последний байт, который является завершением nil, который пуст.

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

Наименьшее изменение по сравнению с исходным исходным кодом:

#include <stdio.h>
#include <stdlib.h>
int main(){
    char *des = malloc(10);
    char *p = des;
    for(char *src="abcdef";(*p++ = *src++););
    printf("%s\n",des);
}

Итак, p является указателем на следующий целевой символ и перемещается по строка. Но последняя строка, которую вы напечатаете, будет des, с начала выделения.

Конечно, вы также должны выделить strlen(src)+1 байтов для des. И нет необходимости завершать строковый литерал завершающим нулем, поскольку это сделает за вас компилятор.

...