Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Си
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Страницы: 1, 2, 3, 4, 5, 6
ReAl
Хех.
Цитата(С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 обновили, не только С++
ViKo
Наверное, цитата по ссылке
At the end of a full expression. This category includes expression statements (such as the assignment a=b;)
говорит, что любое ^= создает sequence point.
ReAl
Не говорит. В "например" определяющей является точка с запятой, заканчивающая утверждение, а не присваивающий оператор, являющийся вместе со своими операндами выражением.
Цитата
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 = d
Cостоящее из нескольких subexpression.

Может, в С++, особенно в C++11, что-то и не так, я туда ещё так не вчитывался.
Какие-то моменты, undefined в C, стали unspecified в C++, с этими их тонкостями ещё пойди разберись.

Да, кстати, я не подчеркнул отдельно
Цитата( @ Jan 25 2013, 13:11) *
как можно простенькие такие программки потестить?

Вот ответ, разными компиляторами можно:

http://liveworkspace.org

Причём не только C/C++.
В ответ на кусок кода вся диагностическая выдача компилятора (в зависимости от ключей) и, при создании исполняемого файла, выдача на stdout
Буратино
Вот еще вариант. В основе смелая идея (уже ранее высказанная) о том чтоб испоьзовать буфер, но я немного переосмыслил это дело sm.gif

Код
#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;
}
xemul
Цитата(Буратино @ Jan 28 2013, 18:11) *
Итак, я считаю что с задачей А мы справились.

Для таких программ потом и пишут заплатки на тему переполнения какого-нить буфера.
Цитата
Потом мне непонятна почему нужно static const char strue[]="бла-бла-бла"; делать!?

static - чтобы можно было вернуть из функции указатель на что-то вменяемое, const - считайте просто правилом хорошего тона.
Буратино
Цитата(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];
}
ReAl
Цитата(Буратино @ 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];
}
Да, лучше внутри функции.
xemul
Цитата(ReAl @ Jan 28 2013, 19:04) *
Я надеюсь, высказывание касается только того, что перед реверсом либо в процессе оного не проверияется длина строки.

Безусловно.
Т.к. в условии задачи не сказано, что исходная строка должна сохраниться, я бы делал реверс на месте. Цена вопроса - один strlen(). (оно, конечно, не спасёт, если строка вдруг окажется без '\0')
Цитата
Само по себе наличие промежуточного буфера фиксированного размера к ошибкам переполнения буфера не приводит.

К переполнению, естественно, не приведёт. Но если память (опять же вдруг) закончится...

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

Цитата(xemul @ Jan 28 2013, 18:10) *
За жисть. У меня K&R был первого советского издания (в мягком переплёте, голубая обложка), не помню в нём таких упражнений. Хотя, может опять склероз буйствует.
У меня был K&R, любовно кем-то подготовленный (фигурные скобки сделаны из круглых с надпечаткой поверх минусом, ключевые слова двойной печатью, …) и напечатанный на барабанном АЦПУ, переплетённый в переплётной мастерской (кажись, в один заказ с дипломной работй жены, тогда это 1987 год). Тоже таких упражнений не помню :-)
ViKo
Цитата(xemul @ Jan 28 2013, 19:10) *
У меня K&R был первого советского издания (в мягком переплёте, голубая обложка), не помню в нём таких упражнений. Хотя, может опять склероз буйствует.

