Глубокое понимание функций strcat и strlen - PullRequest
3 голосов
/ 30 июня 2019

Мы знаем, что strcat () получает указатель на целевой массив в качестве параметров и объединяет их с исходной строкой. Массив назначения должен быть достаточно большим, чтобы хранить объединенный результат. Недавно я обнаружил, что strcat () по-прежнему может выполняться должным образом для небольших программ, даже когда целевой массив недостаточно велик для добавления второй строки. Я начал серфить в stackoverflow и обнаружил пару - ответов на этот вопрос. Я хочу углубиться и понять, что именно происходит на аппаратном уровне, когда я запускаю этот код ниже?

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstring>

using namespace std;

int main(){
    char p[6] = "Hello";
    cout << "Length of p before = " << strlen(p) << endl;
    cout << "Size of p before = " << sizeof(p) << endl;
    char as[8] = "_World!";
    cout << "Length of as before = " << strlen(as) << endl;
    cout << "Size of as before = " << sizeof(as) << endl;
    cout << strcat(p,as) << endl;
    cout << "After concatenation:" << endl;
    cout << "Length of p after = " << strlen(p) << endl;
    cout << "Size of p after = " << sizeof(p) << endl; 
    cout << "Length of as after = " << strlen(as) << endl;
    cout << "Size of as after = " << sizeof(as) << endl;

    return 0;
}

После выполнения этого кода длина массива p [] равна 12, а размер p [] равен 6. Как физически такая длина может быть сохранена для такого размера массива? Я имею в виду, что для этого массива количество байтов ограничено, значит ли это, что функция strlen (p) ищет только терминатор NULL и продолжает считать до тех пор, пока не найдет его и проигнорирует фактический выделенный размер этого массива. И функции sizeof () действительно все равно, хранит ли последний элемент массива, специально выделенный для нулевого символа, нулевой символ или нет.

Ответы [ 2 ]

6 голосов
/ 30 июня 2019

Массив p размещается в кадре стека функций, поэтому strcat "переполняет" буфер p и продолжает записывать в какую-то другую область стека - обычно он переопределяет другие локальные параметры, адрес возврата функции, и т.д. (имейте в виду, что на платформе x86 стек функций обычно растет «вниз», то есть в сторону меньших адресов). Это хорошо известная уязвимость «переполнения буфера».

strlen не может знать, каков реальный размер вашего буфера, он просто ищет 0-определитель. С другой стороны, sizeof - это функция времени компиляции, которая возвращает размер массива в байтах.

1 голос
/ 30 июня 2019

Вы пишете за пределами p, поэтому поведение вашей программы не определено.

Несмотря на то, что поведение полностью не определено, существует несколько распространенных вариантов поведения:

  1. Вы перезаписываете некоторые несвязанные данные. Это могут быть другие локальные переменные, адрес возврата функции и т. Д. Невозможно точно угадать, что будет перезаписано без проверки сборки, сгенерированной компилятором для этой конкретной программы. Это может привести к серьезной уязвимости безопасности, поскольку позволяет злоумышленнику вставлять свой собственный код в область памяти вашей программы и перезаписывать адрес возврата функции, чтобы заставить программу выполнить введенный код.

  2. Программа вылетает. Это может произойти, если вы пишете достаточно далеко за концом массива, чтобы пройти границу страницы памяти. Программа может попытаться записать адрес виртуальной памяти, который ОС не сопоставила с физической памятью вашего приложения. Это приводит к тому, что ОС убивает ваше приложение (например, с SIGSEGV в Linux). Обычно это случается чаще с динамически размещаемыми массивами, чем с локальными функциями.

...