SLC21 Week3 - Strings in C

in #slc21w3sergeyk12 days ago (edited)


image.png

EN

As you may have noticed, I often assign tasks that transform into something else in subsequent lessons – either they continue their development or the assignment changes drastically. One of the tasks from the last lesson was the question, "Can an array have two sizes?" However, due to automatic translation, the assignment shifted to the topic of 2DArrays. "Can an array have two dimensions?"
So, can an array have two sizes? Sooner or later, as you work with arrays, you might come to this thought.

For instance, you have an array with 6 elements, and you need to add another one. This is impossible because when an array is declared, memory is allocated for the array’s elements. Expanding or reducing this memory is impossible.
This limitation led to declaring arrays larger than needed, and eventually, this parameter became known as capacity – the physical size, the actual space occupied by the array.
Thus, we can declare an array with a size of 1000 elements, which is the real size, the physical size of the array, as exactly this much memory is allocated for it. Additionally, we can declare a variable int size=10;, which will be the real size of our array. All array processing loops will refer to this variable to determine the size of the array. But now, if we want to "expand" the array, we simply need to do size++; or size--;, and the size will increase or decrease. Of course, this change is virtual, and the real size of the array remains 1000.

Therefore, with two sizes for an array, it became possible to add and remove elements from it. It’s very easy to add/remove elements at the end of an array. a[size++]=element; The size variable is not the index of the last element. It represents the number of elements, so the index of the last one is size-1, and the next element will have an index of size. So, the value of element should be written to a[size] like this: a[size]=element; and then increase the number of elements by size++;

Removing an element from the end is even easier – size--; meaning the value of the element still exists in a[size], but we no longer see it, as the last element in the array is now a[size-1].
To remove an element from a specific position, we need to place the last element in its place and reduce the array size. (This is where garbage comes from when declaring variables and arrays – we removed elements but didn’t turn them to zero, erase, or reset them, so they remain in their original places, although they are no longer considered part of the array.)

To work with an array, you need to know two characteristics – the initial address and the number of elements.
This information is also necessary to "pass" the array into a function. In fact, the array is not passed into the function; only its starting address and size are passed. That’s why a function can change information in arrays, but a function call cannot change a regular variable.

We cannot yet write functions that add or remove elements from an array, as this would require increasing or decreasing its size. For now, we will change the size after calling the function.
(To make a function change a variable’s value, you need to pass not the value of the variable but the variable itself. But what is the variable itself? It’s its address. This is where the concept of pointers comes in, and it’s complex for beginners.)

Strings.

How do we store text information in C (C++)?

There is a variable type for storing a single character, char. Remember, this is a regular numeric type (although with a small range). If you interpret the value recorded in the variable as a number, it will be a number. If interpreted as a character, it will store not the character itself, but its code according to the ASCII table.

