Форум русскоязычного сообщества Ubuntu


Хотите сделать посильный вклад в развитие Ubuntu и русскоязычного сообщества?
Помогите нам с документацией!

Автор Тема: Строки в C  (Прочитано 1703 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Jack Sparrow

  • Автор темы
  • Активист
  • *
  • Сообщений: 630
    • Просмотр профиля
Строки в C
« : 10 Апреля 2012, 19:32:03 »
Штудирую K&R. Пытаюсь разобраться со строками в С. Но они сводят меня с ума.
Как я понимаю, строка в С это массив символов, заканчивающийся на '\0'. Т.е., например, английский алфавит состоит из 26 букв. Значит в массиве его индексы будут от 0 до 25. Ну и еще на символ '\0'. Значит, чтобы содержать английский алфавит нужно создать массив длиной 27 элементов.
char s1[26] = "abcdefghijklmnopqrstuvwxyz";
Логика проста, как кажется: сколько букв, такое же и число в квадратных скобках. А если нужно хранить только две буквы, например, 'r' и 's',  то, по аналогии, пишем в скобках 2:
char s2[2] = "rs";
Но если попытаться распечатать по очереди элементы этих массивов, то ведут они себя по разному:
Код: (c) [Выделить]
#include <stdio.h>

int main()
{
    char s1[26] = "abcdefghijklmnopqrstuvwxyz";
    char s2[2] = "rs";
   
    int i;
    for (i = 0; s1[i] != '\0'; i++)
        printf("%d\t%c\n", i, s1[i]);
   
    putchar('\v'); // визуально отделим одно от другого
   
    for (i = 0; s2[i] != '\0'; i++)
        printf("%d\t%c\n", i, s2[i]);
   
    return 0;
}
Если у первой строки с алфавитом вывод, вроде бы, какой и должен быть (печатаются элементы с 0 по 25), то у второго массива что-то странное.
(Нажмите, чтобы показать/скрыть)
Но если второй массив создать на один элемент длиннее, то все получается нормально:
char s2[3] = "rs";
(Нажмите, чтобы показать/скрыть)
Получается, что s2[2] = '\0'. А что такое для чего нужен s2[3] ? И почему в первом случае (с полным алфавитом), сколько букв, такое и число, а тут не так?
Не пойму, где я туплю?
« Последнее редактирование: 10 Апреля 2012, 19:34:58 от yorik1984 »
Linux is only free if your time has no value (c) Jamie Zawinski

Оффлайн Olej

  • Забанен
  • Активист
  • *
  • Сообщений: 884
    • Просмотр профиля
Re: Строки в C
« Ответ #1 : 10 Апреля 2012, 19:49:05 »
Логика проста, как кажется: сколько букв, такое же и число в квадратных скобках. А если нужно хранить только две буквы, например, 'r' и 's',  то, по аналогии, пишем в скобках 2:

Логика неверная ;).
Для того, чтобы хранить N латинских букв нужно размерность массива N+1 (для '\0' завершающего).

Но сильно не увлекайтесь таким счётом:
- в зависимости от кодировка символы могут занимать не 1, а от 1 до 4 байт (коды UNICODE)...
- самая распространённая кодировка в Linux на сегодня это UTF-8, в ней латинские буквы будут по 1 байту, а русские - 2 (UTF-8 это тип кодирования для UNICODE);
- но можете попасться и на кодировки UTF-16 и UTF-32 (это всегда 4 байта на символ - чистый UNICODE);

И, в зависимости от этого, в языке C вы можете столкнуться с 3-мя типами для представления символьных данных:
1. char ... ну это то, что вы смотрите;
2. mbchar - это UTF-8;
3. wchar - это UTF-32;

Для каждого типа будут и свои API для работы с ними, например: wstrlen() ...

P.S. посмотрите на вывод:
printf( "%d", strlen( "это русскоязычная строка" ) );и вы будете приятно удивлены.
 


Пользователь решил продолжить мысль 10 Апреля 2012, 19:53:26:
Получается, что s2[2] = '\0'. А что такое для чего нужен s2[3] ? И почему в первом случае (с полным алфавитом), сколько букв, такое и число, а тут не так?
Не пойму, где я туплю?
Не получается ;)
Счёт элементов массива начинается с 0.
В вашем случае:
char s2[3] = "rs";
s2[0] - это 'r'
s2[1] - это 's'
s2[3] - это '\0'
- вот они все 3 и есть.

P.S. вы записывайте объявления вот в таком виде:
char *s2 = "rs";- у вас и проблем поубавится.
« Последнее редактирование: 10 Апреля 2012, 19:53:26 от Olej »

Оффлайн qupl

  • Активист
  • *
  • Сообщений: 286
  • и тебя забанят, и меня забанят
    • Просмотр профиля
Re: Строки в C
« Ответ #2 : 10 Апреля 2012, 20:20:46 »
Извиняюсь, что вмешиваюсь, но в С, ПМСМ, обращение к N-ному элементу массива размером N - это моветон. Хотя со статическими символьными массивами такое работает.
Муха (ё).
Муха - это насекомое. А "ё" - это буква, кому не хватает ее на клавиатуре.

