2016-01-25 2 views
3

Я понимаю, что конец строки указан нулевым символом, но я не могу понять вывод следующего кода.Что делать, если нулевой символ присутствует в середине строки?

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

int 
main(void) 
{ 
    char s[] = "Hello\0Hi"; 
    printf("%d %d", strlen(s), sizeof(s)); 
} 

ВЫВОД:

Если strlen() обнаруживает конец строки в конце о, то почему не sizeof() делать то же самое? Даже если это не делает то же самое, не «\ 0» A нулевой символ (т. Е. Только один символ), так что ответ должен быть не 8?

+0

'main()' -> 'int main (void)' пожалуйста. –

+0

@PW, по-видимому, в стандарте c они не используют NUL, несмотря на множество таблиц ascii, где я его видел. В стандарте c используется слово * null * lowercase. –

ответ

16

Оператор sizeof не дает вам длину строки, а вместо этого размер его операнда. Так как в вашем коде операнд является массивом, sizeof дает вам размер массива, содержащий как null символов.

Если бы это было, как этот

const char *string = "This is a large text\0This is another string"; 
printf("%zu %zu\n", strlen(string), sizeof(string)); 

результат будет сильно отличаться, поскольку string является указателем, а не массив.

Примечание: Используйте "%zu" спецификатор для size_t что и strlen() возвращается, и это тип значения, даваемого sizeof.

+5

Хороший ответ, но добавьте пояснения о '% zu' операнде из-за типа' size_t' 'sizeof' return; – LPs

4

Если strlen() определяет конец строки в конце o, то почему sizeof() не делает то же самое?

strlen работает только для строки (массив символов), в то время как sizeof работ для каждого типа данных. sizeof вычисляет точные пространства памяти для любого заданного типа данных; тогда как strlen предоставляет длину строки (не включая терминатор NULL \0). Таким образом, в обычных случаях, это верно для типичного массива символов s:

char s[] = "Hello"; 
strlen(s) + 1 = sizeof(s); // +1 for the \0 

В вашем случае это отличается тем, что у вас есть терминатор NULL в середине символьного массива s:

char s[] = "Hello\0Hi"; 

Здесь strlen обнаружил бы первый \0 и дал бы длину как 5. sizeof, однако, вычислит общее количество пробелов, достаточное для хранения массивов символов, в том числе двух \0, так вот почему это giv es 9 в качестве второго выхода.

8

strlen() не имеет значения размер строки. Он ищет нулевой байт и останавливается, когда видит первый нулевой байт.

Но sizeof() Оператор знает общий размер. Не важно, какие байты вы используете в строковом литерале. Вы можете также иметь все нулевые байты в строке, а sizeof() все равно даст правильный размер массива (strlen() будет в этом случае извлекать 0).

Они не сопоставимы; они делают разные вещи.

+0

На самом деле, «strlen» действительно заботится о * фактическом * размере строки, потому что размер строки определяется как «nul terminated». Это sizeof, который не заботится о строках вообще, только о типах. – Daniel

+0

@ Daniel 'потому что размер строки определяется как« nul terminated »- Нет, это (нулевое завершение) не является ни строкой * size *, ни' strlen() 'возвращает * размер * строки. Это называется «длина строки», а не «размер строки». 'strlen()', по определению, не знает и не заботится о размере. Он начинается с данного адреса и ищет первый нулевой байт. Я поддерживаю ответ и, пожалуйста, не делайте снизу, потому что это не ясно для * you *. –

+0

Ах, извините, «string size» не имеет определения, strlen заботится о * длине строки, а sizeof заботится о количестве байтов типа! Нет «размера строки». – Daniel

1

Массивы символов в C и указатели на массив символов - это не то же самое. Хотя вы можете печатать адреса и получать одинаковое значение. Массив в C состоит из следующих вещей.

  1. Размер массива
  2. Его адрес/указатель
  3. Однородная Тип элементов

