Глубокое понимание функций strcat и strlen

Мы знаем, что улкат() получает указатель на целевой массив в качестве параметров и объединяет их с исходной строкой. Целевой массив должен быть достаточно большим, чтобы сохранить конкатенированный результат. Недавно я обнаружил, что функция 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() на самом деле не волнует, хранит ли последний элемент в массиве, специально выделенный для нулевого символа, нулевой символ или нет.

Может переполнение кучи? Со временем, когда вы начнете писать рекурсивные функции, вы можете достичь переполнения стека.

Stephen Quan 30.06.2019 07:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
104
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

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

Почти уверен, что стек вызовов был предназначен. Но что, если реализация спрашивающего на C++ не использует стек?

user4581301 30.06.2019 08:15

@SidS, вероятно, имел в виду область стека

casper 30.06.2019 08:31

@Sid S, ты прав, формулировка неточна, лучше было бы «фрейм стека функций».

Igor R. 30.06.2019 09:39

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

Хотя поведение полностью не определено, есть несколько распространенных вариантов поведения:

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

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

Другие вопросы по теме