Во втором издании (в мягком переплете, белая обложка, голубая буква С) есть упражнения по переворачиванию строки.
Буратино
Задача А. Работа над ошибками так сказать..
В этой версии маленькие строки (меньше 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 требует введения второго аргумента, что не очень айс
_Pasha
О майн Готт! Вы на листинг этого чуда хоть смотрели?
Буратино
Цитата(_Pasha @ Jan 29 2013, 18:16) *
О майн Готт! Вы на листинг этого чуда хоть смотрели?


неа, не смотрел. а что там такого страшного?
последний вариант лучше предыдущих тем. что небольшие строки перевернутся без вызова strlen ,ведь даже в вашем варианте нужно вызвать strlen, а потом до половины размера строки ворочать значения. При варианте с буфером я НЕ вызываю strlen и получаю строку в доп. буфере, однако при большой строке переворачиваю строку без буфера, но при этом strlen также не пользую.
_Pasha
Цитата(Буратино @ Jan 29 2013, 19:43) *
неа, не смотрел. а что там такого страшного?
последний вариант лучше предыдущих тем. что небольшие строки перевернутся без вызова strlen ,ведь даже в вашем варианте нужно вызвать strlen, а потом до половины размера строки ворочать значения. При варианте с буфером я НЕ вызываю strlen и получаю строку в доп. буфере, однако при большой строке переворачиваю строку без буфера, но при этом strlen также не пользую.

Ниасилил. Strlen() хоть вызывай, хоть делай сам - оно выглядит одинаково.
Чем, извините, это всё лучше, если оно жрет памяти вдвое больше, не заточено под ASCIIZ, в >3раза больше число итераций. О майн Готт! crying.gif
xemul
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
Буратино
_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
xemul
Код
int main() {
   char s[]= "http://www.youtube.com/watch?v=cZjx6ItATW4";
   char s1[]= "Пачиму-то не работает 1234567890 1234567890 1234567890 1234567890  1234567890 1234567890 1234567890 1234567890 1234567890 1234567890";
   printf("%s\n", reverse (s) );
   printf("%s\n", reverse (s1) );
}

D:\Work\Soft\Test>a
4WTAtI6xjZc=v?hctaw/moc.ebutuoy.www//:ptth
,=
Буратино

Код
#define  LEN_BUF        10

stdout:
4WTAtI6xjZc=v?hctaw/moc.ebutuoy.www//:ptth
0987654321 0987654321 0987654321 0987654321 0987654321 0987654321 0987654321 0987654321 0987654321 1111111111111111111111
Буратино
Есть указатель на указатель на char:
Код
char **argv;

Как и чем его можно инициализировать и как (для чего) можно потом использовать? Что будет если я буду инкрементировать такой указатель на указатель?
К сож. с языком знакомлюсь рывками и че та совершенно запуталсяsad.gif
igorle
обычно это имя переменной, которую получает 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);
toweroff
Да поправят меня, если не прав sm.gif
Указатель передается обычно тогда, когда функция может менять какие-то данные за пределами себя
Указатель за пределами какой-то функции тоже можно менять, не так ли? вот и передается указатель на этот указатель
Lotor
Цитата(toweroff @ Feb 28 2013, 01:08) *
Да поправят меня, если не прав sm.gif
Указатель передается обычно тогда, когда функция может менять какие-то данные за пределами себя
Указатель за пределами какой-то функции тоже можно менять, не так ли? вот и передается указатель на этот указатель

Вы правы - при реализации связных списков, например, именно для этих целей передают указатель на указатель.
igorle
Цитата(toweroff @ Feb 28 2013, 01:08) *
Да поправят меня, если не прав sm.gif

