Передача строки в функцию
Просматривая статьи о функциях на разных сайтах для новичков в С++, прихожу к выводу, что, зачастую, в примерах показано, как передавать функциям, в качестве параметров, числовые типы данных ( int , float , double и т.д.). Так появилась идея написать данную статью, где на практике хочу показать, как передать строку (она же — массив символов) в функцию в виде параметра. И просто замечательно, что в С++ таких способов несколько. Вы можете выбрать тот, который вам просто понравится или который больше подойдет для решения поставленной перед вами задачи.
Способы передачи строки в функцию хочу показать на одном примере. Чтобы лучше освоить материал и взять максимум пользы от прочитанного, открывайте свою среду разработки и переписывайте код по мере прочтения. Практика – лучший способ что-то понять и запомнить. Под примером будут расположены подробные комментарии и разъяснения по данному коду.
#include using namespace std; //определяем функции void showText1 (char str[])//функция принимает строку, как массив < cout void showText2 (char *str)//указатель *str будет указывать на адрес первого символа в строке < cout void showText3 (char (&str)[150])// адрес строки из 150-ти символов < cout int main()
Задача всех наших созданных функций – отобразить на экране строки, которые переданы им, как параметры. Пойдем по порядку:
1) первый способ — функция void showText1 (char str[]) принимает строку, как массив — строка 4 . П ри определении функции, в круглых скобках надо вписать тип массива (строки) char , имя строки и квадратные скобки [] . Обратите внимание, что нам не надо указывать размерность массива (количество символов в строке). С помощью символа ‘\0′, который автоматически добавляется в конец любой строки , функция сама найдет последний элемент массива и прекратит вывод элементов на экран.
2) второй способ, передача строки в функцию при помощи указателя, самый распространенный вариант — строка 9. При определении функции, перед именем строки ставим оператор * — void showText2 (char *str) .
3) ну и третий способ, передача в функцию строки по адресу — строка 14. В нашем примере это функция void showText3 (char (&str)[150]) . Тут следует быть внимательным, т.к. необходимо указывать точный размер строки. Без этого компилятор выдаст ошибку (Мы проверяли :)). А так же важно взять в круглые скобки оператор & с именем строки — (&str) . В строке 38 нашего кода, вы можете видеть, что строка char str3[150] содержит явно меньше символов. Остальным, не инициализированным, будет присвоено значение ‘\0’ .
Думаю, строки 23-25 говорят сами за себя и комментировать не стоит. Иногда, так удобно сделать, не объявляя и не определяя строку заранее.
При вызове функций все просто – надо лишь внести название необходимой строки в круглые скобки. Строки 29, 34, 39.
CppStudio.com
Можно ввести строку в круглых скобках при вызове функции:
~~~ ~~~ ~~~ cppstudio.com ~~~ ~~~ ~~~
str1 — передаем, как массив в функцию void showText1 (char str[]);
str2 — передаем, в функцию void showText2 (char *str); используя указатель.
str3 — передаем, в функцию void showText3 (char &str[]);
Тут используем адрес строки (адрес первого элемента символьного массива)
А вот еще один пример, который покажет насколько удобно работать с указателем в функциях. В нем функция примет, в качестве параметра, указатель на строку символов, с помощью указателя пройдет по каждой ячейке памяти строки и изменит числовые символы на знак ~ или на букву O . В результате строка, содержащая числовые символы 1234123412341234 и т.д., с легкостью превратится в “новогоднюю гирлянду”. В этом коде указатель покажет себя, как альтернатива управляющей переменной в цикле while :
#include using namespace std; void changeSymbol(char *str) < while(*str != '\0')//пока значение ячейки не равно символу конца строки < if (*str == '4') *str = 'O'; else *str = '~'; str++;//на каждой итерации цикла, указатель смещается на одну ячейку >> int main() < setlocale(LC_ALL, "rus"); char str[] = ; cout << "Исходная строка: " << str << endl << endl; changeSymbol(str);//передаем исходную строку в функцию cout
Смотрите, как интересно получается: как мы знаем, указатель указывает на адрес первой ячейки памяти строки, т.е. в нашем случае на str[0] . Значит, используя операцию разыменовывания указателя * , мы можем посмотреть, какой символ в этой ячейке хранится и, если надо, заменить его на другой — строки 6, 8, 9. Плюс, мы можем использовать указатель на строку, вместо управляющей переменной в нашем цикле while . Для этого, на каждой итерации цикла, инкрементируем указатель str++ — строка 11. Таким образом, указатель будет указывать уже на очередную ячейку памяти нашей строки. Так будет происходить до тех пор, пока указатель не перейдет на ячейку с символом ‘\0’ . Тут цикл while прекратит работу.
Результат — наша «новогодняя гирлянда» 🙂
CppStudio.com
Исходная строка: 12341234123412341234123
Каким способом передать строку в функцию — определяйтесь сами. Если у вас остались вопросы по теме, задавайте их нам в комментариях к этой статье. Мы вам обязательно ответим и поможем разобраться.
Как передать строку в функцию си



