|
Си |
|
|
|
Jan 24 2013, 19:24
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
ЗАДАЧА АЕсть функция (K&R), которая обращает порядок символов в строке. Вот она: Код #include <string.h>
void reverse (char s[]) { int c, i, j; for (i = 0, j = strlen(s) - 1; i < j; i++, j--) { c = s[i]; s[i] = s[j]; s[j] = c; } } Вопросы: 1. Какие слабые места у данной реализации. 2. Как можно оптимизировать код. И что конкретно это даст. 3. Какие есть варианты реализации функции. Спасибо!
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
19 страниц
1 2 3 > »
|
 |
Ответов
(1 - 99)
|
Jan 24 2013, 20:15
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Цитата(telix @ Jan 24 2013, 22:37)  В коде ошибка поставьте j--  да ,сори. ЗАДАЧА БЕсть реализация функции перевода "типа" bool в строку. Код char *b2s (char b) { static char *name[] = {"Out Of Range", "False", "True"}; return (b < 0 || b > 1) ? name[0] : name [b+1]; } Вопросы: 1. Какие слабые/ошибочные места у данной реализации. 2. Как можно оптимизировать код. И что конкретно это даст. 3. Какие есть варианты реализации функции.
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 24 2013, 20:31
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
В смысле AVR, может выглядеть так CODE #include <avr/io.h> #include <string.h> void reverse(char s[]) { char *beg = (char *)&(s[0]); char *end = (char *)&(s[strlen(s)]); while(beg < end) { char c = *beg; *beg++ = *(--end); *end = c; } } CODE 82: fc 01 movw r30, r24 84: 01 90 ld r0, Z+ 86: 00 20 and r0, r0 88: e9 f7 brne .-6 ; 0x84 <reverse+0x2> 8a: 31 97 sbiw r30, 0x01 ; 1 8c: e8 1b sub r30, r24 8e: f9 0b sbc r31, r25 90: e8 0f add r30, r24 92: f9 1f adc r31, r25 94: dc 01 movw r26, r24 96: 04 c0 rjmp .+8 ; 0xa0 <reverse+0x1e> 98: 8c 91 ld r24, X 9a: 92 91 ld r25, -Z 9c: 9d 93 st X+, r25 9e: 80 83 st Z, r24 a0: ae 17 cp r26, r30 a2: bf 07 cpc r27, r31 a4: c8 f3 brcs .-14 ; 0x98 <reverse+0x16> a6: 08 95 ret
|
|
|
|
|
Jan 25 2013, 05:38
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 24-08-07
Из: Беларусь, Минск
Пользователь №: 30 045

|
Цитата(_Pasha @ Jan 25 2013, 08:23)  Неправда! Область значений, согласно stdbool.h -это true,false. Исходя из того, что в Си bool это всего лишь костыль для пришедших из других языков, и любое не 0-ое значение является истиной для условных операторов, то, ИМХО редактор прав. Задачу А можно оптимизировать сильнее всего исходя из контекста.
|
|
|
|
|
Jan 25 2013, 05:49
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(gotty @ Jan 25 2013, 08:38)  и любое не 0-ое значение является истиной для условных операторов, то, ИМХО редактор прав. if(0) - выражение в скобках сейчас == 0 if(!0) - выражение в скобках сейчас == 1 Ы? Цитата(gotty @ Jan 25 2013, 08:38)  Задачу А можно оптимизировать сильнее всего исходя из контекста. Работа непосредственно с двумя указателями выигрывает на любых pic/avr/arm/x51
|
|
|
|
|
Jan 25 2013, 06:08
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 24-08-07
Из: Беларусь, Минск
Пользователь №: 30 045

|
Цитата(_Pasha @ Jan 25 2013, 08:49)  if(0) - выражение в скобках сейчас == 0 if(!0) - выражение в скобках сейчас == 1 Ы? Я имел ввиду немного другое Код if (42) { printf("True\n"); } else { printf("False\n"); } Какая ветка условного оператора будет выполнена? Третьего "outofrange" не дано, увы.
|
|
|
|
|
Jan 25 2013, 06:16
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(gotty @ Jan 25 2013, 09:08)  Я имел ввиду немного другое Дык это несовместимость типов.  Почему к случаям явного приведения int к указателю всегда люди относятся настороженно, а к "притягиванию" bool к char - типа что это нормально? Примеров привести не могу, у меня не очень богатый сишный опыт. Только анекдот Цитата #define true false // собаки, ковыряйтесь теперь сами
|
|
|
|
|
Jan 25 2013, 06:33
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
В армоводческом смысле reverse() с указателями CODE 0x0800024A B570 PUSH {r4-r6,lr} 0x0800024C 4605 MOV r5,r0 0x0800024E 462C MOV r4,r5 0x08000250 4628 MOV r0,r5 0x08000252 F000F84F BL.W strlen (0x080002F4) 0x08000256 1946 ADDS r6,r0,r5 0x08000258 E007 B 0x0800026A 0x0800025A 7820 LDRB r0,[r4,#0x00] 0x0800025C 1E71 SUBS r1,r6,#1 0x0800025E 460E MOV r6,r1 0x08000260 7809 LDRB r1,[r1,#0x00] 0x08000262 F8041B01 STRB r1,[r4],#0x01 0x08000266 7030 STRB r0,[r6,#0x00] 0x08000268 BF00 NOP 0x0800026A 42B4 CMP r4,r6 0x0800026C D3F5 BCC 0x0800025A 0x0800026E BD70 POP {r4-r6,pc}
|
|
|
|
|
Jan 25 2013, 07:29
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(gotty @ Jan 25 2013, 08:33)  Моё ИМХО: в нынешней реализации Си тип bool не имеет смысла, т.к. операнды операций равно/неравно не приводятся к одному типу (bool), а используются как есть. Вот и получается что 42 != true, а (bool)42 == true в зависимости от компилятора (Не понял, почему операнды в выражении (0.5 != 'A') должны приводиться к bool) Это тоже не должно зависеть от компилятора. Т.е. если у конкретного компилятора «зависит», то он не соответствует стандарту. В выражении, ну пусть уж (42 != true), операнды используются не «как есть», а приводятся к int. Была бы она твёрдая — я бы её жевал Было бы 42UL, тогда бы true приводилось бы к unsigned long int :-) Но зато вот (42 != true) имеет значение true и в bool b = 5;в переменную будет занесено 1 даже в C99 c <stdbool.h>, не говоря уже о C++.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Jan 25 2013, 08:31
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Цитата(_Pasha @ Jan 24 2013, 23:31)  В смысле AVR, может выглядеть так CODE #include <avr/io.h> #include <string.h> void reverse(char s[]) { char *beg = (char *)&(s[0]); char *end = (char *)&(s[strlen(s)]); while(beg < end) { char c = *beg; *beg++ = *(--end); *end = c; } } А зачем тут скобки? Код char *beg = (char *)&(s[0]); char *end = (char *)&(s[strlen(s)]); *(--end); А почему вы пишите Код *(--end); Это с терминатором строки связано? А какой смысл объявлять и инициализировать "с" внутри операторного блока while? Как Вы думаете, что передается в функцию: массив или строка? Мне кажется что не очень корректно формальный аргумент сотрица, ну как будет тот-же strlen с массивом то? Может правильнее описать аргумент так: char * s ? demiurg_spb, мне не очень понравилось. читаемость отвалилась, а скилов не добавилось как по мне.
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 25 2013, 09:12
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Цитата(Буратино @ Jan 25 2013, 12:31)  А почему вы пишите Код *(--end); Во-первых, пиш ете (изъявительное наклонение вместо повелительного) А во-вторых, я однажды написал * temp ++ ; имея в виду инкремент значения по указателю, а потом долго не мог понять, почему это "бесполезная операция", с т.з. компилятора.
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Jan 25 2013, 09:28
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Цитата(MrYuran @ Jan 25 2013, 12:12)  Во-первых, пишете (изъявительное наклонение вместо повелительного) а как же жи ,ши через и?  Цитата(MrYuran @ Jan 25 2013, 12:12)  А во-вторых, я однажды написал * temp ++ ; имея в виду инкремент значения по указателю, а потом долго не мог понять, почему это "бесполезная операция", с т.з. компилятора. * temp++ сначала плюсанется указатель, а затем получим значение по плюсанутому указателю ,постави(е)те скобки и все будет так как запланированио, но в нашем то случае именно что и не нужны они! Я так думаю
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 25 2013, 09:29
|
Частый гость
 