Оффлайн Olej

  • Забанен
  • Активист
  • *
  • Сообщений: 884
    • Просмотр профиля
Re: Строки в C
« Ответ #3 : 10 Апреля 2012, 20:23:54 »
Пытаюсь разобраться со строками в С. Но они сводят меня с ума.
Если сводят с ума, вы можете ещё пользовать строки ... почти С ;) - С++ :
#include <string>из состава STL.
Которые представлены как Pascal-строки: сначала число длина - затем уже сама строка.

Кроме того, у вас есть ещё альтернатива: использовать строки их APR - Apache Portable Runtime - http://www.ibm.com/developerworks/ru/library/os-apache_8/: apr_strings.
Это платформенно независимый слой API (Linux, Solaris, Windows, ...).
Очень неплохое решение: многие серьёзные крупные проекты, и ориентированные на UNIX - не используют строк С, а используют APR, например SIP телефонные станции: Asterisk, FreeSWITCH, ...
 


Пользователь решил продолжить мысль 10 Апреля 2012, 20:24:56:
Извиняюсь, что вмешиваюсь, но в С, ПМСМ, обращение к N-ному элементу массива размером N - это моветон. Хотя со статическими символьными массивами такое работает.

До поры до времени ;)...

Оффлайн qupl

  • Активист
  • *
  • Сообщений: 286
  • и тебя забанят, и меня забанят
    • Просмотр профиля
Re: Строки в C
« Ответ #4 : 10 Апреля 2012, 20:27:12 »
До поры до времени ;)...
И я о том же. Надежда очень слаба и ее нужно гнать от себя.
Муха (ё).
Муха - это насекомое. А "ё" - это буква, кому не хватает ее на клавиатуре.

Оффлайн Jack Sparrow

  • Автор темы
  • Активист
  • *
  • Сообщений: 630
    • Просмотр профиля
Re: Строки в C
« Ответ #5 : 10 Апреля 2012, 20:36:47 »
С кодировками я уже подозревал, что что-то будет не все просто - в Питоне, примерно, то же самое: длина русских строк в два раза длиннее латинских.
До конструкций вида char *s2 = "rs"; еще не дошел, это указатели, что ли? Но все равно, спасибо, буду пользоваться.

Тем не менее, мне не понятно, чем отличается
char s1[26] = "abcdefghijklmnopqrstuvwxyz";
char s2[2] = "rs";
Ведь кодировки, вроде бы, одинаковые, однобайтовые. Почему же такая разница?
Вообще-то, прочитал, что в таких случаях можно вообще не указывать длину
char s1[] = "abcdefghijklmnopqrstuvwxyz";
char s2[] = "rs";
и компилятор сам определит правильный размер.

Цитировать
Если сводят с ума, вы можете ещё пользовать строки ... почти С  - С++ :
Это все, разумеется, в учебных целях. Как мне кажется, С не слишком заточен для работы с текстами.
Linux is only free if your time has no value (c) Jamie Zawinski

Оффлайн Olej

  • Забанен
  • Активист
  • *
  • Сообщений: 884
    • Просмотр профиля
Re: Строки в C
« Ответ #6 : 10 Апреля 2012, 20:49:30 »
С кодировками я уже подозревал, что что-то будет не все просто - в Питоне, примерно, то же самое: длина русских строк в два раза длиннее латинских.
Питон здесь не при чём - это Linux + UTF8, это будет проявляться в любых языках.

До конструкций вида char *s2 = "rs"; еще не дошел, это указатели, что ли? Но все равно, спасибо, буду пользоваться.
Вообще-то, прочитал, что в таких случаях можно вообще не указывать длину
char s1[] = "abcdefghijklmnopqrstuvwxyz";
char s2[] = "rs";
и компилятор сам определит правильный размер.
А это и есть эквивалентные описания:
char *s1 = "rs";
char s2[] = "rs";

Тем не менее, мне не понятно, чем отличается
char s1[26] = "abcdefghijklmnopqrstuvwxyz";
char s2[2] = "rs";
Ведь кодировки, вроде бы, одинаковые, однобайтовые. Почему же такая разница?
Да нет нигде никакой разницы ... то вы буквы не так считаете ;)
А вообще - не берите дурного в голову: никому никогда считать буквы не приходится.

Как мне кажется, С не слишком заточен для работы с текстами.
А он никогда и не затачивался на работу с символьными данными.
Самая частая причина SIGSEGV (это когда ошибка обращения и всё падает в кору) - это работа с символьными API.

Оффлайн qupl

  • Активист
  • *
  • Сообщений: 286
  • и тебя забанят, и меня забанят
    • Просмотр профиля
Re: Строки в C
« Ответ #7 : 10 Апреля 2012, 20:56:32 »
Jack Sparrow,
"Феномен" твоего примера я тебе на пальцах могу объяснить. Компилятор расположил твои два массива один за другим. Когда ты добавил 3-ий элемент, он заполнился нулем и всё сработало как ты планировал. Работа с массивами в С небезопасна - ответственность за контроль конца массива лежит на программисте.
Муха (ё).
Муха - это насекомое. А "ё" - это буква, кому не хватает ее на клавиатуре.

