× К оглавлению На главную Об авторе

   Дата и время публикации:

выражение, указывающее на потенциальный объект данных.


Под объектом данных понимаются переменные, массивы данных, константные и иные выражения, под которые была выделена память и явно назначен тип отличный от void. В противном случае, при попытке использовать lvalue, поведение не определено и обычно сборка заканчивается с ошибкой компилятора.

(п.6.3.2.1 ISO/IEC 9899:2x или более поздней редакции)

Таким образом, выражение lvalue должно иметь объект данных, которому назначен адрес, что подразумевается наличие доступ на протяжении всей времени жизни области видимости, как показано в листинге 1

Листинг 1


 1 void* foo ( void *buf, char *label )
 2 {
 3    return (buf=label);  
 4 }
 5 
 6
 7typedef void* (*change_t) ( void *buf, char *label );
 8  
 9struct some_entry
10{
11   int   id;
12   char *name; 
13};
14
15int main (int argc, char **argv)
16{
17/* Определение объектов данных */
18
19 float   fps[3]             = { 0.23445, 89.789, -879.12 };   
20 int     numirics [10]      = { '0','1','2','3','4','5','6','7','8','9'};
21 char    *hellop            = "hello world!";    
22 struct some_entry  list[7] = {
23			       { 0, "Peter"     },
24			       { 1, "John"      },
25			       { 2, "Robert"    },
26			       { 3, "Johann"    },
27			       { 4, "Lacksley"  },
28			       { 5, "Albert"    },
29			       { 6, "Hugh"      },	  
30			      };
31 int     base         = 2;
32 change_t   callptr   = change;
33 char    *cstrp       = 1+hellop,
34         *strp        = --cstrp,
35         *nextp       = ++cstrp; 
36 int     *np          = numirics+(base+1);
37 float   *fp          = fps + 1;
38
39 struct some_entry *dp = list+(2*base+1);
...
48 return 0;
49}
50
51/*eof*/

Как показано в листинге 1, в строках 19,...,22 определяются массивы данных, которым выделяется память в секции данных .rodate, а в строках 31,...,35 непосредственно lvalue, результат совмещения с кодом ассемблера можно получить c помощью gasrunparts опции gnuc_locator_value (версии не ниже 15).

Примечание Применять словосочетание locator value, не совсем корректно, потому что речь идет все же о некоем выражении стоящим в левой части, а не указателе на объект, хотя во многих случаях таковым и является. Особенно, когда речь идет о константных значениях, стоящих в левой части.

Согласно п.6.3.2.1 ISO/IEC 9899:2x, к модифицированным выражениям lvalue (англ. modifiable lvalue) относятся такие lvalue, которые не имеют неполных типов (англ. incomplete type), не объявлены с квалификатором const. Опять же, если тип является структурным или объединением, то он не должен содержать квалификатор const, как и включая, рекурсивно, любой член или элемент всех содержащихся составных типов или объединений.

Поэтому, как показано в листинге 2, если вдруг массив hellop в строке 21 листинга 1 обретет квалификатор const, а в строке 33 выражение останется неизменным,

Листинг 2


...
21 char    *hellop      = "hello world!";     
...
33 char    *cstrp       = 1+hellop,

все закончиться большим пуком в виде ошибки на выходе компилятора, которая показана в дампе 1

Дамп 1


. . .
user@home:~/Projects/gasrunparts-0.15$ src/gasrunparts gnuc_locator_value
Compile "/usr/local/share/gasrunparts/examples/gnuc_lvalue.c" in dir "/home/user/Projects/gasrunparts-0.15"
/usr/local/share/gasrunparts/examples/gnuc_lvalue.c: In function ‘main’:
/usr/local/share/gasrunparts/examples/gnuc_lvalue.c:33:26: error: initialization discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
  char    *cstrp        = 1+hellop,
                          ^
cc1: all warnings being treated as errors
error: can't compile THIS!
Failure
. . .

В прочем, если все-таки выражение lvalue относится к модифицируемым, и планируется использовать некоторые операторы, необходимо не забывать о накладываемых к ним ограничениям, перечисленных в таблице 1

Накладываемые ограничения по использованию операторов с lvalue
Оператор Требования
& (унарный) Операнд должен быть lvalue
++, -- (инкремента,декремента) Операнд должен быть lvalue. Требование применяется как постфиксной, так и префиксной формам записи, но не применима к типам объектов, имеющих вещественный тип (float,double) и агрегатный, т.е. составной как в случае со структурами.
=, +=, -=, *=, %=, <<=, >>=, &=, ^=, |= Первый операнд, слева должен быть lvalue