Если указатель состоит из всего:

  1. Адрес
  2. Тип информация

    char s[] = "Hello\0Hi"; printf("%d %d", strlen(s), sizeof(s));

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

Но если рассматривать этот массив символов в строке, чем массив (строка теперь) теряет информацию о своем размере и становится просто указателем на персонажа. То же самое происходит, когда вы пытаетесь напечатать массив символов, используя %s.

So strlen() и %s обрабатывать символьный массив как строку и использовать его адресную информацию. Вы можете догадаться, strlen() продолжает увеличивать указатель для вычисления длины от первого до нулевого символа. Когда он сталкивается с нулевым символом, вы получаете длину до этой точки.

Так что strlen() дает вам 5 и не учитывает null персонаж.

Оператор sizeof() сообщает только размер своего операнда. Если вы даете ему переменную массива, то она использует информацию о размере массива и сообщает размер независимо от нулевой позиции символа.

Но если вы даете sizeof()pointer to array of characters, то он находит указатель без информации о размере и печатает размер указателя, который обычно составляет 64 бит/8 байтов на 64-битных системах или 32 бит/4 байта на 32-битных системах.

Еще одна вещь, если вы инициализируете свои массивы символов, используя двойные кавычки, такие как "Hello", чем C, добавляет нулевой символ, иначе это не произойдет в случае {'H','e','l','l','o'}.

Использование gcc-компилятора. Надеюсь, это поможет только понять.

+0

Все объекты (включая массивы и указатели) имеют адрес и информацию типа. Ни одна из этих вещей не требует хранения. В случае массивов часть информации о типе - это количество элементов в массиве. –

4

strlen() вычисляет длину строки. Это делается путем возврата количества символов до (и не включая) символа '\0'. (См. Страницу руководства ниже.)

sizeof() возвращает количество байтов данной переменной (или типа данных). Обратите внимание, что ваш пример "Hello\0Hi" имеет 9 символов. Но вы, кажется, не понимаете, откуда в вашем вопросе возникает символ 9. Позвольте мне сначала объяснить данную строку.Ваш пример строка:

"Hello\0Hi" 

Это можно записать в виде следующего массива:

['H', 'e', 'l', 'l', 'o', '\0', 'H', 'i', '\0'] 

Обратите внимание на последний '\0' характер. При использовании строковых котировок компилятор завершает строку символом '\0'. Это означает, что "" также является ['\0'] и, следовательно, имеет 1 элемент.

BEWARE что sizeof() делает НЕ возвращает количество элементов в массиве. Он возвращает количество байтов. char 1 байт и поэтому sizeof() действительно возвращает количество элементов. Но если вы использовали какой-либо другой тип данных, например, если бы вы набрали sizeof() на [1, 2, 3, 4], он вернется 16. Поскольку int - это 4 байта, а массив имеет 4 элемента.

BEWARE, что передача массива в качестве параметра будет проходить только указатель. Если вы передадите s в другую функцию и вызовите sizeof(), он вернет размер указателя, то же самое, что и sizeof(void *). Это фиксированная длина, не зависящая от массива.

STRLEN(3)    BSD Library Functions Manual    STRLEN(3) 

NAME 
    strlen, strnlen -- find length of string 

LIBRARY 
    Standard C Library (libc, -lc) 

SYNOPSIS 
    #include <string.h> 

    size_t 
    strlen(const char *s); 

    size_t 
    strnlen(const char *s, size_t maxlen); 

DESCRIPTION 
    The strlen() function computes the length of the string s. The strnlen() 
    function attempts to compute the length of s, but never scans beyond the 
    first maxlen bytes of s. 

RETURN VALUES 
    The strlen() function returns the number of characters that precede the 
    terminating NUL character. The strnlen() function returns either the 
    same result as strlen() or maxlen, whichever is smaller. 

SEE ALSO 
    string(3), wcslen(3), wcswidth(3) 