Оффлайн Jack Sparrow

  • Автор темы
  • Активист
  • *
  • Сообщений: 630
    • Просмотр профиля
Re: Строки в C
« Ответ #8 : 10 Апреля 2012, 21:02:16 »
Мдя, просветления пока еще нет.  :idiot2:

Ну ничего. Зато упражнение выполнено, можно переходить к следующей главе.
Linux is only free if your time has no value (c) Jamie Zawinski

Оффлайн Vovaldo

  • Любитель
  • *
  • Сообщений: 82
  • Ху Тин Пуй
    • Просмотр профиля
    • Путин забрал у Медведева инновационный орган
Re: Строки в C
« Ответ #9 : 11 Апреля 2012, 07:35:18 »
А это и есть эквивалентные описания:
char *s1 = "rs";
char s2[] = "rs";
Строго говоря - это не совсем эквивалентные описания. И в некоторых реализациях они могут оказаться совсем не эквивалентными. :coolsmiley:

Оффлайн Yurror

  • Старожил
  • *
  • Сообщений: 1966
    • Просмотр профиля
Re: Строки в C
« Ответ #10 : 11 Апреля 2012, 07:47:59 »
0   r
1   s
2   a
3   b
4   c
5   d
6   e
7   f
8   g
9   h
10   i
11   j
12   k
13   l
14   m
15   n
16   o
17   p
18   q
19   r
20   s
21   t
22   u
23   v
24   w
25   x
26   y
27   z

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

Пользователь решил продолжить мысль 11 Апреля 2012, 07:50:54:
А это и есть эквивалентные описания:
char *s1 = "rs";
char s2[] = "rs";
Строго говоря - это не совсем эквивалентные описания. И в некоторых реализациях они могут оказаться совсем не эквивалентными. :coolsmiley:
абсолютно не эквивалентные. мало того первая еще и сегфолтом грозит хотя и является якобы разрешённой в Си
в первом случае на стеке лежит лишь адрес строчки которая сама находится скорее всего в readonly секции,
во втором случае на стеке лежит сам массив и доступен для изменения.
« Последнее редактирование: 11 Апреля 2012, 07:50:54 от Yurror »

Оффлайн Jack Sparrow

  • Автор темы
  • Активист
  • *
  • Сообщений: 630
    • Просмотр профиля
Re: Строки в C
« Ответ #11 : 11 Апреля 2012, 09:57:48 »
Свой устав сам напрашивается, т.к. тема С строк подробно не разжевана, или еще не дошел до нее.
Уже в нескольких упражнениях была подобная ошибка, когда что-то не работает, разбираешься, а оказывается, что это проблема не в логике программы, а в типах данных.
Linux is only free if your time has no value (c) Jamie Zawinski

Оффлайн qupl

  • Активист
  • *
  • Сообщений: 286
  • и тебя забанят, и меня забанят
    • Просмотр профиля
Re: Строки в C
« Ответ #12 : 11 Апреля 2012, 10:33:02 »
В примере из первого поста проблема именно в логике программы.
Муха (ё).
Муха - это насекомое. А "ё" - это буква, кому не хватает ее на клавиатуре.

Оффлайн Jack Sparrow

  • Автор темы
  • Активист
  • *
  • Сообщений: 630
    • Просмотр профиля
Re: Строки в C
« Ответ #13 : 11 Апреля 2012, 11:18:26 »
Какая проблема с логикой? Ведь если объявить массивы без указания размера
char s1[] = "abcdefghijklmnopqrstuvwxyz";
char s2[] = "rs";
то все работает. Простой перебор. Ошибка именно с типами данных.
Linux is only free if your time has no value (c) Jamie Zawinski

Оффлайн Vovaldo

  • Любитель
  • *
  • Сообщений: 82
  • Ху Тин Пуй
    • Просмотр профиля
    • Путин забрал у Медведева инновационный орган
Re: Строки в C
« Ответ #14 : 11 Апреля 2012, 11:45:00 »
Какая проблема с логикой? Ведь если объявить массивы без указания размера
char s1[] = "abcdefghijklmnopqrstuvwxyz";
char s2[] = "rs";
то все работает. Простой перебор. Ошибка именно с типами данных.
Ошибка именно с логикой.
Сами ведь написали:
Цитировать
Значит, чтобы содержать английский алфавит нужно создать массив длиной 27 элементов.
И тут же
char s1[26] = "abcdefghijklmnopqrstuvwxyz";создали массив из 26 символов. ;D
При определении массива - число в квадратных скобочках - его размерность. А при использовании - индекс. Они на единицу отличаются.

А в случае
char s1[] = "abcdefghijklmnopqrstuvwxyz";размерность массива автоматически будет 27.
« Последнее редактирование: 11 Апреля 2012, 11:48:17 от Vovaldo »

 

Страница сгенерирована за 0.051 секунд. Запросов: 25.