Как я могу найти длину указателя символа с помощью strlen () в C? - PullRequest
3 голосов
/ 03 июня 2019

Я даю понять, что мой вопрос является точной копией этого вопроса.

Но, к сожалению, у меня есть один вопрос, на который ни один из ответов не был адресован. Итак, код был: -

#include <string.h>

int foo(void) {
  char bar[128];
  char *baz = &bar[0];
  baz[127] = 0;
  return strlen(baz);
}

Вопрос был: каковы возможные выходы этой функции?

Когда я запускаю этот код, он каждый раз дает 0, а правильные ответы - 0 и 127 (я до сих пор не понял, почему?).

Мой вопрос заключается в том, насколько это утверждение является действительным Я имею в виду, что мы вычисляем длину baz, который содержит адрес памяти, скажем 0xb96eb740, который является шестнадцатеричным числом, поэтому мы делаем следующее. strlen() по этому адресу, чтобы найти это длина? Я имею в виду, как мы можем найти длину адреса , который является просто числом?

Я действительно смущен и пытаюсь понять это в течение часов , но все еще не понимаю.

Ответы [ 4 ]

8 голосов
/ 03 июня 2019

Не зацикливайтесь на том факте, что ему передают адрес.strlen() всегда принимает адрес.Это аргумент const char *, адрес строки.Все эти вызовы проходят по одному и тому же адресу:

strlen(baz);
strlen(&bar[0]);
strlen(bar);

baz назначается &bar[0], поэтому первый и второй эквивалентны. Массив распадается на указатель на свой первый элемент (array == &array[0]), поэтому второй и третий эквивалентны.

Я имею в виду, как мы можем найти длину адреса , который является просто числом?

Допустим, что bar == &bar[0] == baz == (char *) 0xb96eb740 согласно вашемупример.strlen() сначала проверит, содержит ли ячейка памяти 0xb96eb740 \0.Если нет, то он проверит 0xb96eb741.Тогда 0xb96eb742.Тогда 0xb96eb743.Он будет продолжать проверять каждое местоположение последовательно, пока не найдет \0.

Я знаю, что это правда.Но почему strlen(baz) возвращает 0?

Как объясняет связанный вопрос и ответ, поведение не определено , поскольку содержимое массива bar[128] неинициализировано.В этом массиве может быть что угодно.Единственная известная нам ячейка - это bar[127], которая установлена ​​в \0.Все остальные неинициализированы.

Это означает, что любой из них, или все, или ни один из них, может содержать символ \0.Это может измениться от бега к бегу, от звонка к звонку даже.Каждый раз, когда вы звоните foo(), вы можете получить другой результат.Это вполне возможно.Результат будет зависеть от того, какие данные находятся в стеке до вызова foo().

Когда я запускаю этот код, каждый раз выдается 0, а правильные ответы 0 и 127 (я до сих пор не понимаю, почему?).

Может возвращать любое значение в диапазоне от 0 до 127. Из-за неопределенного поведения вы не должны слишком много читать в том, чтопрограмма происходит , чтобы вернуться, когда вы запустите его.Вывод может быть другим, если вы снова запустите программу, если вы вызовете другой набор функций до foo(), если вы запустите другую программу заранее, если вы измените компиляторы, если вы запустите ее в другой день недели, есливы используете другую операционную систему и т. д. и т. д. и т. п.

2 голосов
/ 03 июня 2019

Ответ на ваш вопрос: может произойти все, что угодно .

Массив bar неинициализирован. Только bar[127] явно установлено на '\0'. Передача неинициализированного массива в strlen(), что вы делаете косвенно, передавая baz, который указывает на bar[0], имеет неопределенное поведение.

На практике в современных архитектурах без значений ловушек функция foo() имеет неопределенное поведение и может возвращать любое значение от 0 до 127 в зависимости от того, что содержится в стеке при его вызове.

В вашем случае он возвращает 0, поскольку в начале bar оказывается нулевой байт, но вы не можете полагаться на это, и последовательные вызовы foo() могут возвращать разные значения.

Если вы запускаете программу, которая вызывает foo() в valgrind или какой-либо другой инструмент очистки памяти, он может жаловаться, что strlen() обращается к неинициализированной памяти.

2 голосов
/ 03 июня 2019

Мой вопрос заключается в том, как это утверждение даже допустимо. Я имею в виду, что мы вычисляем длину baz, которая содержит адрес памяти, скажем, 0xb96eb740, который является шестнадцатеричным числом, поэтому мы выполняем strlen () по этому адресу, чтобы найти его длину

Функция strlen принимает адрес в качестве аргумента, и ее поведение заключается в чтении символа, хранящегося по этому адресу. (Он не пытается прочитать символы из адреса, как вы, похоже, предлагаете). Если этот символ не '\0', то он будет читать символ по следующему адресу и посмотреть, если это '\0' и т. Д.

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

Другие говорили, что значение не определено , поэтому я перехожу к следующему:

Я имею в виду, как мы можем найти длину адреса, которая является просто числом?

Нет.Длина строки рассчитывается путем последовательного считывания памяти с адреса, с которого вы хотите начать, и определения того, как далеко вы должны пройти, прежде чем наберете первый символ '\0'.Вот пример того, как вы можете реализовать функцию, которая возвращает длину строки:

int strlen(char * str) {
    int length=0;
    while(str[length] != '\0') 
        length++;
    return length;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...