Группа: Свой
Сообщений: 118
Регистрация: 9-12-12
Из: Курск
Пользователь №: 74 767

|
Я б пример А оптимизировал за счет второго буфера. То есть не делал тройную пересылку a=>c b=>a c=>b а однократную b[j--] => a[i++] но при этом a и b это разные массивы "b" это исходная строка, "a" это пустой массив для записи строки в обратном порядке. За счет использования второго буфера резко увеличивается скорость собственно операции.
Сообщение отредактировал telix - Jan 25 2013, 09:31
--------------------
улыбаемся ...
|
|
|
|
|
Jan 25 2013, 09:38
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(Буратино @ Jan 25 2013, 11:31)  А зачем тут скобки?
А почему вы пишите
А какой смысл объявлять и инициализировать "с" внутри операторного блока while?
Как Вы думаете, что передается в функцию: массив или строка? Мне кажется что не очень корректно формальный аргумент сотрица, ну как будет тот-же strlen с массивом то? Может правильнее описать аргумент так: char * s ? 1. Во-первых, без скобок оно не работало, во вторых, имею недостаток в виде "хронического плавания " в приоритетах, тк Си "не родной язык", по сему не брезгую лишними скобками, тем более, что в 80% случаев это правильный подход. 2. MrYuran ответил, именно после его сообщения, не помню когда и не скажу где  этот случай навечно впечатался в мозг. А делов-то всего пробел... 3. Как-раз смысл произошел из давних разговоров о свойствах GCC, что чем больше нагрузки на локальные переменные, тем будет правильнее. Листинг AVR это демострирует 4. Прототип функции кривоват, т.к. выпадает из общепринятой практики и заставляет задуматься о языке а не о том, что нужно.
|
|
|
|
|
Jan 25 2013, 09:45
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(Буратино @ Jan 25 2013, 12:28)  * temp++ сначала плюсанется указатель, а затем получим значение по плюсанутому указателю ,постави(е)те скобки и все будет так как запланированио, но в нашем то случае именно что и не нужны они! Я так думаю Я думаю не так. Цитата(_Pasha @ Jan 25 2013, 12:38)  ... имею недостаток в виде "хронического плавания " в приоритетах, тк Си "не родной язык", по сему не брезгую лишними скобками, тем более, что в 80% случаев это правильный подход. Я выписал на листок приоритеты операций, и держу его перед мор над монитором. А иногда еще в текстовые файлы - заметки по проекту - вставляю. И теперь безошибочно пишу что-то вроде a << 4 | b; А Буратино может набить себе тату - таблицу с приоритетами. Лучшего вряд ли можно посоветовать!
|
|
|
|
|
Jan 25 2013, 09:48
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Цитата(_Pasha @ Jan 25 2013, 12:38)  1. Во-первых, без скобок оно не работало, во вторых, имею недостаток в виде "хронического плавания " в приоритетах, тк Си "не родной язык", по сему не брезгую лишними скобками, тем более, что в 80% случаев это правильный подход. 2. MrYuran ответил, именно после его сообщения, не помню когда и не скажу где  этот случай навечно впечатался в мозг. А делов-то всего пробел... 3. Как-раз смысл произошел из давних разговоров о свойствах GCC, что чем больше нагрузки на локальные переменные, тем будет правильнее. Листинг AVR это демострирует 4. Прототип функции кривоват, т.к. выпадает из общепринятой практики и заставляет задуматься о языке а не о том, что нужно. Хорошо ,мне нравится ваш вариант с указателями давайте его допилим и перейдем ко второй задачке!? Итак: давайте формальный аргумент заменим на "char * s", это правильнее будет судя по всему. Раз так, то тогда зачем приводить &(s[0]); к (char *) да еще и скобки использовать!? Сначала индекс ,а только потом одноместные унарные! Код char *beg = (char *)&(s[0]);
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 25 2013, 09:52
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(ViKo @ Jan 25 2013, 12:45)  Я выписал на листок приоритеты операций, и держу его перед мор над монитором. А иногда еще в текстовые файлы - заметки по проекту - вставляю. И теперь безошибочно пишу что-то вроде a << 4 | b; Дык я это расцениваю(для себя) как write only style  Тем более, не встречал случаев, когда лишние скобки влияют не результат. -- Закномерность: чем более пустяковый вопрос, тем оживленнее тема. Насчет замечания Herz. Только штука в том, что здесь из простейшего произрастают интересные вещи. Довольно часто.
|
|
|
|
|
Jan 25 2013, 09:53
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Цитата(MrYuran @ Jan 25 2013, 12:48)  жи это жи, а ше это ше ) Насчет скобок - лучше явно указать. И самому спокойнее, и читателю понятнее будет. Нет, давайте так: в этих "задачках" сделаем все правильно ,а в жизни будем беспокоится о читателях. Ищу самую правильную и изящную реализацию, хочу вот именно так и не иначе.
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 25 2013, 09:59
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(Буратино @ Jan 25 2013, 12:31)  demiurg_spb, мне не очень понравилось. читаемость отвалилась, а скилов не добавилось как по мне. Профит есть 2 строчки кода с хорошей читаемостью вместо 6..9 строк кода в другой реализации. Вы это поймёте, когда будете писать большие программы, в которых становится важна не только выразительность отдельно взятой процедуры, а модуля целиком... Всему своё время. Цитата(_Pasha @ Jan 25 2013, 09:23)  Код static const char strue[]="TRUE"; static const char sfalse[]="FALSE"; static const char sndef[]="OUT OF RANGE"; switch(boolean) { case true: return &strue; case false: return &sfalse; default: return &sndef; } Кстати  В этом кусочке кода три ошибки (три лишних символа &). А вы говорите хорошая читаемость... Чем лаконичнее, тем ИМХО профессиональнее.
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Jan 25 2013, 10:04
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(Буратино @ Jan 25 2013, 12:48)  давайте его допилим и Чо там пилить? CODE void reverse(char *s) { if(s == NULL) return; char *end = s; while(*end) end++; while(s < end) { char c = *s; *s++ = *(--end); *end = c; } }
Упс... может, они это имели в виду, что при передаче не-указателя не нужно его проверять?
Сообщение отредактировал _Pasha - Jan 25 2013, 10:06
|
|
|
|
|
Jan 25 2013, 10:06
|
Частый гость
 
