Почему tt [9] равно '4' после того, как strcpy изменил свое значение? - PullRequest
0 голосов
/ 05 мая 2019
#include <stdio.h>
#include <string.h>

int main(void) {
    char tt[20] = "9081726354";
    strcpy(tt, tt + 3);
    printf("%d\n", strlen(tt) - tt[9] + '5');

    return 0; 
}

После strcpy, tt теперь равно 1726354, а его длина 7. ASCII '5' является 53. Но я не понимаю, как tt[9] равно '4' после изменения строки, указанной tt.

Ответы [ 4 ]

2 голосов
/ 05 мая 2019

Вот спецификация стандарта C17:

7.24.2.3 Функция strcpy

Синопсис

    #include <string.h>
    char *strcpy(char * restrict s1,
                 const char * restrict s2);

Функция strcpy копирует строку, на которую указывает s2 (включая завершающий нулевой символ), в массив, на который указывает s1. Если копирование происходит между объектами, которые перекрываются, поведение не определено.

Returns

Функция strcpy возвращает значение s1.

Копирование строки в tt + 3, которая содержит 8 байтов, в tt действительно вызовет копирование между перекрывающимися объектами и, таким образом, имеет неопределенное поведение .

Тем не менее, поскольку место назначения находится перед источником, вероятно, что копия будет выполнена, как ожидалось, и первые 8 байтов tt будут перезаписаны с "1726354" и нулевым байтом. Байт со смещением 9 не изменяется и все еще имеет значение '4', поэтому ваш оператор printf может выдать 7 - '4' + '5', то есть 8, но стандарт C не гарантирует такое поведение.

На самом деле у вас есть еще один экземпляр неопределенного поведения, когда вы передаете значение типа size_t для спецификатора преобразования %d. size_t имеет размер, отличный от int в 64-битных системах, поэтому несоответствие типов также имеет неопределенное поведение. Вы можете обнаружить такую ​​ошибку, увеличив уровень предупреждения компилятора: gcc -Wall -Wextra -Werror или clang -Weverything -Werror.

Вот исправленная версия, которая надежно выводит 8:

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

int main(void) {
    char tt[20] = "9081726354";
    memmove(tt, tt + 3, strlen(tt + 3) + 1);
    printf("%d\n", (int)strlen(tt) - tt[9] + '5');
    return 0;
}

Обратите внимание, что значения size_t могут быть переданы в printf с помощью спецификатора преобразования %zu или %zd, но многие устаревшие системы не поддерживают это расширение C99.

0 голосов
/ 05 мая 2019

ЭТО УБ в общем.

char *safestrcpy(char *dest, const char *src)
{
    memmove(dest, src, strlen(src) + 1);
}

Но в этом направлении копирования все разумные реализации работают хорошо.

Так что объяснение очень простое.

enter image description here

Как видите, байты после нового терминатора не изменились.

0 голосов
/ 05 мая 2019

Ответ приходит как 8 в gcc.

До strcpy(tt, tt + 3); длина массива tt, возвращаемого strlen, равна 10, даже если вы создали массив размером 20. Этопотому что, когда вы инициализируете массив числовой строкой char tt[20] = "9081726354";, компилятор добавляет нулевой завершающий символ в конце строки и strlen вернет длину 10.

Во время strcpy(tt, tt + 3); вы по существу копируете числовуюстрока от tt + 3 т.е. значение 1 до завершающего нулевого символа в начале вашей строки.Теперь ваш tt[7] = '\0', следовательно, strlen после strcpy дает вам длину 7. Обратите внимание, что вы не перезаписали tt [9], следовательно, он по-прежнему содержит значение '4'.

Наконец, strlen(tt) - tt[9] + '5' по существу равен 7 - '4' + '5', то есть 7 -52 + 53 = 8.

0 голосов
/ 05 мая 2019
#include <stdio.h>
        #include <string.h>
        int main(void) {
            char tt[20] = "9081726354";
            printf("Before: %s\n",tt);
            strcpy(tt, tt + 3);
            printf("After: %s\n",tt);
            printf("Length: %d,\ntt[9]: %d,\n'5': %d\n",strlen(tt),tt[9],'5');
            printf("Result: %d\n", strlen(tt) - tt[9] + '5');
            printf("Start analysis:\n");
            int i;
            for(i=0;i<10;i++)
                printf("%d: %c\n",i,tt[i]);
            return 0; 
        }

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

 #include <stdio.h>
        #include <string.h>
        int main(void) {
            char tt[20] = "9081726399";
            printf("Before: %s\n",tt);
            strcpy(tt, tt + 3);
            printf("After: %s\n",tt);
            printf("Length: %d,\ntt[9]: %d,\n'5': %d\n",strlen(tt),tt[9],'5');
            printf("Result: %d\n", strlen(tt) - tt[9] + '5');
            printf("Start analysis:\n");
            int i;
            for(i=0;i<10;i++)
                printf("%d: %c\n",i,tt[i]);
            return 0; 
        }


printf не может сказать, где заканчивается строка и продолжает печатать значения мусора.

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