Экранированная обратная косая черта (двойная обратная косая черта) в C считается как два байта в строке

Я относительно неопытный программист, и у меня возникла некоторая путаница по поводу экранированных обратных косых черт. Я использую строки, содержащие обратную косую черту, для представления путей к файлам Windows (по какой-то причине косая черта не работала, хотя Windows ее поддерживает), и двойная обратная косая черта принимается Windows, но по какой-то причине, в отличие от других экранированных символов, двойная обратные косые черты учитываются функцией strlen() как два символа вместо одного, и мне приходится использовать malloc() дополнительный байт для escape-символа, чтобы предотвратить ошибку сегментирования. Другие escape-последовательности, такие как '\n', похоже, работают совершенно нормально и считаются как один символ. Почему моя двойная обратная косая черта "foo\\bar" считается как два символа, а не как один?

Я использую VSCodium для написания своего кода и компилирую его с помощью MinGW64 GCC в MSYS2.

Пример кода: strlen("documents\\notes") возвращает 16 вместо 15 и занимает 17 байт, включая нулевой терминатор.

полный код: https://github.com/WyntrHeart/note/blob/main/note.c

Обновлено: Чтобы прояснить решение, я думал, что мои escape-обратные косые черты занимают дополнительный байт, потому что добавление дополнительного байта к моим вызовам malloc() привело к тому, что сбои сбоя сегментации исчезли, но на самом деле я вообще забыл выделить память. для части моих струн, так что это «работало» только по счастливой случайности.

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

Some programmer dude 31.05.2024 17:57
strlen("foo\\bar") — 7. Как вы думаете, почему экранированная обратная косая черта считается за 2 символа? Покажите код, который вы используете, который заставляет вас поверить, что он учитывается дважды; возможно, произошла какая-то банальная ошибка.
William Pursell 31.05.2024 18:02

Вопросы должны быть самостоятельными. Не размещайте ссылки на код. Такие ссылки могут исчезнуть, а содержимое может измениться. А еще, когда вы пытаетесь создать минимально воспроизводимый пример всегда есть шанс, что вы сами разберетесь в проблеме.

Some programmer dude 31.05.2024 18:40

Кстати, в Windows (а до нее MS-DOS) можно использовать косую черту / уже более 30 лет.

Some programmer dude 31.05.2024 18:41

Я слышал, что код просто не работал в Windows, пока я не перешел на обратную косую черту. Лучшее, что я могу предположить, это то, что мне не нравилось смешивать косые черты из моего кода с переменными среды, которые я читаю, которые содержат пути обратной косой черты.

WyntrHeart 31.05.2024 18:44

О, и никогда не делайте ничего подобного retVal=(retVal & (!!((!!((str[i]|32u)>='a')) && (!!((str[i]|32u)<='z')))));! В цикле используйте, например. if (!isalpha(str[i])) { return 0; }. После цикла просто return 1;. Простой, легкий для чтения, понимания и обслуживания, обрабатывает как верхний, так и нижний регистр. А также переносимый на системы, которые не используют ASCII.

Some programmer dude 31.05.2024 18:44

Есть и другие вещи, которые можно было бы сделать в этом коде намного проще. Например, сортировка. Или все эти глобальные переменные (что является очень плохой привычкой).

Some programmer dude 31.05.2024 18:47

Я не хотел добавлять дополнительный #include для одной функции, которую я мог бы написать в одной строке, хотя я не знал, что существуют системы, не использующие ASCII, поэтому я мог бы это изменить. .

WyntrHeart 31.05.2024 18:50

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

WyntrHeart 31.05.2024 18:55

Сначала я бы сосредоточился на том, чтобы ваш код работал. В этот момент codereview.stackexchange.com является лучшим местом для критики рабочего кода.

Chris 31.05.2024 19:00

Это работает прямо сейчас

WyntrHeart 31.05.2024 19:09

@WyntrHeart Я не хотел добавлять целый дополнительный #include для одной функции, которую я могу написать сам в одной строке. Действительно, очень, очень плохая идея. Каждая строка кода, которую вы пишете, представляет собой ошибку, ожидающую своего появления, и ее нигде не тестируют так часто, как это делают стандартные функции. И если вы принесете неясный, запутанный, непонятный код, такой как retVal=(retVal & (!!((!!((str[i]|32u)>='a')) && (!!((str[i]|32u)<='z')))));, на любую профессиональную проверку кода, вас сразу вышвырнут. Если вы не видите, что делает код примерно за пять секунд, это ПЛОХОЙ код.

Andrew Henle 31.05.2024 23:45