STANDARDS 
    The strlen() function conforms to ISO/IEC 9899:1990 (``ISO C90''). 
    The strnlen() function conforms to IEEE Std 1003.1-2008 (``POSIX.1''). 

BSD       February 28, 2009       BSD 
+1

«ОЗНАЧАЙТЕ, что sizeof() НЕ возвращает размер массива» - на самом деле это так. Вы, кажется, имеете в виду, что «sizeof не дает количество элементов в массиве», но в этом конкретном случае это происходит, потому что элементы имеют размер 1. –

+0

Хорошая точка. Я изменил «размер» на «количество элементов», но мой ответ уже заявил, что в этом конкретном случае это правда. Таким образом, вы должны прочитать весь ответ, прежде чем комментировать полпути через следующий раз ;-) –

2

Как имя буквального сам по себе предполагает строковый литерал представляет собой последовательность символов, заключенных в двойных кавычках. Неявно эта последовательность символов добавляется оканчивающимся нулем.

Таким образом, любой символ, заключенный в двойные кавычки, является частью строкового литерала.

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

Каждый строковый литерал, в свою очередь, имеет тип массива символов.

Например, эта строка литерала "Hello\0Hi" в C имеет тип char[9]: 8 символов, заключенных в котировки плюс неявный завершающий нуль.

Таким образом, в памяти эта строка символов хранится как

{ 'H', 'e', 'l', 'l', 'o', '\0', 'H', 'i', '\0' } 

Оператор sizeof возвращает количество байтов, занимаемых объектом. Поэтому для строкового литерала над оператором sizeof будет возвращено значение 9 - это число байтов, занятых литералом в памяти.

Если вы написали "Hello\0Hi", тогда компилятор не может просто удалить эту часть Hi из буквального текста. Он должен хранить его в памяти вместе с другими символами литерала, заключенного в кавычки.

Оператор sizeof возвращает размер в байтах любого объекта в C не только из массивов символов.

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

Стандартная функция C strlen специально написана для массивов символов, чтобы найти длину сохраненной строки в массиве символов. Он не знает, какие данные хранятся в массиве и как они были написаны в нем. Все, что он делает, ищет первый нулевой символ в массиве символов и возвращает количество символов в массиве символов до нулевого символа.

Вы можете хранить в одном массиве символов несколько строк последовательно. Например

char s[12]; 

strcpy(s, "Hello"); 
strcpy(s + sizeof("Hello"), "World"); 

puts(s); // outputs "Hello" 
puts(s + sizeof("Hello")); // outputs "World" 

Если бы определить двумерный массив как этот

char t[2][6] = { "Hello", "World" }; 

то в памяти будет храниться так же, как одномерный массив выше. Таким образом, вы можете написать

char *s = (char *)t; 

puts(s); // outputs "Hello" 
puts(s + sizeof("Hello")); // outputs "World" 

Другой пример. Стандартная функция C strtok может разбивать одну строку, хранящуюся в массиве символов, на несколько строк, заменяя указанные пользователем разделителями нулевыми байтами. В результате массив символов будет содержать несколько строк.

Например

char s[] = "Hello World"; 

printf("%zu\n", sizeof(s)); // outputs 12 

strtok(s, " "); 

puts(s); // outputs "Hello" 
puts(s + sizeof("Hello")); // outputs "World" 

printf("%zu\n", sizeof(s)); // outputs 12 

Последнее Printf заявление будет выдавать то же самое значение, равное 12, так как массив занимает такое же число байтов. Просто один байт в памяти, выделенной для массива, был изменен с ' ' на '\0'.

+0

Примечание: Re: «Как и сам буквенный язык подразумевает строковый литерал ...», буквально не обязательно должна быть строка. '(int []) {2, 4}' является литералом (составным), а не строкой. – chux

+0

@chux Никто не сказал, что литерал является только строковым литералом. –

+0

Чтение «Поскольку сам буквенный символ подразумевает строковый литерал ...» означает, что _literal_ подразумевает _string literal_. – chux