Поэтому нам не возбраняется применять операцию инкремента (++), например к массиву (hellop) в строке 33 листинга 1, как показано в листинге 3

Листинг 3


...
 33 char    *cstrp       = 1+hellop++,
...

Но ни в коим случае, употреблять с вещественным типом (float,double) и агрегатным, например к структуре some_entry, как показано в листинге 3а

Листинг 3a


37 float   *fp          = fps--;
...
39 struct some_entry *dp = ++list+(2*base+1);
...

Такая варварская форма записи непременно приведет к сообщениям, показанным в дампе 1а

Дамп 1а

. . .
/usr/local/share/gasrunparts/examples/gnuc_lvalue.c:41:29: error: lvalue required as decrement operand
  float   *fp           = fps--;
                             ^~
/usr/local/share/gasrunparts/examples/gnuc_lvalue.c:43:26: error: lvalue required as increment operand
  struct some_entry *dp = ++list +(2*base+1);
. . .

В тоже время, не возбраняется производить обе операции инкремента (++) и декремента (--) в постфиксной и префиксной форме записи, как показано в строках 34, 35 листинга 1

А вот употреблять спецификатор класса хранения объекта register не следует. Особенно, если не предполагается объект инициализировать,т.е. при объявлении переменной base не был применен оператор присваивания, как показано в листинге 4

Листинг 4


...
31 register int  base;
...
36 int     *np = & base;
... 

Из которого, компилятор сделал печальный вывод с сообщением об ошибке, что регистровой переменной base хорошо бы иметь адрес на реальный объект. Полный текст "обшибочного" сообщения компилятора приведен в дампе 2

Дамп 2

. . .
/usr/local/share/gasrunparts/examples/gnuc_lvalue.c:40:2: error: address of register variable ‘base’ requested
  int     *np           = &base;
  ^~~
. . . 

Конверсия от lvalue к rvalue производится, когда компилятор ждет от lvalue перевоплощения в rvalue, которое будет произведено согласно условиям предшествующие ситуациям, перечисленным в таблице 2 .

Таблица 2. Перечень ситуаций, предшествующих конверсии lvalue к rvalue
Благоприятные и не очень условия возникновения ситуации конверсии Результирующее поведение
lvalue является функциональным типом Соответсвует заявленным условиям
lvalue является массивом
lvalue относится неполным типом Закончится ошибкой во время компиляции
lvalue ссылается на неинициализированный объект Неопределенное поведение.
lvalue ссылается на объект не являющимся rvalue или производным от него.

(п.6.5.4/5 ISO/IEC 9899:2x или более поздней редакции)

Листинг 5


...
 40  /* conversion rvalue to rvalue */
 41  if ( callptr != NULL )
 42     {
 43       hellop = callptr(strp,"this is test!");
 44     }
 45
 46  printf ( "\n\n!!! hellop=[%s] !!! \n\n", hellop );
 47
...

В результате неявных преобразований типа, которые вылились в конвертирование lvalue к rvalue, как показано в строках 41 и 43 листинга 5.

Таким образом,

В первой строке реализуется соотношение функция-указатель, потому что проверяется наличие адреса, присвоенного rvalue в строке 32 листинга 1

Во второй строке, соответственно, имеем дело со вторым соотношением, т.к. по функциональному указателю callptr будет вызвана функция change(), который вернет указатель на массив "this is test!", в виде результирующего значения, возвращаемого этой функции, а затем присваиваемого указателю hellop. Что и иллюстрирует дамп 3 выполнение gasrunparts c помощью опции gnuc_locator_value (версии не ниже 15).

Дамп 3

user@home:~/Projects/gasrunparts-0.15$ src/gasrunparts gnuc_locator_value
Compile "/usr/local/share/gasrunparts/examples/gnuc_lvalue.c" in dir "/home/user/Projects/gasrunparts-0.15"
Running "gnuc_lvalue" in dir "/home/user/Projects/gasrunparts-0.15 "


!!! hellop=[this is test!] !!! 

retcode: 0
== Press key Enter to continue ==
. . .

 


Библиография

3.1 Understanding lvalues and rvalues in C and C++

3.2 The Tutorials point. C - Variables

3.3 IBM® Knowledge Center. lvalue to rvalue conversion

3.4 The StackOverflow. lvalue to rvalue implicit conversion