Группа: Свой
Сообщений: 118
Регистрация: 9-12-12
Из: Курск
Пользователь №: 74 767

|
Цитата(Буратино @ Jan 25 2013, 13:35)  по поводу тройной пересылки мы еще поговорим, а вот второй буфер не айс однозначно. во первых b[j--] => a[i++] в 2 раза больше нужно а во вторых место под буфер. Я бы поспорил насчет в два раза, и про место под буфер Так что удачи...
Сообщение отредактировал telix - Jan 25 2013, 10:09
--------------------
улыбаемся ...
|
|
|
|
|
Jan 25 2013, 10:06
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Давайте в первом чтении вот такую версию ЗАДАЧИ А от _Pasha обсудим? Код #include <string.h> void reverse ( char *s ) {
char *beg = s; char *end = &s[strlen(s)]; char c;
while(beg < end) { c = *beg; *beg++ = *--end; // благодаря префиксному декременту end терминатор строки '\0' останется на своем месте *end = c; } }
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 25 2013, 10:17
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Для нелюбителей тройных пересылок есть одна бяка Код while(beg < end) { *beg++ = *(--end); *end = *beg; } Строка "сползает", но ее можно искусственно расширить и передать указ. не на начало, а на s[1] Бред, в общем случае, но.. если у нас не char а нечто более существенное, пересылки могут доставить ЗЫ вот только для чего эти сферокони, не пойму
Сообщение отредактировал _Pasha - Jan 25 2013, 10:21
|
|
|
|
|
Jan 25 2013, 11:00
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(ViKo @ Jan 25 2013, 13:29)  Т.е. начать переносить на 1 символ левее строки? Тогда в середине появится разрыв.  По-моему, без промежуточной переменной - никак! Ага, ошибочка вышла. Да простит меня высокое собрание. Цитата(Буратино @ Jan 25 2013, 13:53)  Какие есть мысли, соображения? Спасбио! На общеприменяемых архитектурах будет хуже. Потому что "лишний" char c есть регистровая переменная. А так - добавляются операции, такие же переменные неявно выделяются компилятором, но самое обидное будет если у нас не char, а long например. В принципе, вместо + - как у Вас надо бы Код поменять местами A,B A ^= B; B ^= A; A ^= B;
Сообщение отредактировал _Pasha - Jan 25 2013, 11:00
|
|
|
|
|
Jan 25 2013, 11:11
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Цитата(_Pasha @ Jan 25 2013, 14:00)  Ага, ошибочка вышла. Да простит меня высокое собрание. На общеприменяемых архитектурах будет хуже. Потому что "лишний" char c есть регистровая переменная. А так - добавляются операции, такие же переменные неявно выделяются компилятором, но самое обидное будет если у нас не char, а long например. В принципе, вместо + - как у Вас надо бы Код поменять местами A,B A ^= B; B ^= A; A ^= B; с исключающим или мне не очень, хотя красиво конечно a ^= b ^= a ^= b; Вот еще вариантец, только проверить нужно. Кстати как можно простенькие такие программки потестить? Например в самом ИАРе? Как в отладочное окно выводить результаты всяких printf? Спрашивал однажды и запамятовал к сож( Код #include <string.h> void reverse ( char *s ) {
char *beg = s; char *end = &s[strlen(s)-1];
do *beg += *end - (*end = *beg); while (++beg < --end); }
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 25 2013, 11:35
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(Буратино @ Jan 25 2013, 15:11)  Вот еще вариантец, только проверить нужно. Кстати как можно простенькие такие программки потестить? У меня три вивария, с чем работаю, симуляторы 1. C::B + Proteus для аврок - всё в два клика, но создать проект под АРМ - муки творчества, ибо визардов нормальных нету, поэтому 2. Кейл, там тоже элементарно, исключительно из-за stm32 и at91sam7s 3. Mplab Сыр-бор весь только чтобы глянуть листинг.
Сообщение отредактировал _Pasha - Jan 25 2013, 11:36
|
|
|
|
|
Jan 25 2013, 11:55
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (Буратино @ Jan 25 2013, 13:11)  Кстати как можно простенькие такие программки потестить? Например в самом ИАРе? Как в отладочное окно выводить результаты всяких printf? CODE C:\user\My documents>echo "#include <stdio.h>\ int main()\ {\ printf (\"Hello, world\");\ return 0;\ }\ " > test.c
C:\user\My documents>gcc test.c -o test.exe C:\user\My documents>test.exe Hello, world C:\user\My documents>
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 25 2013, 12:48
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(Буратино @ Jan 25 2013, 13:11)  с исключающим или мне не очень, хотя красиво конечно a ^= b ^= a ^= b; UB на двух записях в a в переделах sequence point. Правильно так, как было написано сначала, в три строки. Можно записать в две, но это уже неинтересно. Цитата(Буратино @ Jan 25 2013, 13:11)  Код *beg += *end - (*end = *beg); Вам так хочется UB-иться? UB на произвольном порядке вычисления подвыражений (тут возможность записи в *end в скобках в правом аргументе операции вычитания до того, как будет прочитано *end в левом операнде).
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Jan 25 2013, 12:58
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
что такое UB? что такое в пределах sequence point? a ^= b ^= a ^= b; сначала выполнится a ^= b ,затем b ^= a и в конце a ^= b по идее все верно. Нужно проверить. Код *beg += *end - (*end = *beg); тут мне кажется что однозначно от старого *end новое отнимется ,но снова вынужден признать- необходимо проветить
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 25 2013, 13:20
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Да вроде всегда была определена последовательность вычислений, но не последовательность записи результатов. Так как значением выражения (a = B) является не «содержимое а после записи», а «значение b приведенное к неквалифицированному типу а». А (a ^= B) это «то же, что и (a = a ^ B), за исключением того, что a оценивается один раз» А порядок записи в пределах между точками следования неопределён. Но что-то меня LWS поколебал... http://liveworkspace.org/code/Ll7BX$13gcc по 4.6.3 выдаёт Цитата source.cpp:7:20: warning: operation on 'a' may be undefined [-Wsequence-point] в режимах и C, и C++, gcc 4.7.2 только в режиме C, но не в C++ clang 3.2 доволен всем. Для меня это новость. Первое (и последнее -- больше так не баловался) предупреждение на a ^= b ^= a ^= b; я получил как бы не от BCC5 уже не помню как давно. Почитал стандарт. Вроде понял :-) Что касается *beg += *end - (*end = *beg);, то все компиляторы единодушны -- UB (undefined behavior). Цитата(Буратино @ Jan 25 2013, 14:58)  что такое UB? что такое в пределах sequence point? «тут мне кажется», что Вы рано за такие оптимизации взялись :-) Sequence_point популярно для начала.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Jan 25 2013, 13:43
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Хех. Цитата(С99) 6.5.16 Assignment operators Syntax assignment-expression: conditional-expression unary-expression assignment-operator assignment-expression assignment-operator: one of = *= /= %= += -= <<= >>= &= ^= |= ...
Semantics 3 An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment, but is not an lvalue. The type of an assignment expression is the type of the left operand unless the left operand has qualified type, in which case it is the unqualified version of the type of the left operand. The side effect of updating the stored value of the left operand shall occur between the previous and the next sequence point. Цитата(C++11) 5.17 Assignment and compound assignment operators ... In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. Это они сводят поведение assignment operators для POD к поведению перегруженных операторов для классов (которые есть функции и уж точно завершат запись до возврата значения)? Свежий стандарт C (не С++) у кого-то есть? Недавно и C обновили, не только С++
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Jan 25 2013, 16:10
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Не говорит. В "например" определяющей является точка с запятой, заканчивающая утверждение, а не присваивающий оператор, являющийся вместе со своими операндами выражением. Цитата 6.8 Statements and blocks Syntax 1 statement: labeled-statement compound-statement expression-statement selection-statement iteration-statement jump-statement … 4 A full expression is an expression that is not part of another expression or of a declarator. Each of the following is a full expression: an initializer; the expression in an expression statement; the controlling expression of a selection statement (if or switch); the controlling expression of a while or do statement; each of the (optional) expressions of a for statement; the (optional) expression in a return statement. The end of a full expression is a sequence point. … 6.8.3 Expression and null statements Syntax expression-statement: expressionopt ; Т.е. expression в данному случае превращается в full expression путём участия в expression statement при помощи завершения символом ;т.е.: a = b = c = d; Это один expression-statement, превращает в full-expression всё a = b = c = dCостоящее из нескольких subexpression. Может, в С++, особенно в C++11, что-то и не так, я туда ещё так не вчитывался. Какие-то моменты, undefined в C, стали unspecified в C++, с этими их тонкостями ещё пойди разберись. Да, кстати, я не подчеркнул отдельно Цитата( @ Jan 25 2013, 13:11)  как можно простенькие такие программки потестить? Вот ответ, разными компиляторами можно: http://liveworkspace.orgПричём не только C/C++. В ответ на кусок кода вся диагностическая выдача компилятора (в зависимости от ключей) и, при создании исполняемого файла, выдача на stdout
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Jan 28 2013, 14:11
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Вот еще вариант. В основе смелая идея (уже ранее высказанная) о том чтоб испоьзовать буфер, но я немного переосмыслил это дело  Код #include <stdio.h> #define MAX_LEN 100
char * reverse ( char *s ) { static char buf[MAX_LEN]; char *buf_pointer = &buf[MAX_LEN-1];
while ( (*buf_pointer-- = *s++) ) // компилятор попросил о 2х скобках, ну раз ему так легче, то пусть будут:) ; return buf_pointer+2; }
int main() { char s[]= "Hello World!"; printf("%s\n", reverse (s) );
} Код stdout: !dlroW olleH Итак, я считаю что с задачей А мы справились. Плавно переходим к задаче Б.. Задача Б В этой реализации мне не нравится, что в случае дефолта сначала проверится один кейс, затем другой и только потом собственно выполнится подходящее действие! Что если нужно например месяца из чисел в названия переводить или там справочные данные какие? Потом мне непонятно почему нужно static const char strue[]="бла-бла-бла"; делать!? Это в смысле что строки в памяти программ расположатся? Как с доступом к таким значениям? не будет страдать производительность? Код static const char strue[]="TRUE"; static const char sfalse[]="FALSE"; static const char sndef[]="OUT OF RANGE"; switch(boolean) { case true: return &strue; case false: return &sfalse; default: return &sndef; }
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 28 2013, 14:46
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(Буратино @ Jan 28 2013, 18:11)  Итак, я считаю что с задачей А мы справились. Для таких программ потом и пишут заплатки на тему переполнения какого-нить буфера. Цитата Потом мне непонятна почему нужно static const char strue[]="бла-бла-бла"; делать!? static - чтобы можно было вернуть из функции указатель на что-то вменяемое, const - считайте просто правилом хорошего тона.
|
|
|
|
|
Jan 28 2013, 14:52
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Цитата(xemul @ Jan 28 2013, 18:46)  Для таких программ потом и пишут заплатки на тему переполнения какого-нить буфера.
static - чтобы можно было вернуть из функции указатель на что-то вменяемое, const - считайте просто правилом хорошего тона. Для этой задачи оптимальным наверное будет сочетание двух подходов: в случае если длина строки не более чем MAX_LEN использовать один подход, если больше - второй, и менять данные прямо по месту. Я пока еще не придумал как красиво пофиксить переполнение буфера ибо нужно на каждой итерации проверять условие достижения конца буфера, а мне не оч. такой вариант. Вот у меня тоже static, но при этом данные в массиве указателей описаны внутри функции, вроде так логичнее получается ,зачем выносить их наружу меняя область действия? Код char *b2s (char b) { static char *name[] = {"Out Of Range", "False", "True"}; return (b < 0 || b > 1) ? name[0] : name [b+1]; }
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 28 2013, 15:04
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(Буратино @ Jan 28 2013, 16:11)  Итак, я считаю что с задачей А мы справились. Поосторожнее… А то сейчас набегут спецы, которые сначала скажут, что возвращать указатель на внутренний статический буфер нельзя, потом скажут, что они имели ввиду, что оно-то можно, но глюкодром. А на вопрос, чем именно Код const char *foo() { static char buf[10]; ... return buf; } глюкодромее Код const char *moo() { return "bla-bla-bla"; } просто не будут отвечать по существу. Недостаток у способа один: Код char str1[] = "String 1"; char str2[] = "String 2"; printf("%s %s\n", reverse(str1), reverse(str2)); Впрочем, для таких случаев мои функции подобного типа принимают второй аргумент (со значением по умолчанию в C++), которым программа, имеющая желание в одни заход получить несколько строк, обязан апередать буфер достаточного размера: Код const char *reverse(const char *src, char *tmpbuf = 0) { static char inernal_buf[сикоку_не_жаль]; if (!tmpbuf) tmpbuf = internal_buf; … return tmpbuf; // или что там надо по алгоритму } В итоге в большинстве случаев вызывается reverse(some_string); (второй аргумент по умолчанию подставляется 0), и только в отдельных случаях подставляется доп. буфер. Однако задача в некотором смысле не решена и не может быть решена без уточнения условий и целей. Например, если цель — скорость, а строки преимущественно «достаточно длинные», а процессор Cortex-M3 или другой, имеющий команду реверся байтов в 32-битном слове (i486+), то стоит подумать о зачитывании по 32 бита, реверсе порядка байтов соответствующей командой и т.п. Соответственно, с выделением места под буфер кратно 4 байтам + под концевой 0, спецобработкой хвоста из 1..3 символов и возвратом указателя на tmp_buf с нужным смещением. «Если Вы не указали цель оптимизации, то Вам всё равно, как писать код» © Чеширсикй Кот – программист. Цитата(Буратино @ Jan 28 2013, 16:11)  Это в смысле что строки в памяти программ расположатся? Как с доступом к таким значениям? не будет страдать производительность? По простому const объекты расположатся в памяти программ только у тех процессоров, у которых память программ и память данных находятся в одном и том же адресном пространстве. ( чтобы не повторяться) А в таком случае производительность, в первом приближении, не пострадает. Цитата(xemul @ Jan 28 2013, 16:46)  Для таких программ потом и пишут заплатки на тему переполнения какого-нить буфера. Я надеюсь, высказывание касается только того, что перед реверсом либо в процессе оного не проверияется длина строки. Само по себе наличие промежуточного буфера фиксированного размера к ошибкам переполнения буфера не приводит. Цитата(Буратино @ Jan 28 2013, 16:52)  Вот у меня тоже static, но при этом данные в массиве указателей описаны внутри функции, вроде так логичнее получается ,зачем выносить их наружу меняя область действия? Код char *b2s (char b) { static char *name[] = {"Out Of Range", "False", "True"}; return (b < 0 || b > 1) ? name[0] : name [b+1]; } Да, лучше внутри функции.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Jan 28 2013, 16:10
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(ReAl @ Jan 28 2013, 19:04)  Я надеюсь, высказывание касается только того, что перед реверсом либо в процессе оного не проверияется длина строки. Безусловно. Т.к. в условии задачи не сказано, что исходная строка должна сохраниться, я бы делал реверс на месте. Цена вопроса - один strlen(). (оно, конечно, не спасёт, если строка вдруг окажется без '\0') Цитата Само по себе наличие промежуточного буфера фиксированного размера к ошибкам переполнения буфера не приводит. К переполнению, естественно, не приведёт. Но если память (опять же вдруг) закончится... За жисть. У меня K&R был первого советского издания (в мягком переплёте, голубая обложка), не помню в нём таких упражнений. Хотя, может опять склероз буйствует.
|
|
|
|
|
Jan 28 2013, 18:45
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(xemul @ Jan 28 2013, 18:10)  Т.к. в условии задачи не сказано, что исходная строка должна сохраниться, я бы делал реверс на месте. Цена вопроса - один strlen(). (оно, конечно, не спасёт, если строка вдруг окажется без '\0') Так в том-то и дело, что в условии задачи не сказано ничего (см. выше про Чеширского Кота – программиста). Как я говорил дочери, когда в школе на информатике пошёл паскаль, — программу можно оптимизировать на скорость написания, на простоту отладки, на скорость выполнения, на минимальный размер, на скорость понимания другим человеком, … «Множество чего? Просто множество!» (что-то меня на Алису потянуло) «просто оптимизировать» невозможно. Цитата(xemul @ Jan 28 2013, 18:10)  За жисть. У меня K&R был первого советского издания (в мягком переплёте, голубая обложка), не помню в нём таких упражнений. Хотя, может опять склероз буйствует. У меня был K&R, любовно кем-то подготовленный (фигурные скобки сделаны из круглых с надпечаткой поверх минусом, ключевые слова двойной печатью, …) и напечатанный на барабанном АЦПУ, переплетённый в переплётной мастерской (кажись, в один заказ с дипломной работй жены, тогда это 1987 год). Тоже таких упражнений не помню :-)
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Jan 29 2013, 14:52
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Задача А. Работа над ошибками так сказать.. В этой версии маленькие строки (меньше LEN_BUF байт) копируются в обратном порядке в буфер и возвращается указатель на голову, а если строки большие, то перестановками решаем. Во втором варианте проверям чтоб строка не слишком большой была (меньше MAX_LEN_STR) Код #include <stdio.h>
#define LEN_BUF 100 #define MAX_LEN_STR 1000
#define _LenStr buf[LEN_BUF-1] // для хранения длины строки #define _TmpChar buf[LEN_BUF-2] // для хранения временных значений
char * reverse ( char *s ) {
static char buf[LEN_BUF]; // буфер данных char *buf_pointer = &buf[LEN_BUF-1]; // определяем указатель, инициализируем значением адреса
char *beg = s; // определяем указатель, инициализируем адресом начала строки char *end;
while ( (*buf_pointer-- = *s++) ) { // копипастим if (buf_pointer <= &buf[0]) { // если строка больше, чем буфер, то: while (*++s) ; if (s - beg > MAX_LEN_STR) return "Error, overflow!";
_LenStr = s - beg; // Вычисляем длинну строки end = s-1; // Указатель на конец строки do { // Меняем местами данные: _TmpChar = *beg; *beg = *end; *end = _TmpChar; } while (++beg < --end); return s - _LenStr; } }
return buf_pointer+2; }
int main() { char s[]= "http://www.youtube.com/watch?v=cZjx6ItATW4"; printf("%s\n", reverse (s) ); } Не ясно еще как все же быть с одновременным вызовом функций Код char str1[] = "String 1"; char str2[] = "String 2"; printf("%s %s\n", reverse(str1), reverse(str2)); Вариант который предлагает ReAl требует введения второго аргумента, что не очень айс
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 29 2013, 15:43
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Цитата(_Pasha @ Jan 29 2013, 18:16)  О майн Готт! Вы на листинг этого чуда хоть смотрели? неа, не смотрел. а что там такого страшного? последний вариант лучше предыдущих тем. что небольшие строки перевернутся без вызова strlen ,ведь даже в вашем варианте нужно вызвать strlen, а потом до половины размера строки ворочать значения. При варианте с буфером я НЕ вызываю strlen и получаю строку в доп. буфере, однако при большой строке переворачиваю строку без буфера, но при этом strlen также не пользую.
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Jan 29 2013, 15:55
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(Буратино @ Jan 29 2013, 19:43)  неа, не смотрел. а что там такого страшного? последний вариант лучше предыдущих тем. что небольшие строки перевернутся без вызова strlen ,ведь даже в вашем варианте нужно вызвать strlen, а потом до половины размера строки ворочать значения. При варианте с буфером я НЕ вызываю strlen и получаю строку в доп. буфере, однако при большой строке переворачиваю строку без буфера, но при этом strlen также не пользую. Ниасилил. Strlen() хоть вызывай, хоть делай сам - оно выглядит одинаково. Чем, извините, это всё лучше, если оно жрет памяти вдвое больше, не заточено под ASCIIZ, в >3раза больше число итераций. О майн Готт!
|
|
|
|
|
Jan 29 2013, 16:01
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
K&R всплакнули бы от умиления... Есть такая прилада профайлер, которая позволяет оценить время выполнения кода. Сравните результаты Вашей программы и поминавшихся выше вариантов со strlen(). Про наглядность кода и разбазаривание памяти ... пусть будет Задачей №3. И попробуйте Код int main() { char s[]= "http://www.youtube.com/watch?v=cZjx6ItATW4"; char s1[]= "Пачиму-то не работает"; printf("%s\n", reverse (s) ); printf("%s\n", reverse (s1) ); } UPD: сделайте s1 > LEN_BUF
|
|
|
|
|
Jan 29 2013, 16:25
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
_Pasha , функция работает в двух так сказать режимах: а) когда входная строка меньше по длине чем LEN_BUF б) когда входная строк больше, чем LEN_BUF в варианте "а" strlen не вызывается, но при этом крутится вся строка в варианте "б" strlen не вызывается, но выполняются действия аналогичные по смыслу. после этого строка перебирается и меняются местами данные, кол-во итераций длина строки/2. Вот как и у вас по сути. последняя реализация лучше тем ,что если на входе функции будут разные данные ,то в общем итоге получается оптимальнее! И обратите внимание - алгоритм "б" дополняет собой алгоритм "а" ! Цитата(xemul @ Jan 29 2013, 19:01)  K&R всплакнули бы от умиления... Есть такая прилада профайлер, которая позволяет оценить время выполнения кода. Сравните результаты Вашей программы и поминавшихся выше вариантов со strlen(). Про наглядность кода и разбазаривание памяти ... пусть будет Задачей №3. Я согласен. Предлагаю обсудить условия проведения соревнования на скорость и денежные призы. Итак!? Цитата(xemul @ Jan 29 2013, 19:01)  И попробуйте Код int main() { char s[]= "http://www.youtube.com/watch?v=cZjx6ItATW4"; char s1[]= "Пачиму-то не работает"; printf("%s\n", reverse (s) ); printf("%s\n", reverse (s1) ); } ну не знаю ,кажись работает: Код char s[]= "http://www.youtube.com/watch?v=cZjx6ItATW4"; char s1[]= "http://www.ebay.com/sch/i.html?_from=R40&_sacat=0&_nkw=pl-660&_sop=15"; printf("%s\n", reverse (s) ); printf("%s\n", reverse (s1) ); Код stdout: 4WTAtI6xjZc=v?hctaw/moc.ebutuoy.www//:ptth 51=pos_&066-lp=wkn_&0=tacas_&04R=morf_?lmth.i/hcs/moc.yabe.www//:ptth
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Feb 27 2013, 20:10
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Есть указатель на указатель на char: Код char **argv; Как и чем его можно инициализировать и как (для чего) можно потом использовать? Что будет если я буду инкрементировать такой указатель на указатель? К сож. с языком знакомлюсь рывками и че та совершенно запутался
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Feb 27 2013, 21:03
|
Местный
  
Группа: Свой
Сообщений: 338
Регистрация: 14-07-12
Пользователь №: 72 753

|
обычно это имя переменной, которую получает main. Это массив указателей на строки - аргументы командной строки. В паре к ней идет argc - количество элементов в массиве. Код void foo1(int argc, char **argv) { while (argc--) printf("\"%s\"\n", argv++); }
void foo(void) { char *my_argv[] = {"Hello", "World", "one", "two", "four"};
foo1(5, my_argv);
|
|
|
|
|
Feb 28 2013, 04:17
|
Местный
  
Группа: Свой
Сообщений: 476
Регистрация: 3-07-07
Из: Санкт-Петербург
Пользователь №: 28 866

|
Цитата(toweroff @ Feb 28 2013, 01:08)  Да поправят меня, если не прав  Указатель передается обычно тогда, когда функция может менять какие-то данные за пределами себя Указатель за пределами какой-то функции тоже можно менять, не так ли? вот и передается указатель на этот указатель Вы правы - при реализации связных списков, например, именно для этих целей передают указатель на указатель.
--------------------
Ковырял чукча отверткой в ухе, звук в телевизоре и пропал.
|
|
|
|
|
Feb 28 2013, 06:45
|
Местный
  
Группа: Свой
Сообщений: 338
Регистрация: 14-07-12
Пользователь №: 72 753

|
Цитата(toweroff @ Feb 28 2013, 01:08)  Да поправят меня, если не прав  Формально - прав. Фактически - нет. Поправляю. Вопрос был о char **argv. Есть такая штука, как code convention. Именем argv принято обозначать статический массив указателей на строки. Поэтому упаси бог использовать его по тому же принципу, как используют при работе со списками. Например, здесь переменную answer назвать argv нельзя из соображений личной безопасности: Код void true_of_false(char **answer, int is_true) { *answer = is_true ? "True" : "False"; }
void foo(void) { char *b_char;
true_of_false(&b_char, 2013); printf("He said %s %s %s!\n", b_char, b_char, b_char); }
Сообщение отредактировал igorle - Feb 28 2013, 06:46
|
|
|
|
|
Feb 28 2013, 07:06
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(igorle @ Feb 28 2013, 10:45)  То что вы написали тут формально и гарантированно работать не будет ИМХО. Т.к. вы инициализируете указатель адресом локальной не статической строки и используете этот указатель вне области жизни этих строк (в другом стековом фрейме). Вот так все формальности будут соблюдены: Код void true_of_false(char **answer, int is_true) { static char True[] = "True"; static char False[] = "False";
*answer = is_true ? True : False; } Хотя набрёл тут на такое: Цитата 4. Строковой литерал во всех прочих случаях Во всех прочих случаях строковой литерал трактуется как НЕявно заведённый статический константный объект типа массив char'ов, инициализированный символами данного строкового литерала с включением неявного завершающего нуля, и далее взятие адреса на нулевой элемент данного объекта. и немного из другой плюсатой оперы Цитата "Память под строковые литералы выделяется статически, поэтому их возврат из функций безопасен" Б. Страуструп Захотелось найти это в первоисточнике....
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Feb 28 2013, 07:37
|
Местный
  
Группа: Свой
Сообщений: 338
Регистрация: 14-07-12
Пользователь №: 72 753

|
Цитата(demiurg_spb @ Feb 28 2013, 10:06)  То что вы написали тут формально и гарантированно работать не будет ИМХО.... Это таки будет работать гарантировано. Потому что моя функция возвращает указатели на read only строки из секции dataВаша функция тоже будет работать, но в ней есть бесполезное копирование строк из read only секции data в read write секцию. Недостатки - в два раза увеличился объем памяти - нет защиты от случайного изменения текста (оно в данном случае не желательно) Посмотрите здесьНу и посыпаю голову пеплом по поводу опечатки в названии функции. Должно было быть true_ or_false. Хотя так даже прикольно получилось, многозначительно.
Сообщение отредактировал igorle - Feb 28 2013, 07:34
|
|
|
|
|
Feb 28 2013, 07:45
|
Местный
  
Группа: Свой
Сообщений: 338
Регистрация: 14-07-12
Пользователь №: 72 753

|
Цитата(demiurg_spb @ Feb 28 2013, 10:39)  На платформе АVR и так и сяк будет двойной расход памяти flash + копия в озу пока явно не зададим __flash. Это так к слову... На MSP430 - все литеральные строки хранятся во флэше. А для инициализированных массивов, если я не хочу переносить их п память, достаточно добавить constПопутно. Оффтопик. Как вам удается редактировать свои сообщения, без того чтобы было написано "изменен тогда-то и тем-то"?
|
|
|
|
|
Feb 28 2013, 07:58
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(igorle @ Feb 28 2013, 11:45)  На MSP430 - все литеральные строки хранятся во флэше. А для инициализированных массивов, если я не хочу переносить их п память, достаточно добавить const Это всё понятно. У MSP430 единое адресное пространство - просто и удобно... И про повсеместный static const я как бы тоже в теме. Меня интересует конкретный отсыл в стандарт и только... Цитата Попутно. Оффтопик. Как вам удается редактировать свои сообщения, без того чтобы было написано "изменен тогда-то и тем-то"? Также как и вам. Нажимаю редактировать -> быстрое... В avr-gcc вообще все очень непросто со строковыми литералами. Можете почитать нашу дискуссию на сей счёт: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56263И был бы очень рад если ещё кто-то меня поддержит в ней!
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Feb 28 2013, 08:27
|
Местный
  
Группа: Свой
Сообщений: 338
Регистрация: 14-07-12
Пользователь №: 72 753

|
Цитата(demiurg_spb @ Feb 28 2013, 10:58)  Также как и вам. Нажимаю редактировать -> быстрое... Я так делаю - обязательно пишет что меняли текст. Цитата Можете почитать нашу дискуссию на сей счёт Почитал. Познавательно. Я сам с AVR не работаю.
|
|
|
|
|
Feb 28 2013, 08:29
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(demiurg_spb @ Feb 28 2013, 11:39)  Это объяснение не на том уровне. Хотелось бы в контексте Си стандарта... Запросто - С99 стандарт, глава 6.4.5 (String Literals), параграф 5 Цитата In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals. The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence. Так что возвращать из функции можно
|
|
|
|
|
Feb 28 2013, 08:33
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(XVR @ Feb 28 2013, 12:29)  О! Вот это вразумительно! Спасибо! Приятно осознавать что ты поумнел после содержательной беседы. Серьёзно. Отдельное спасибо igorle!
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Feb 28 2013, 12:11
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
igorle ,вы привели программу Код void foo1(int argc, char **argv) { while (argc--) printf("\"%s\"\n", argv++); }
void foo(void) { char *my_argv[] = {"Hello", "World", "one", "two", "four"};
foo1(5, my_argv); } Мне кажется, что argv не указатель на указатель, скорее это указатель на массив указателей ! И в аргументах функции foo1 правильнее будет записать *argv[] вместо **argvИ в том виде в котором Вы запостили, код не компилится и нужно либо в аргументах printf разыменовывать argv либо использовать квадр. скобки и индекс для каждого элемента массива. Мне больше нравится вот так: Код void foo1(char *argv[]) { while (*argv) printf("\"%s\"\n", *argv++); }
int main(void) { char *my_argv[] = {"Hello", "World", "one", "two", "four", NULL}; foo1(my_argv); } Мы подчеркнули природу аргумента функции foo1 ,но вынуждено используем "указательные" св-ва масива для упорядоченного, с начала в конец, вывода элементов на "печать". Кароче трудно как-то все получается
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Feb 28 2013, 13:49
|
Местный
  
Группа: Свой
Сообщений: 338
Регистрация: 14-07-12
Пользователь №: 72 753

|
Цитата Мне кажется, что argv не указатель на указатель, скорее это указатель на массив указателей ! И в аргументах функции foo1 правильнее будет записать *argv[] вместо **argv С точки зрения языка С, в данном случае (для прототипа функции), обе записи равнозначны. Используйте то, что вам понятнее. Со временем придет "чуство" того, что имя массива и указатель - это одно и то-же. Цитата ... код не компилится ... Да, это я ошибся. Нужно *argv++Ваш вариант, с последним элементом NULL, правильный, тем более что функция main действительно получает массив argv размером на единицу больше, чем argc, с NULL терминатором. Цитата Мы подчеркнули природу аргумента функции foo1 ,но вынуждено используем "указательные" св-ва масива для упорядоченного, с начала в конец, вывода элементов на "печать". Короче, трудно как-то все получается  Массив и указатель - близнецы братья. При использовании - это одно и то-же. Немного практики, и это станет простым и естественным. И будет непонятно, как некоторые языки не используют указатели. Верной дорогой идете!
Сообщение отредактировал igorle - Feb 28 2013, 13:50
|
|
|
|
|
Feb 28 2013, 14:52
|
Местный
  
Группа: Свой
Сообщений: 338
Регистрация: 14-07-12
Пользователь №: 72 753

|
Указатель и массив - это не одно и то же. Но имя массива - это указатель на первый его элемент a[0] то же самое что и *a a[10] то же самое что и *(a+10)
Понятно, что при определении переменной int a[] и int *b - разные вещи. Но после этого они во многом ведут себя одинаков. Во многом, но не во всем. a нельзя присваивать новое значение, b - можно.
int a[10]; int *b = a;
Теперь все, что вы хотите делать с a, вы можете делать с b. Обратное не верно Например b[2] - это то-же самое что и a[2].
Я считаю, что это из области, где легче понять чем объяснить. Даже гугл не поможет. И приходит только с небольшим опытом. Просто поиграйте с указателями. Потом - с указателями на указатели. Это действительно просто. Наш экспириенс не поможет. Можете придумыват, писать и компилировать простые примеры из нескольких строк, и задавать конкретные вопросы если не работает. Когда поймете то что говорили про указатели и массивы - посмотрите примеры про обработку связных списков. Там указатели на указатели очень эффективно работают.
Можете порешать олимпиаду, про которую я говорил в соседней ветке. Там тоже про указатели на указатели. Если не выиграете приз, то хоть согреетесь.
Сообщение отредактировал igorle - Feb 28 2013, 14:54
|
|
|
|
|
Mar 1 2013, 02:45
|
Местный
  
Группа: Свой
Сообщений: 459
Регистрация: 30-03-06
Из: Москва
Пользователь №: 15 600

|
Цитата(igorle @ Feb 28 2013, 18:52)  Указатель и массив - это не одно и то же. Но имя массива - это указатель на первый его элемент Дьявол в мелочах (с). Тот случай, когда пренебежение терминологией, привело к не верному выводу. А именно, "указатель" != "адрес". Имя массива - не "указатель" на первый элемент, а "адрес" первого элемента. P.S. Оказывается, объяснить тоже не сложно.
|
|
|
|
|
Mar 2 2013, 14:51
|
Местный
  
Группа: Свой
Сообщений: 338
Регистрация: 14-07-12
Пользователь №: 72 753

|
Цитата(XVR @ Mar 2 2013, 18:39)  Код int dim1=5; int dim2=10;
int** my_array=GenerateArray(dim1,dim2);
my_array[i][j] = ... Забавно. Никогда так не делал. Первая реакция была - "ерунда". Потом подумал - а ведь должно работать....
Сообщение отредактировал igorle - Mar 2 2013, 14:56
|
|
|
|
|
Mar 2 2013, 22:07
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Да, оч. интересно! Правда не совсем ппонимаю, как освобождать память из под такого массивчика?  Если указатель связанный с данными через "new" погибнет, потеряется - память "потечет"!? Если я использую библиотечные функции, ну например "strcat" библиотеки cLib, прототип описан в string.h, выделить память для результирующей строки должен программист? о_О Код /* * Copyright (C) 2002 Manuel Novoa III * Copyright (C) 2000-2005 Erik Andersen <andersen@uclibc.org> * * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. */
#include "_string.h"
#ifdef WANT_WIDE # define Wstrcat wcscat #else # define Wstrcat strcat #endif
Wchar *Wstrcat(Wchar * __restrict s1, register const Wchar * __restrict s2) { register Wchar *s = s1;
while (*s++); --s; while ((*s++ = *s2++) != 0);
return s1; } libc_hidden_def(Wstrcat)
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Mar 3 2013, 07:16
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(Tahoe @ Mar 1 2013, 04:45)  Дьявол в мелочах (с). Вот именно™ Цитата(Tahoe @ Mar 1 2013, 04:45)  Тот случай, когда пренебежение терминологией, привело к не верному выводу. А именно, "указатель" != "адрес". Имя массива - не "указатель" на первый элемент, а "адрес" первого элемента. Имя массива — это имя массива. Оно имеет тип «массив вон-того» (в зависимости от объявления). Почти во всех выражениях оно автоматически приводится к указателю на первый элемент. Цитата(C99) 6.3 Conversions ... 6.3.2 Other operands 6.3.2.1 Lvalues, arrays, and function designators ... 3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Mar 3 2013, 07:22
|
Местный
  
Группа: Свой
Сообщений: 338
Регистрация: 14-07-12
Пользователь №: 72 753

|
Цитата(XVR @ Mar 2 2013, 23:43)  Это один из классических способов реализаций многомерных динамических массивов (я бы даже сказал, что самый первый из классических) Я считаю что хороший способ запутать (и запугать) новичка. Вы должны теперь объяснить, что my_array и your_array следующем примере: Код int** my_array=GenerateArray(6, 9); int your_array[6][9]; это две большие разницы. И вообще эти переменные - разного типа. Это неочевидно ученику. Давайте спросил Буратино (задачу решить на листочке, не используя компилятор): - какого типа your_array? - что вернет sizeof(my_array) и sizeof(your_array)? - можно ли сделать такое присвоение int **his_array = your_array; ?
|
|
|
|
|
Mar 3 2013, 07:25
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(Буратино @ Mar 3 2013, 00:07)  Да, оч. интересно! Правда не совсем ппонимаю, как освобождать память из под такого массивчика?  «наоборотом». Сначала удалить по каждому из указателей из массива указателей, потом удалить по указателю на массив. Такие массивы удобны тем, что каждая «строка» выделена отдельно и хранится отдельно. Можно менять месстами строки, перемещая только значения указателей, не трогая данные. Например, было актуально во времена «640 килобайт» При фильтрации изображений, скажем, окном 3х3, можно выделить место на две дополнительных строки, начать фильтрацию в них. При получении третьей (номер 2) строки выходного изображения первая строка (номер 0) входного изображения уже не нужна, результат можно помещать в неё, потом в нужное место переписать указатель. В итоге через какое-то время строки изображений в памяти разбросаны как попало, но все работает :-)
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Mar 3 2013, 07:38
|
Местный
  
Группа: Свой
Сообщений: 338
Регистрация: 14-07-12
Пользователь №: 72 753

|
Цитата Если я использую библиотечные функции, ну например "strcat" библиотеки cLib, прототип описан в string.h, выделить память для результирующей строки должен программист? о_О Да, и узнать это из исходников - похвальный, но не самый простой путь. Лучше читать man pagesКстати - многие функции еще и не проверяют корректность параметров. Например strcat(NULL, "basa"); просто покрошится.
|
|
|
|
|
Mar 3 2013, 08:13
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(igorle @ Mar 3 2013, 09:22)  это две большие разницы. И вообще эти переменные - разного типа. Это неочевидно ученику. Давайте спросил Буратино Давайте сначала Вы объясните ученику, как при помощи записи вида int your_array[что-то-одно][что-то-другое]; создать массив неизвестного заранее размера с времением жизни, выходящим за рамки функции, в которой он создан. Был вопрос «зачем такое может быть нужно». Дан ответ. Дальше ученик должен думать. Различия можно объяснять. Цитата(igorle @ Mar 3 2013, 09:22)  - что вернет sizeof(my_array) и sizeof(your_array)? sizeof(my_array) вернёт в точности то же самое, что тут вернёт sizeof(your_array)Код int foo(int your_array[6][9]) { return sizeof(your_array); } и всё равно придётся объяснять, почему sizeof(your_array) в двух разных местах возвращает разные значения. Кстати, я считаю, что запись foo(char *arr[]) гораздо хуже, чем foo(char **arr). В итоге-то одно и то же, у функций одинаковый прототип, принимают pointer to pointer to char. В режиме компиляции С++ должно быть одинаковое mangled-имя. В первой записи arr по сути тот же char **, только этого не видно (пресловутому ученику) и этот arr невозможно сделать константным. foo(char const * const arr[])Вот тут аж дальние char — константные. Указатели на них, хранящиеся в массиве, на который указывает аргумент arr (в С ведь массивы не передаются как параметры, не правда ли?) — тоже константные. А сам аргумент - не константный, хоть плачь. Код int foo(char const * const arr[]) { int i = 0; while( *arr++) ++i; // Тут всё отлчино! return i; }
int moo(char const * const * const arr) { int i = 0; while(*arr++) ++i; // А вот тут компилятор говорит об ошибке return i; } всё, что имеем: Цитата arrn.c: In function ‘moo’: arrn.c:11: error: increment of read-only location ‘arr’ А с первой функцией все нормально, компилятор молчит.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Mar 3 2013, 08:53
|
Местный
  
Группа: Свой
Сообщений: 459
Регистрация: 30-03-06
Из: Москва
Пользователь №: 15 600

|
Цитата(ReAl @ Mar 3 2013, 11:16)  Имя массива — это имя массива. Оно имеет тип «массив вон-того» (в зависимости от объявления).Почти во всех выражениях оно автоматически приводится к указателю на первый элемент. Ещё раз. Указатель, это переменная, занимающая место в памяти. Под адрес же, память не выделяется. Что там происходит дальше, на что похоже, как оптимизируется и т.п. - дело десятое. Не знаю как сейчас, а еще 3-4 года назад тот же IAR/ARM, на максимальной оптимизации, генерил совершенно разный код в одном и том же цикле, для Array[i] и *(pArray+i). Т.е. работал совершенно по-разному ( через указатель гораздо быстрее ). "Хотя казалось бы..." (с). Пришлось ручками ковырять libavcodec, на предмет переделки обращения a[] в *pa, потому что иначе SAM7S64 не успевал размотать два канала честного ADPCM.
|
|
|
|
|
Mar 3 2013, 09:56
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(Tahoe @ Mar 3 2013, 10:53)  Ещё раз. Указатель, это переменная, занимающая место в памяти. Ну хорошо. «is converted to an expression with type ‘‘pointer to type’’»приводится к выражению типа «указатель на»Не к «переменной типа „указатель на”». А к выражению. Приводится, а не есть им. Результат этого выражения может не сохраняться в памяти. Может сохраняться во временной переменной в регистре. Может в результате оптимизации сразу компилироваться в тело команды. Но сначала имя массива приводится к указателю на. Везде, кроме & и sizeof. Только в результате этого приведения выражения Код array[i] и Код i[array] эквивалентны. Да, кстати. Возьмём char a, b;. Теперь возьмём выражение a+b. Приянто говорить «перед сложением a и b приводятся к целому». Вы и здесь будете говорить «Ещё раз. Целое это переменная, занимающая место в памяти.» ? «Целое», «указатель на» — это типы. А не переменные этих типов. Кстати, и «переменная типа „указатель на”» может не занимать место в памяти. И даже в регистре. После оптимизации, конечно.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|