Скачай курс
в приложении
Перейти в приложение
Открыть мобильную версию сайта
© 2013 — 2023. Stepik
Наши условия использования и конфиденциальности

Public user contributions licensed under cc-wiki license with attribution required
передача строки в функцию
Все ниже написанное проверялось под средой Atollic True Studio на контроллере STM32F205.
Строка это указатель на последовательность байт завершающихся ‘\0’. Поэтому самый логичный способ передавать строку в функцию как указатель на байты.
Имея внутри вызываемой функции указатель на строку мы имеем адрес в памяти где лежат эти байты (строки) . И можем читать строку , можем менять содержание строки , но длина новой (измененной) строки не должна быть больше исходной, иначе можем (и скорее всего затрем) что-то другое в памяти.
Передаем строку в функцию как указатель
void myFunction(uint8_t *str) < printf(" myFunction str = %s \n" , str); >int main(void) < uint8_t *str1 = "abc"; printf(" str1 = '%s' \n" , str1); myFunction( str1 ); uint8_t str2[]="abcd"; printf(" str2 = '%s' \n" , str2); myFunction( str2 ); uint8_t str3[]=; printf(" str3 = '%s' \n" , str3); myFunction( str3 ); > вывод : str1 = 'abc' myFunction str = abc str2 = 'abcd' myFunction str = abcd str3 = 'abcde' myFunction str = abcde
Работаем с переданной строкой внутри функции
Работаем как с обычным указателем на массив байтов. Только заканчивается массив тогда — когда встречается символ ‘\0’.
void myFunction(uint8_t *str) < printf(" myFunction str : " ); for(uint8_t ii=0 ; ii < strlen(str); ii++) printf("%c" , str [ ii ] ); printf("\n"); // myFunction str : abc printf(" myFunction str : " ); for(uint8_t ii=0 ; ii < strlen( str ) ; ii++) printf("%c" , *( uint8_t* )( str + ii )); printf("\n"); // myFunction str : abc >
Изменяем строку внутри функции
Точнее меняем содержание строки.
И вот тут начинаются чудеса , сморите внимательно :
Отличие определения uint8_t *str1 = «abc»; от uint8_t str2[]=»abc»;
void myFunction(uint8_t *str) < printf(" myFunction str : '%s' " , str ); for(uint8_t ii = 0 ; ii < strlen( str ) ; ii++) *( uint8_t* )( str + ii ) = '1' ; printf("\n"); >int main(void) < uint8_t *str1 = "abc"; myFunction( str1 ); printf(" str1 (x%0.8X) '%s' \n" , str1 , str1); uint8_t str2[]="abcd"; myFunction( str2 ); printf(" str2 (x%0.8X) '%s' \n" , str2 , str2); >вывод : myFunction str : 'abc' str1 (x0800EE60) 'abc' // почему тут не поменялось содержимое строки myFunction str : 'abcd' str2 (x2001FFE0) '1111 // а тут поменялось нормально содержимое строки
Обратите внимание на адрес строки в скобочках : в первом случае это (x0800EE60), а во втором (x2001FFE0).
То есть компилятор/компоновщик :
uint8_t *str1 = «abc»; поместил в Flash память контроллера (а она неизменяемая), то есть пытаться записать можно и никто не ругнется, но эффекта не будет.
uint8_t str2[]=»abcd»; компилятор поместил в str2 оперативную память (она изменяемая).
Такие нюансы и портят всем жизнь.
В первом случае (чтобы я не делал) ничего не поможет сделать str1 изменяемой ( чтобы он поместил str1 в оперативную память (она же SRAM , или еще точнее в кучу SRAM).
Ни добавление префикса volatile, ни добавление static , ни вынесение всего определения за main, ни объявление static myFunction — ничего не убедит компилятор поместить str1 в оперативную память.
Кардинально вопрос можно решить с помощью функции malloc , но лично я не сторонник в контроллерах пользоваться динамическим выделением памяти от слова вообще.
Да и зачем пользуемся таким вариантом uint8_t str2[]=»abcd»; и проблем нет.
Си: передача предопределённых строк в функцию
Всегда думал, что при предопределении строковой переменной *s = «habr» и s[] = «habr» — практически полные синонимы.
Тогда почему в коде ниже (компилировал в GCC, различные версии, на Linux) работает лишь вариант I, а варианты II и III валятся с Segmentation Fault?
// Вариант I #include void test(char *s) < s[2] = 'X'; puts(s); >int main() < char x[] = "123456"; test(x); return 0; >// Вариант II #include void test(char *s) < s[2] = 'X'; puts(s); >int main() < char *x = "123456"; test(x); return 0; >// Вариант III #include void test(char *s) < s[2] = 'X'; puts(s); >int main()
Возможно, это что-то элементарное для языка, но сам я не разобрался.
- Вопрос задан более трёх лет назад
- 6486 просмотров
1 комментарий
Оценить 1 комментарий
Передача строки в функцию
Просматривая статьи о функциях на разных сайтах для новичков в С++, прихожу к выводу, что, зачастую, в примерах показано, как передавать функциям, в качестве параметров, числовые типы данных ( int , float , double и т.д.). Так появилась идея написать данную статью, где на практике хочу показать, как передать строку (она же — массив символов) в функцию в виде параметра. И просто замечательно, что в С++ таких способов несколько. Вы можете выбрать тот, который вам просто понравится или который больше подойдет для решения поставленной перед вами задачи.
Способы передачи строки в функцию хочу показать на одном примере. Чтобы лучше освоить материал и взять максимум пользы от прочитанного, открывайте свою среду разработки и переписывайте код по мере прочтения. Практика – лучший способ что-то понять и запомнить. Под примером будут расположены подробные комментарии и разъяснения по данному коду.
#include using namespace std; //определяем функции void showText1 (char str[])//функция принимает строку, как массив < cout void showText2 (char *str)//указатель *str будет указывать на адрес первого символа в строке < cout void showText3 (char (&str)[150])// адрес строки из 150-ти символов < cout int main()
Задача всех наших созданных функций – отобразить на экране строки, которые переданы им, как параметры. Пойдем по порядку:
1) первый способ — функция void showText1 (char str[]) принимает строку, как массив — строка 4 . П ри определении функции, в круглых скобках надо вписать тип массива (строки) char , имя строки и квадратные скобки [] . Обратите внимание, что нам не надо указывать размерность массива (количество символов в строке). С помощью символа ‘\0′, который автоматически добавляется в конец любой строки , функция сама найдет последний элемент массива и прекратит вывод элементов на экран.
2) второй способ, передача строки в функцию при помощи указателя, самый распространенный вариант — строка 9. При определении функции, перед именем строки ставим оператор * — void showText2 (char *str) .
3) ну и третий способ, передача в функцию строки по адресу — строка 14. В нашем примере это функция void showText3 (char (&str)[150]) . Тут следует быть внимательным, т.к. необходимо указывать точный размер строки. Без этого компилятор выдаст ошибку (Мы проверяли :)). А так же важно взять в круглые скобки оператор & с именем строки — (&str) . В строке 38 нашего кода, вы можете видеть, что строка char str3[150] содержит явно меньше символов. Остальным, не инициализированным, будет присвоено значение ‘\0’ .
Думаю, строки 23-25 говорят сами за себя и комментировать не стоит. Иногда, так удобно сделать, не объявляя и не определяя строку заранее.
При вызове функций все просто – надо лишь внести название необходимой строки в круглые скобки. Строки 29, 34, 39.
CppStudio.com
Можно ввести строку в круглых скобках при вызове функции:
~~~ ~~~ ~~~ cppstudio.com ~~~ ~~~ ~~~
str1 — передаем, как массив в функцию void showText1 (char str[]);
str2 — передаем, в функцию void showText2 (char *str); используя указатель.
str3 — передаем, в функцию void showText3 (char &str[]);
Тут используем адрес строки (адрес первого элемента символьного массива)
А вот еще один пример, который покажет насколько удобно работать с указателем в функциях. В нем функция примет, в качестве параметра, указатель на строку символов, с помощью указателя пройдет по каждой ячейке памяти строки и изменит числовые символы на знак ~ или на букву O . В результате строка, содержащая числовые символы 1234123412341234 и т.д., с легкостью превратится в “новогоднюю гирлянду”. В этом коде указатель покажет себя, как альтернатива управляющей переменной в цикле while :
#include using namespace std; void changeSymbol(char *str) < while(*str != '\0')//пока значение ячейки не равно символу конца строки < if (*str == '4') *str = 'O'; else *str = '~'; str++;//на каждой итерации цикла, указатель смещается на одну ячейку >> int main() < setlocale(LC_ALL, "rus"); char str[] = ; cout << "Исходная строка: " << str << endl << endl; changeSymbol(str);//передаем исходную строку в функцию cout
Смотрите, как интересно получается: как мы знаем, указатель указывает на адрес первой ячейки памяти строки, т.е. в нашем случае на str[0] . Значит, используя операцию разыменовывания указателя * , мы можем посмотреть, какой символ в этой ячейке хранится и, если надо, заменить его на другой — строки 6, 8, 9. Плюс, мы можем использовать указатель на строку, вместо управляющей переменной в нашем цикле while . Для этого, на каждой итерации цикла, инкрементируем указатель str++ — строка 11. Таким образом, указатель будет указывать уже на очередную ячейку памяти нашей строки. Так будет происходить до тех пор, пока указатель не перейдет на ячейку с символом ‘\0’ . Тут цикл while прекратит работу.
Результат — наша «новогодняя гирлянда» 🙂
CppStudio.com
Исходная строка: 12341234123412341234123
Каким способом передать строку в функцию — определяйтесь сами. Если у вас остались вопросы по теме, задавайте их нам в комментариях к этой статье. Мы вам обязательно ответим и поможем разобраться.
Как передать строку в функцию си