text = "this is some text"; – this can be written in some languages, even in C++ if text is a string, but we’ll talk about C-style strings, which are referred to as C-style strings.
C-style strings are a sequence of characters that ends with a null character. So, strings are arrays of characters. However, all string processing functions do not process the entire array; they process the string until they encounter a null character, or more precisely, a character with a null code.
It’s easy to confuse the null character, which is visible as '0' with an ASCII code of 48, with the null code character, '\0', which has an ASCII code of 0. This character belongs to the non-printable symbols, with ASCII codes from 0 to 31. The first visible symbol is actually invisible((

To store text information in C, character arrays are used: char text[50]="string"; Now, tell me, what is the length of the text string? Six – the length of the text string is six characters. And how much space does it take in memory? Six bytes? (One character is one byte.) The string occupies 50 bytes in memory because that’s the specified size of the array. So, again, it seems the array has two sizes – one physical (material), which is 50, as we can store up to 50 characters...actually, 49 characters, as space needs to be reserved for the null-terminator '\0'.

Array tasks are very important for beginners. Since loops and conditional operators are vital in programming – they are used thousands of times. Therefore, it’s good to get a lot of practice here.

So tasks on numeric arrays and tasks on strings are not different. With strings, it’s even better – they have one special feature – they "print themselves" on the screen.

int a[6]={1,2,3,4,5,6};
char b[]="123456";
for(int i=0; i<6; i++)
    cout<<a[i]<<" "; //printf("%d ", a[i]);

for(int i=0; i<6; i++)
    cout<<b[i]<<" "; //printf("%c ", b[i]);

cout<<"\n"<<a<<"\n";
cout<<b<<"\n";

As we saw in the last lesson, cout<<"\n"<<a<<"\n"; will output the address of array a, but cout<<b<<"\n"; will output the entire array b (without character-by-character printing with a loop).
By the way, if I had written char b[6] = "123456";, this would cause an error because the compiler automatically adds a null character to the character array, so the array size would need to be 7.

If you need to print a character array character by character, this can be done conveniently with a while loop.

char s[]="123456";
int i=0;
while(s[i])
    //while(s[i]!=0)    can be written this way
    //while(s[i)!='\0') or this way
{
   cout<<s[i];
}

We process the string to the end, and the end will be when the character with code 0 appears.

Homework

You cannot immediately write a program that will work correctly and thereby complete the task. It doesn’t work that way; most of the time, if not half, is spent on finding errors, both syntactic and logical. Many people write little text, only providing the code and one or two sentences describing either the code or rephrasing the problem statement.
It’s essential to describe the solution process. That is, initially, you had a certain idea, but when you implemented it in code, it turned out to be completely incorrect (or partially incorrect, as it sometimes gives a false result). You should describe your incorrect or erroneous path. Describe how you arrived at the correct (in your opinion) solution through mistakes.
Do not use library functions for working with text – work with the string as an array.
If you cannot immediately solve the task, I recommend solving all tasks in three steps:

  • First, create a mock solution, meaning do not transform the array but simply output the desired result. (Simulate the solution)
  • Solve the task using an additional array. That is, initially enter the result into an additional array, and after forming it, copy it into the original array, as the task was to change this array in a certain way.
  • Completing the previous two steps will help understand how to solve the task without such "relaxation."

If the task requires increasing the string length after completion, take care of this by reserving additional space when declaring the array.

If the length of the string should increase after completing the task, take care of this when declaring the array, reserving additional space.

Tasks

  1. (3 points) Practically (i.e., with code examples) explain the theory from the first part of this lesson, where the concept of two sizes of an array is discussed. Demonstrate how to make it look like the array size can be increased/decreased. All loops should work with the array size stored in the size variable. Keep the physical, actual size in the constant N.
  1. (1 point) Declare a string variable (store any sentence in the array). Task: reverse the string, i.e., write it backward. For example: char s[]="ABCDEF";.....your code.....cout<<s; => FEDCBA

  2. (1 point) Swap neighboring letters char s[]="ABCDEF";.....your code.....cout<<s; => BADCFE

  3. (1.5 points) Shift the string cyclically to the left (it’s easier to start with this), then cyclically to the right. char s[]="ABCDEF", x[]="abrakadabra";.....your code.....cout<<s<<"\n"<<x; => BCDEFA aabrakadabr

  4. (1.5 points) Remove all vowel letters char s[]="this is some text";...your code...cout<<s; => ths s sm txt

  5. (2 points) Double each vowel letter char s[]="this is some text";...your code...cout<<s; => thiis iis soomee teext

Additional task (1-2 points) - as a replacement for any of the tasks 2-6: Choose any number, preferably slightly larger than the length of the text. Increase the text length to the specified number by adding spaces between words. char s[]="this is some text";...your code...cout<<s; => (this is some text) len of s => 17 number =27

Contest Rules

You can publish on any language, in any community, or simply on your own blog, and add the link to your work as a comment here.

To ensure I can quickly find, check, and assess your works, leave the link in a comment under this text and use the hashtag #slc21w3sergeyk in your work.

Provide code for each task as a screenshot, not as text. Demonstrate the result of the program's work with a screenshot as well.

Be cautious about ideal or overly efficient solutions; it is challenging for a regular person or beginner to find them.

Do not provide solutions to tasks using materials that have not been covered yet. For example, arrays we haven’t covered yet. This restriction does not apply to students already practically familiar with programming and who provide expanded responses to tasks in a way similar to a lecture.

Plagiarism and using AI are prohibited.

Participants must be verified and active platform users.

Images used must belong to the author or be copyright-free. (Do not forget to indicate the source.)

Participants must not use any bot services for voting, participate in vote-buying, or use other unfair methods.

Recommend participating to your friends.

Entries should be published from Monday, Nov 11, 2024, to Sunday, Nov 17, 2024.

Your works will be commented on, evaluated, and four of the best works will be selected by me.

UA

Як ви помітили я часто даю завдання які перевтілюються в наступних уроках на щось інше - або продовжують свій розвиток, або кардинально змінюють завдання. Одним із завдань на минулому уроці будо таке запитання "Чи може бути у масивах два розміри?" Але через автоматичний переклад завдання змінилося на тему 2DArrays.
То чи може бути у масива два розміри - рано чи пізно при роботі з масивами ви прийшли б до такої думки.
Наприклад є масив з 6 елементів і необхідно додати до нього ще один. Це зробити неможливо - адже при оголошенні масиву під елементи масиву виділяється пам'ять. І розширити/зменшити цю пам'ять неможливо.
Це обмеження призвело до того що масив стали оголошувати більшим ніж треба, в подальшому цей параметр звуть capacity - місткість, тобто я б це назвав фізичним розміром, це той простір який займає масив.
Тобто ми можемо оголосити масив на 1000 елементів, це буде реальний розмір масиву, фізичний розмір масиву, рівно стільки пам'яті виділено під масив. І на додачу до цього можна оголосити змінну int size=10; тобто реальний розмір нашого масиву буде 10, і всі цикли обробки масиву будуть звертатися до цієї змінної і дізнаватися розмір масиву. Але тепер якщо ми захочемо "збільшити" масив - то варто зробити лиш size++; або size--; і розмір зменшиться. Звісно що ця зміна фіктивна, і реальний розмір масиву як був 1000 так і лишається 1000.
Отже завдяки двом розмірам масиву - з'явилася можливість додавати та видаляти елементи з масиву. Дуже просто додавати/видаляти елементи в кінець масиву. a[size++]=element; Змінна size це не індекс останнього елемента. Це кількість елементів - а отже індекс останнього буде size-1, наступний елемент матиме індекс size, отже значення element слід записати в a[size] так : a[size]=element; І після цього збільшити кількість елементів size++;
Видаляти елемент з кінця ще простіше - size--; тобто значення елемента там і залишилося, воно тепер знаходиться в a[size] Але ми його не бачимо, його в масиві нема так як останній елемент масиву a[size-1]
Для того щоб видалити елемент в певній позиції, слід на його місце поставити останній елемент і зменшити розмір масиву. (Тепер зрозуміло звідки береться сміття при оголошенні змінних та масивів - ми ж елементи видаляли, але не перетворювали на нуль не стирали, не обнуляли, вони залишилося на своїх місцях, лиш тепер не числяться за масивом)
Щоб користуватися масивом слід знати його дві характеристики - початкову адресу і кількість елементів.
Ця ж інформація необхідна щоб "передати" масив у функцію. Насправді масив у функцію не передається, передається адреса його початку і розмір. Саме тому функція може змінювати інформацію в масивах. Але виклик функції так працює, що не зможе змінити звичайну змінну.
Ми не можемо ще написати функції що додають та видаляють елементи з масиву - адже для цього слід збільшити/зменшити його розмір. Поки що будемо змінювати розмір після виклику функції.
(Щоб функція змінила значення змінної, слід передавати не значення змінної а саму змінну, а що таке сама змінна - це її адреса. Тут треба вивчити поняття вказівників, а воно складне для початківців)

Рядки.
Як на мові С(С++) зберігати текстову інформацію?
Є змінна для збереження одного символа - тип такої змінної char Не забувайте що це звичайний числовий тип(хоч і з малим діапазоном). Якщо трактувати значення записану в змінну як число - це буде число, якщо ж трактувати як символ - то у змінній записаний не сам символ - а його код за таблицею ASCII.

text = "this is some text"; - так можна писати на деяких мовах, навіть на С++ якщо text це string, але ми будемо говорити про рядки в стилі С - вони так і звуться C-style string
Рядки в стилі С це послідовність символів що закінчується нулем. Тобто рядки - це масиви символів. Але всі функції роботи з рядками не обробляють весь масив, вони обробляють рядок доти - доки е зустрінуть символ нуль, точніше символ з кодом нуль.
Їх легко сплутати це символ нуль, який видимий '0', код за таблицею ASCII 48, а це символ з нулевим кодом, кодом нуль '\0' тобто його код за таблицею ASCII - нуль Цей символ належить до недрукованих символів, тобто такі символи ніяк не відображаються, їх коди в таблиці ASCII від 0 до 31. Але перший же видимий символ - невидимий((

Для того щоб зберігати текстову інформацію на мові С - використовують масиви символів: char text[50]="string"; А тепер скажіть - яка довжина рядка text? - шість, довжина рядка text шість символів. А скільки він займає в пам'яті місця? Шість байт? (Один символ - один байт) - рядок займає в пам'яті 50 байт - саме цей розмір вказаний при оголошенні масиву. Виходить знов ніби у масива два розміри - один фізичний(матеріальний) 50, адже максимум ми зможемо записати 50 символів.... насправді 49 символів - так як слід зарезервувати місце і під символ-термінатор '\0'

Задачі на масиви - дуже важливі задачі для початківців. Адже цикли та умовний оператор займають важливе місце в програмуванні - їх там тисячі. Тому бажано тут отримати багато практики.

Тобто задачі на числові масиви і задачі на рядки - нічим не відрізняються. З рядками навіть краще - у них є одна особливість - вони "самі" друкуються на екрані.
int a[6]={1,2,3,4,5,6};
char b[]="123456";
for(int i=0; i<6; i++)
cout<<a[i]<<" "; //printf("%d ", a[i]);

for(int i=0; i<6; i++)
cout<<b[i]<<" "; //printf("%c ", b[i]);

cout<<"\n"<<a<<"\n";
cout<<b<<"\n";

Як ми бачили на останньому уроці cout<<"\n"<<a<<"\n"; виведе адресу масива a, але cout<<b<<"\n"; виведе весь масив b(без посимвольного друку циклом)
До речі, якби я написав char b[6] = "123456"; - це викликало б помилку, так як компілятор автоматично дописує нуль до масиву символів - а тоді розмір масиву мав би бути 7;

Якщо ж потрібно роздрукувати символьний масив посимвольно, це зручно робити з допомогою циклу while
char s[]="123456";
int i=0;
while(s[i])
//while(s[i]!=0) можна так
//while(s[i)!='\0') або так писати цю умову
{
cout<<s[i];
}

Тобто ми обробляємо рядок до кінця, а кінцем буде поява символа з кодом 0.

Домашнє завдання

Неможна одразу написати програму, вона вірно запрацює і цим виконати завдання. Так не буває, більшу частину часу , якщо не половину, займає пошук помилок, як синтаксичних, так і логічних. Багато хто пише мало тексту, лиш наводить код та одне два речення які описують або цей код, або іншими словами описують умову задачі.
Слід обов'язково описати процес пошуку розв'язку, тобто спочатку виникла певна ідея, але коли ви її втілили в код - виявилося що вона зовсім не вірна(або частково невірна. Так як іноді дає хибний результат), Слід описати Свій невірний помилковий шлях. Описати через які помилки ви прийшли до вірного(на ваш погляд) розв'язку.
Бібліотечні функції для роботи з текстом не використовувати - працювати з рядком як з масивом.
Якщо не виходить розв'язати задачу одразу - рекомендую всі задачі розв'язувати так(три кроки):

  • спочатку зробити видимість розв'язку тобто масив не перетворювати, а лиш вивести на екран необхідний результат. (Імітувати розв'язок)
  • вирішити завдання з допомогою додаткового масиву. Тобто спочатку результат вписати в додатковий масив, а після його формування переписати у даний масив. Адже задача була змінити саме цей масив певним чином.
  • виконані два попередні пункти дадуть можливість збагнути як розв'язувати задачу без такого "послаблення

Якщо після виконання завдання довжина рядка повинна збільшитися - подбайте про це при оголошенні масиву, зарезервувавши додатковий простір.

1.(3 бали) Практично(тобто прикладами коду) описати теорію першої частини цього уроку, де розказано про два розміри в масива. Продемонструвати (показати) як можна зробити видимість збільшення/зменшення розміру масиву. Всі цикли мають працювати з розміром масиву що збережений в змінній size. Фізичний, справжній, фактичний розмір зберігати в константі N;
підготовленим учням...
2.(1 бал) Оголосити рядкову змінну(зберегти будь яке речення в масиві). Завдання - розвернути рядок, тобто записати задом наперед. Наприклад char s[]="ABCDEF";.....your code.....cout<<s; => FEDCBA
3.(1 бал) Переставити сусідні літери місцями char s[]="ABCDEF";.....your code.....cout<<s; => BADCFE

  1. (1.5 бали) Циклічно зсунути рядок вліво(так спочатку легше), Потім циклічно праворуч. char s[]="ABCDEF", x[]="abrakadabra";.....your code.....cout<<s<<"\n"<<x; => BCDEFA aabrakadabr
  2. (1.5 бали) Видалити всі голосні літери char s[]="this is some text";...your code...cout<<s; => ths s sm txt
  3. (2 бал) Подвоїти кожну голосну літеру char s[]="this is some text";...your code...cout<<s; => thiis iis soomee teext

Додаткове завдання (1-2 бали) - як заміна іншого з завдань 2-6,
Вибрати довільне число, бажано незначно більше за довжину тексту. Збільшити довжину тексту до вказаного числа додаванням пробілів між словами.
char s[]="this is some text";...your code...cout<<s; => (this is some text) len of s => 17 number =27

Правила проведення

Публікувати можна на будь-якій мові, в будь якій спільноті чи просто у власному блозі, посилання на вашу роботу додайте сюди коментарем

Щоб я швидко знайшов, перевірив та оцінив ваші роботи залиште посилання в коментарі під цим текстом а в роботі поставите тег #slc21w3sergeyk

До всіх завдань код наводити скріншотом, не текстом. Демонструвати теж скріншотом результат роботи програми.

Будьте обережні стосовно ідеальних та надефективних рішень, звичайній людині, початківцю їх не легко найти.

Не надавати рішення задач з допомогою матеріалів, які не вчили. Наприклад масивів, котрі ми ще не вчили. Це обмеження не стосується тих студентів які вже практично знайомі з програмуванням, та надають розширені відповіді на завдання, що більш схоже на лекцію.

Плагіат і використання ШІ заборонено.

Учасники мають бути перевіреними та активними користувачами платформи.

Використані зображення мають належати автору або бути вільними від авторських прав. (Не забудьте вказати джерело.)

Учасники не повинні використовувати будь-які сервіси ботів для голосування, не брати участь у купівлі голосів.

Порекомендуйте прийняти участь своїм друзям.

Роботи слід опублікувати з Monday 11 Nov 24 to Sunday 17 Nov 24

Ваші роботи будуть мною прокоментовані, оцінені та відібрані чотири кращі роботи.