Формально - прав. Фактически - нет. Поправляю.
Вопрос был о 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);
}
demiurg_spb
Цитата(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'ов, инициализированный символами данного строкового литерала с включением неявного завершающего нуля, и далее взятие адреса на нулевой элемент данного объекта.
и немного из другой плюсатой оперы
Цитата
"Память под строковые литералы выделяется статически, поэтому их возврат из функций безопасен" Б. Страуструп

Захотелось найти это в первоисточнике....
igorle
Цитата(demiurg_spb @ Feb 28 2013, 10:06) *
То что вы написали тут формально и гарантированно работать не будет ИМХО....


Это таки будет работать гарантировано. Потому что моя функция возвращает указатели на read only строки из секции data
Ваша функция тоже будет работать, но в ней есть бесполезное копирование строк из read only секции data в read write секцию. Недостатки
- в два раза увеличился объем памяти
- нет защиты от случайного изменения текста (оно в данном случае не желательно)

Посмотрите здесь

Ну и посыпаю голову пеплом по поводу опечатки в названии функции. Должно было быть true_or_false. Хотя так даже прикольно получилось, многозначительно.
demiurg_spb
На платформе АVR и так и сяк будет двойной расход памяти flash + копия в озу пока явно не зададим __flash.
Это так к слову...

Цитата(igorle @ Feb 28 2013, 11:37) *
Посмотрите здесь
Это объяснение не на том уровне. Хотелось бы в контексте Си стандарта...
igorle
Цитата(demiurg_spb @ Feb 28 2013, 10:39) *
На платформе АVR и так и сяк будет двойной расход памяти flash + копия в озу пока явно не зададим __flash.
Это так к слову...

На MSP430 - все литеральные строки хранятся во флэше. А для инициализированных массивов, если я не хочу переносить их п память, достаточно добавить const

Попутно. Оффтопик. Как вам удается редактировать свои сообщения, без того чтобы было написано "изменен тогда-то и тем-то"?
demiurg_spb
Цитата(igorle @ Feb 28 2013, 11:45) *
На MSP430 - все литеральные строки хранятся во флэше. А для инициализированных массивов, если я не хочу переносить их п память, достаточно добавить const

Это всё понятно. У MSP430 единое адресное пространство - просто и удобно... И про повсеместный static const я как бы тоже в теме. Меня интересует конкретный отсыл в стандарт и только...
Цитата
Попутно. Оффтопик. Как вам удается редактировать свои сообщения, без того чтобы было написано "изменен тогда-то и тем-то"?
Также как и вам. Нажимаю редактировать -> быстрое...

В avr-gcc вообще все очень непросто со строковыми литералами.
Можете почитать нашу дискуссию на сей счёт: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56263
И был бы очень рад если ещё кто-то меня поддержит в ней!
igorle
Цитата(demiurg_spb @ Feb 28 2013, 10:58) *
Также как и вам. Нажимаю редактировать -> быстрое...

Я так делаю - обязательно пишет что меняли текст.

Цитата
Можете почитать нашу дискуссию на сей счёт

Почитал. Познавательно. Я сам с AVR не работаю.
XVR
Цитата(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.
Так что возвращать из функции можно biggrin.gif
demiurg_spb
Цитата(XVR @ Feb 28 2013, 12:29) *
О! Вот это вразумительно!
Спасибо! Приятно осознавать что ты поумнел после содержательной беседы. Серьёзно.
Отдельное спасибо igorle!
toweroff
Цитата(igorle @ Feb 28 2013, 12:27) *
Я так делаю - обязательно пишет что меняли текст.

если после поста были добавлены посты других участников, то добавляется информация о редактировании. Возможно, на случай, что ваш пост процитировали, а вы потом что-то в нем поменяли
Если ваш пост последний - можно редактировать без опасения того, что информация о редактировании появится. Это касается конкретно этого форума. На других встречал упоминание о редактировании вне зависимости от других постов, причем даже была информация о всех редактированиях поста
Буратино
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 ,но вынуждено используем "указательные" св-ва масива для упорядоченного, с начала в конец, вывода элементов на "печать".

Кароче трудно как-то все получаетсяsad.gif
igorle
Цитата
Мне кажется, что argv не указатель на указатель, скорее это указатель на массив указателей ! И в аргументах функции foo1 правильнее будет записать *argv[] вместо **argv

С точки зрения языка С, в данном случае (для прототипа функции), обе записи равнозначны. Используйте то, что вам понятнее.
Со временем придет "чуство" того, что имя массива и указатель - это одно и то-же.
Цитата
... код не компилится ...

Да, это я ошибся. Нужно *argv++
Ваш вариант, с последним элементом NULL, правильный, тем более что функция main действительно получает массив argv размером на единицу больше, чем argc, с NULL терминатором.

Цитата
Мы подчеркнули природу аргумента функции foo1 ,но вынуждено используем "указательные" св-ва масива для упорядоченного, с начала в конец, вывода элементов на "печать".

Короче, трудно как-то все получаетсяsad.gif

Массив и указатель - близнецы братья. При использовании - это одно и то-же. Немного практики, и это станет простым и естественным. И будет непонятно, как некоторые языки не используют указатели. Верной дорогой идете!
Буратино
Не думаю, что массив и указатель одно и то же. Указатель это переменная, а массив это массив. Существует тесная, родственная связь механизма доступа к данным в массиве по индексам, и адресной арифметикой указателей, но это все же разные вещи.

Я спрашивал именно о указателе на указатель. Дело в том что гуглится что-то невразумительное на этот счет. В К&R нет подробного освещения вопроса. Уповаю на ваш експириентс. Спасибо!
igorle
Указатель и массив - это не одно и то же. Но имя массива - это указатель на первый его элемент
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].

Я считаю, что это из области, где легче понять чем объяснить. Даже гугл не поможет. И приходит только с небольшим опытом. Просто поиграйте с указателями. Потом - с указателями на указатели. Это действительно просто. Наш экспириенс не поможет. Можете придумыват, писать и компилировать простые примеры из нескольких строк, и задавать конкретные вопросы если не работает. Когда поймете то что говорили про указатели и массивы - посмотрите примеры про обработку связных списков. Там указатели на указатели очень эффективно работают.

Можете порешать олимпиаду, про которую я говорил в соседней ветке. Там тоже про указатели на указатели. Если не выиграете приз, то хоть согреетесь.
Tahoe
Цитата(igorle @ Feb 28 2013, 18:52) *
Указатель и массив - это не одно и то же. Но имя массива - это указатель на первый его элемент

Дьявол в мелочах (с).
Тот случай, когда пренебежение терминологией, привело к не верному выводу. А именно, "указатель" != "адрес". Имя массива - не "указатель" на первый элемент, а "адрес" первого элемента.

P.S.
Оказывается, объяснить тоже не сложно. wink.gif
Буратино
Я вот думаю, что указатель на указатель используется только как формальный аргумент функции. А в качестве фактического аргумента используют адрес простого указателя. Не вижу смысла в использовании указателя на указатель где-то еще. Естественно могу ошибаться.

Скажите, а где можно посмотреть реализацию отдельных функций описаных в заголовочном файле string.h ?
igorle
Цитата(Буратино @ Mar 2 2013, 11:01) *
Скажите, а где можно посмотреть реализацию отдельных функций описаных в заголовочном файле string.h ?

http://www.uclibc.org/

http://git.uclibc.org/uClibc/tree/libc/string
XVR
Цитата(Буратино @ Mar 2 2013, 12:01) *
Я вот думаю, что указатель на указатель используется только как формальный аргумент функции. А в качестве фактического аргумента используют адрес простого указателя. Не вижу смысла в использовании указателя на указатель где-то еще.
Многомерный динамический массив.
Код
int** GenerateArray(int dim1, int dim2)
{
int** rv=new int*[dim1];
for(int i=0;i<dim1;++i)
  rv[i]=new int[dim2];
return rv;
}


// Usage:

int dim1=5;
int dim2=10;

int** my_array=GenerateArray(dim1,dim2);

for(int i=0;i<dim1;++i)
for(int j=0;j<dim2;++j)
   my_array[i][j] = ...

igorle
Цитата(XVR @ Mar 2 2013, 18:39) *
Код
    int dim1=5;
    int dim2=10;

    int** my_array=GenerateArray(dim1,dim2);

    my_array[i][j] = ...


Забавно. Никогда так не делал. Первая реакция была - "ерунда". Потом подумал - а ведь должно работать....
XVR
Цитата(igorle @ Mar 2 2013, 18:51) *
Забавно. Никогда так не делал. Первая реакция была - "ерунда".
Это один из классических способов реализаций многомерных динамических массивов (я бы даже сказал, что самый первый из классических)

Буратино
Да, оч. интересно! Правда не совсем ппонимаю, как освобождать память из под такого массивчика? sm.gif
Если указатель связанный с данными через "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)
ReAl
Цитата(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.
igorle
Цитата(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; ?
ReAl
Цитата(Буратино @ Mar 3 2013, 00:07) *
Да, оч. интересно! Правда не совсем ппонимаю, как освобождать память из под такого массивчика? sm.gif
«наоборотом». Сначала удалить по каждому из указателей из массива указателей, потом удалить по указателю на массив.

Такие массивы удобны тем, что каждая «строка» выделена отдельно и хранится отдельно. Можно менять месстами строки, перемещая только значения указателей, не трогая данные.

Например, было актуально во времена «640 килобайт»
При фильтрации изображений, скажем, окном 3х3, можно выделить место на две дополнительных строки, начать фильтрацию в них. При получении третьей (номер 2) строки выходного изображения первая строка (номер 0) входного изображения уже не нужна, результат можно помещать в неё, потом в нужное место переписать указатель. В итоге через какое-то время строки изображений в памяти разбросаны как попало, но все работает :-)
igorle
Цитата
Если я использую библиотечные функции, ну например "strcat" библиотеки cLib, прототип описан в string.h, выделить память для результирующей строки должен программист? о_О

Да, и узнать это из исходников - похвальный, но не самый простой путь. Лучше читать man pages
Кстати - многие функции еще и не проверяют корректность параметров. Например strcat(NULL, "basa"); просто покрошится.
ReAl
Цитата(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’

А с первой функцией все нормально, компилятор молчит.
Tahoe
Цитата(ReAl @ Mar 3 2013, 11:16) *
Имя массива — это имя массива. Оно имеет тип «массив вон-того» (в зависимости от объявления).Почти во всех выражениях оно автоматически приводится к указателю на первый элемент.

Ещё раз. Указатель, это переменная, занимающая место в памяти. Под адрес же, память не выделяется. Что там происходит дальше, на что похоже, как оптимизируется и т.п. - дело десятое.

Не знаю как сейчас, а еще 3-4 года назад тот же IAR/ARM, на максимальной оптимизации, генерил совершенно разный код в одном и том же цикле, для Array[i] и *(pArray+i). Т.е. работал совершенно по-разному ( через указатель гораздо быстрее ). "Хотя казалось бы..." (с). Пришлось ручками ковырять libavcodec, на предмет переделки обращения a[] в *pa, потому что иначе SAM7S64 не успевал размотать два канала честного ADPCM.
ReAl
Цитата(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 приводятся к целому».
Вы и здесь будете говорить «Ещё раз. Целое это переменная, занимающая место в памяти.» ?
«Целое», «указатель на» — это типы. А не переменные этих типов.

Кстати, и «переменная типа „указатель на”» может не занимать место в памяти. И даже в регистре. После оптимизации, конечно.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.