Скачай курс
в приложении
Перейти в приложение
Открыть мобильную версию сайта
© 2013 — 2023. Stepik
Наши условия использования и конфиденциальности

Public user contributions licensed under cc-wiki license with attribution required
Передача строки в функцию в Си
Это код для STM32F030, он компилируется без ошибок, но если залить этот код в микроконтроллер — он даже не запускается. Подскажите, сам код написан правильно?
void print(char * string) < while(*string) < while(!(USART_GetFlagStatus(USART1, USART_FLAG_TXE))); USART_SendData(USART1, *string++); >> int main (void)
Почему всё работает нормально, если вызвать функцию вот так:
char hello[] = ; print(hello);
Как правильно передать строку в функцию?
Отслеживать
задан 25 сен 2018 в 7:20
61 5 5 бронзовых знаков
С контроллерами дел не имел, но с точки зрения чистого языка — разница в том, что в первом случае вы передаете указатель на литерал, который не изменяем (представляет собой const char* ), а во втором — вполне изменяем. Попробуйте интереса ради объявить print(const char*) . Еще раз — в контроллерах я не смыслю; комментирую только с точки зрения чисто языка как такового.
25 сен 2018 в 7:31
Мне и нужно с точки зрения языка как такового) Хочу понять, это я коряво пишу или особенность микроконтроллера. const пробовал — код ведёт себя точно так же
25 сен 2018 в 7:37
Вы скомпилируйте оба варианта так, чтобы компилятор Вам вывел ассемблерный листинг. И сравните ассемблерные листинги для обоих вариантов. Сразу все станет понятно.
25 сен 2018 в 7:51
Скорее всего во втором варианте строка расположена на стеке и инициализируется константой из секции кода. А в первом варианте строка расположена в секции кода в константах. То есть имеется какая-то проблема с доступом к константам в коде. Транслятор-то какой? GCC или самодельный от фирмы-производителя контроллера? А, вижу. Контроллер ARM так что транслятор скорее всего GCC. Это странно. GCC для ARM хорошо отлажен, там не должно быть таких очевидных косяков.
25 сен 2018 в 7:57
Еще проверьте под тот ли тип ядра ARM у Вас компилируется код. Похоже, что указатель string как-то с ошибкой ползает по константным строкам, но без ошибок ползает по строкам, расположенным в памяти или на стеке.
Си: передача предопределённых строк в функцию
Всегда думал, что при предопределении строковой переменной *s = «habr» и s[] = «habr» — практически полные синонимы.
Тогда почему в коде ниже (компилировал в GCC, различные версии, на Linux) работает лишь вариант I, а варианты II и III валятся с Segmentation Fault?
// Вариант I #include void test(char *s) < s[2] = 'X'; puts(s); >int main() < char x[] = "123456"; test(x); return 0; >// Вариант II #include void test(char *s) < s[2] = 'X'; puts(s); >int main() < char *x = "123456"; test(x); return 0; >// Вариант III #include void test(char *s) < s[2] = 'X'; puts(s); >int main()
Возможно, это что-то элементарное для языка, но сам я не разобрался.
- Вопрос задан более трёх лет назад
- 6486 просмотров
1 комментарий
Оценить 1 комментарий