C, sprintf и сумма строк и int

Я долгое время не использовал C, и теперь мне нужно изменить небольшой фрагмент кода. Одного не могу понять:

char filename[20];
filename[0] = '\0';
for (j=0; j < SHA_DIGEST_LENGTH; j++){
  sprintf(filename + strlen(filename),"%02x",result[j]);
}

В первой строке объявляется строка из 20 символов. Во второй строке первый символ установлен в '\0', так что, я полагаю, это пустая строка.

В цикле for я не понимаю "сумму" между именем файла и его длиной... Первым параметром sprintf должен быть буфер, куда копировать отформатированную строку справа. Каков результат этой суммы? Мне кажется, что я пытаюсь суммировать массив и целое число...

Что мне не хватает?


person Segolas    schedule 08.07.2010    source источник


Ответы (5)


Это арифметика указателей. strlen возвращает количество символов до терминатора NUL. Результат сложения будет указывать на этот терминатор. Например. если текущая строка имеет значение «AA» (за которым следует NUL), значение strlen равно 2. filename + 2 указывает на NUL. Он запишет следующие шестнадцатеричные символы (например, BB) поверх NUL и следующего символа. Затем он снова завершит его с помощью NUL (в filename + 4). Итак, у вас будет «AABB» (тогда NUL).

Хотя на самом деле это не имеет смысла. Это тратит много времени на поиск этих NUL. В частности, это квадратичный алгоритм. В первый раз проверяется 1 символ, затем 3, 5, 7, ..., 2 * SHA_DIGEST_LENGTH — 1) тот . Это может быть просто:

sprintf(filename + 2 * j,"%02x",result[j]);

Есть еще одна проблема. Шестнадцатеричное представление суммы SHA-1 занимает 40 символов, поскольку для байта требуется два символа. Затем у вас есть конечный терминатор NUL, поэтому должно быть 41. В противном случае происходит переполнение буфера.

person Matthew Flaschen    schedule 08.07.2010
comment
@zebediah, что ты имеешь в виду? sprintf всегда будет добавлять терминатор NUL. - person Matthew Flaschen; 08.07.2010
comment
It will write the next hex character (e.g. C) over this NUL, then NUL-termiante it again Добавляет ли sprintf \0 по умолчанию? - person Amarghosh; 08.07.2010
comment
К сожалению... да, это так - person Amarghosh; 08.07.2010
comment
+1 за указание на неэффективный алгоритм и ошибку длины массива. И за объяснение арифметики указателей :) - person Mark Pim; 08.07.2010
comment
Я не могу поставить +1 из-за своей репутации (похоже, я никуда не годен!), но это очень хороший ответ! - person Segolas; 08.07.2010

Почему бы вам не объявить

char filename[SHA_DIGEST_LEN*2 +1]; /* И +1, если вы хотите, чтобы завершающий символ NULL*/

Это связано с тем, что длина дайджеста SHA1 составляет 20 байт, и если вы просто печатаете дайджест, то вам, возможно, не нужна дополнительная память, но поскольку вам нужна шестнадцатеричная строка дайджеста, вы можете использовать приведенное выше объявление. Операция strlen возвращает длину строки до тех пор, пока не встретится нулевой завершающий символ.

Итак, в основном, когда вы делаете следующее:

sprintf(filename + strlen(filename),"%02x",result[j]);

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

После второй итерации он становится AABB. После 20-й итерации у вас есть вся строка AABBCC......AA[40 байт] и +1, если вам нужен '\0', который является завершающим символом NULL.

person Praveen S    schedule 08.07.2010

Первая итерация, когда j = 0, вы запишете 3 символа (да, включая '\0', завершающий строку) в начало filename, так как strlen() затем возвращает 0. В следующем раунде strlen() возвращает 2, и он продолжит запись после первые два символа.

Будьте осторожны, чтобы выйти за пределы выделенного пространства в 20 символов. Распространенной ошибкой является забывание места, необходимого для разделителя строки.

EDIT: убедитесь, что SHA_DIGEST_LENGTH не больше 9.

person MattBianco    schedule 08.07.2010

вы добавляете strlen (имя файла) только для объединения результата [j]

Каждая итерация объединяет текущий результат [j] в конце имени файла, поэтому каждый раз вам нужно знать смещение в имени файла, где должно происходить объединение.

person Daniel Băluţă    schedule 08.07.2010

Замените код на:

char filename[SHA_DIGEST_LENGTH*2+1];
for (j=0; j < SHA_DIGEST_LENGTH; j++){
  sprintf(filename + 2*j,"%02x",result[j]);
}

Быстрее, проще и ошибок больше нет.

person R.. GitHub STOP HELPING ICE    schedule 08.07.2010