Я относительно неопытный программист, и у меня возникла некоторая путаница по поводу экранированных обратных косых черт. Я использую строки, содержащие обратную косую черту, для представления путей к файлам 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() привело к тому, что сбои сбоя сегментации исчезли, но на самом деле я вообще забыл выделить память. для части моих струн, так что это «работало» только по счастливой случайности.
strlen("foo\\bar") — 7. Как вы думаете, почему экранированная обратная косая черта считается за 2 символа? Покажите код, который вы используете, который заставляет вас поверить, что он учитывается дважды; возможно, произошла какая-то банальная ошибка.
Вопросы должны быть самостоятельными. Не размещайте ссылки на код. Такие ссылки могут исчезнуть, а содержимое может измениться. А еще, когда вы пытаетесь создать минимально воспроизводимый пример всегда есть шанс, что вы сами разберетесь в проблеме.
Кстати, в Windows (а до нее MS-DOS) можно использовать косую черту / уже более 30 лет.
Я слышал, что код просто не работал в Windows, пока я не перешел на обратную косую черту. Лучшее, что я могу предположить, это то, что мне не нравилось смешивать косые черты из моего кода с переменными среды, которые я читаю, которые содержат пути обратной косой черты.
О, и никогда не делайте ничего подобного retVal=(retVal & (!!((!!((str[i]|32u)>='a')) && (!!((str[i]|32u)<='z')))));! В цикле используйте, например. if (!isalpha(str[i])) { return 0; }. После цикла просто return 1;. Простой, легкий для чтения, понимания и обслуживания, обрабатывает как верхний, так и нижний регистр. А также переносимый на системы, которые не используют ASCII.
Есть и другие вещи, которые можно было бы сделать в этом коде намного проще. Например, сортировка. Или все эти глобальные переменные (что является очень плохой привычкой).
Я не хотел добавлять дополнительный #include для одной функции, которую я мог бы написать в одной строке, хотя я не знал, что существуют системы, не использующие ASCII, поэтому я мог бы это изменить. .
Я не знаю, предполагается ли нам вести этот разговор в комментариях, но не могли бы вы подробнее рассказать о проблеме с моей сортировкой? В этом наспех созданном коде для обучения по ходу работы мне хотелось бы многое переработать, но сортировка, похоже, работает нормально, поэтому мне любопытно, что я делаю не так?
Сначала я бы сосредоточился на том, чтобы ваш код работал. В этот момент codereview.stackexchange.com является лучшим местом для критики рабочего кода.
Это работает прямо сейчас
@WyntrHeart Я не хотел добавлять целый дополнительный #include для одной функции, которую я могу написать сам в одной строке. Действительно, очень, очень плохая идея. Каждая строка кода, которую вы пишете, представляет собой ошибку, ожидающую своего появления, и ее нигде не тестируют так часто, как это делают стандартные функции. И если вы принесете неясный, запутанный, непонятный код, такой как retVal=(retVal & (!!((!!((str[i]|32u)>='a')) && (!!((str[i]|32u)<='z')))));, на любую профессиональную проверку кода, вас сразу вышвырнут. Если вы не видите, что делает код примерно за пять секунд, это ПЛОХОЙ код.
@AndrewHenle, это имеет большой смысл, и как новичок в самообучении я очень ценю профессиональный совет, спасибо. Это была довольно запутанная строка, особенно с таким количеством вложенных круглых скобок, поэтому, если я не смогу переписать ее во что-то читабельное, я отброшу ее и использую std isAlpha().





Строка имеет правильную длину. Рассмотрим следующий код.
#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 приводит к множеству подобных ошибок, если вы не уверены, что вызываете правильную функцию.
Это не проблема с нулевым байтом, я понимаю разницу. strlen("documents/notes") было 15 символов, как и ожидалось, а изменение на strlen("documents\\notes") увеличило его до 16 символов, и мне пришлось использовать malloc() дополнительный байт (17 байт вместо 16).
@WyntrHeart ваша вторая строка возвращает 15, а не 16.
Может ли это быть какая-то проблема с моей настройкой MSYS2 и/или MinGW? Потому что тестирование на моем телефоне Android дает правильную длину, а на моем ПК с Windows обратная косая черта считается дополнительным символом.
Быстрое прочтение связанного кода (который должен был представлять собой минимальный воспроизводимый пример, встроенный в форматированный текст) показывает, что использование sizeof вместо strlen не является проблемой. Этот ответ, похоже, не намного выше предположения без этого MRE из ОП.
@Chris Мой код воспроизводим. Скопируйте любой фрагмент в свой текстовый редактор и создайте его, и он скомпилируется и будет вести себя так, как я сказал. Более того, он был встроен как форматированный текст. Когда я опубликовал свой пост, ОП еще не предоставил фрагмент кода.
Проблема, скорее всего, в следующем (из функции fullPathOfFileName):
fullPath = malloc((strlen(notesDirName)+strlen(fileName)+3)*sizeof(char));
Вы копируете notesDirName в fullPath, и это нормально.
Затем вы добавляете косую черту (назад или вперед), и это нормально.
Затем вы добавляете fileName, и это тоже нормально.
Затем возникает проблема: вы добавляете ".txt" к строке.
Всего вы добавляете в строку strlen(notesDirName) + 1 + strlen(fileName) + 4 + 1 символов. Который выйдет за пределы и даст вам неопределенное поведение.
Мораль этой истории — не быть дешевым. Если вы не ориентируетесь на систему, о которой известно, что ее память ограничена (небольшие встроенные системы), памяти здесь достаточно. Черт возьми, даже на моей старой Amiga с памятью 512 КиБ памяти обычно было достаточно, чтобы обеспечить некоторый запас на отказ.
Каждый раз, когда вы сталкиваетесь с ситуацией «на некоторых машинах/компиляторах работает, а на других нет», «работает», скорее всего, случайность, чем «не работает».
Ах! Я забыл добавить байты расширения файла. Я не знаю, как вообще работал код...
В бесконечной сфере возможностей «неопределённого поведения» лежит «работает так, как ожидалось». В подобном случае это, вероятно, означает, что память после переполненного буфера случайно оказывается нулевой, что позволяет ей по-прежнему оставаться строкой с нулевым завершением.
Пожалуйста, отредактируйте свой вопрос, включив в него минимальный воспроизводимый пример и то, как вы считаете размер строк.