@AndrewHenle, это имеет большой смысл, и как новичок в самообучении я очень ценю профессиональный совет, спасибо. Это была довольно запутанная строка, особенно с таким количеством вложенных круглых скобок, поэтому, если я не смогу переписать ее во что-то читабельное, я отброшу ее и использую std isAlpha().

WyntrHeart 01.06.2024 00:30
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
13
79
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Строка имеет правильную длину. Рассмотрим следующий код.

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

int main(int argc, const char *argv[])
{
    printf("%d\n", (int)strlen("foo\\bar"));
}

Если мы скомпилируем и запустим этот код, мы получим 7 в консоли. Почему же тогда строка кажется длинной 8? Это потому, что он завершается нулем. Всякий раз, когда вы пишете "foo", это на самом деле синтаксический сахар для { 'f', 'o', 'o', 0 }. Причина, по которой он добавляет ноль, заключается в том, что всякий раз, когда функции необходимо выполнить операции со строкой, она не сохраняет длину строки. Вместо этого вы определяете длину во время выполнения, находя 0.

Чтобы доказать эту теорию, используйте любой метод проверки длины, который вы использовали для этой строки: "foo\\\\ar". Если вы попробуете эту строку, она также вернет длину 8.

Скорее всего, вы используете sizeof, поэтому можете попробовать сделать это следующим образом.

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

int main(int argc, const char *argv[])
{
    printf("%d\n", (int)sizeof("foo\\\\ar"));
}

Чтобы уточнить, я говорю, что sizeof(str) включает 0, а strlen — нет. sizeof является общим и работает для всех массивов, но strlen специализируется на особенностях строк. В общем, вам следует попытаться использовать такие функции, как последняя, ​​поскольку слабая система типов C приводит к множеству подобных ошибок, если вы не уверены, что вызываете правильную функцию.

Spencer Rosas-Gunn 31.05.2024 18:05

Это не проблема с нулевым байтом, я понимаю разницу. strlen("documents/notes") было 15 символов, как и ожидалось, а изменение на strlen("documents\\notes") увеличило его до 16 символов, и мне пришлось использовать malloc() дополнительный байт (17 байт вместо 16).

WyntrHeart 31.05.2024 18:19

@WyntrHeart ваша вторая строка возвращает 15, а не 16.

Fredrik 31.05.2024 18:21

Может ли это быть какая-то проблема с моей настройкой MSYS2 и/или MinGW? Потому что тестирование на моем телефоне Android дает правильную длину, а на моем ПК с Windows обратная косая черта считается дополнительным символом.

WyntrHeart 31.05.2024 18:41

Быстрое прочтение связанного кода (который должен был представлять собой минимальный воспроизводимый пример, встроенный в форматированный текст) показывает, что использование sizeof вместо strlen не является проблемой. Этот ответ, похоже, не намного выше предположения без этого MRE из ОП.

Chris 31.05.2024 19:03

@Chris Мой код воспроизводим. Скопируйте любой фрагмент в свой текстовый редактор и создайте его, и он скомпилируется и будет вести себя так, как я сказал. Более того, он был встроен как форматированный текст. Когда я опубликовал свой пост, ОП еще не предоставил фрагмент кода.

Spencer Rosas-Gunn 02.06.2024 03:21
Ответ принят как подходящий

Проблема, скорее всего, в следующем (из функции fullPathOfFileName):

fullPath = malloc((strlen(notesDirName)+strlen(fileName)+3)*sizeof(char));

Вы копируете notesDirName в fullPath, и это нормально.

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

Затем вы добавляете fileName, и это тоже нормально.

Затем возникает проблема: вы добавляете ".txt" к строке.

Всего вы добавляете в строку strlen(notesDirName) + 1 + strlen(fileName) + 4 + 1 символов. Который выйдет за пределы и даст вам неопределенное поведение.

Мораль этой истории — не быть дешевым. Если вы не ориентируетесь на систему, о которой известно, что ее память ограничена (небольшие встроенные системы), памяти здесь достаточно. Черт возьми, даже на моей старой Amiga с памятью 512 КиБ памяти обычно было достаточно, чтобы обеспечить некоторый запас на отказ.

Каждый раз, когда вы сталкиваетесь с ситуацией «на некоторых машинах/компиляторах работает, а на других нет», «работает», скорее всего, случайность, чем «не работает».

Chris 31.05.2024 19:08

Ах! Я забыл добавить байты расширения файла. Я не знаю, как вообще работал код...

WyntrHeart 31.05.2024 19:13

В бесконечной сфере возможностей «неопределённого поведения» лежит «работает так, как ожидалось». В подобном случае это, вероятно, означает, что память после переполненного буфера случайно оказывается нулевой, что позволяет ей по-прежнему оставаться строкой с нулевым завершением.

Chris 31.05.2024 19:16

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