Почему l-значение требуется как левый операнд присваивания в этом for-l oop? - PullRequest
1 голос
/ 05 апреля 2020

Это не мой код; Я работаю над получением очень старого клиента и сервера inte rnet для обмена файлами / чатами, скомпилированного для Linux - и хотя сервер компилируется нормально, я сталкиваюсь с ошибками при компиляции клиента.

fh_lookup (const char *path)
{
        struct hl_filelist_hdr *fh;
        char const *p, *ent;
        char *dirpath;
        int len, flen, blen = 0;
        u_int16_t fnlen;
        struct cached_filelist *cfl;

        ent = path;
        len = strlen(path);
        for (p = path + len - 1; p >= path; p--) {
                if (*p == dir_char) {
                        ent = p+1;
                        while (p > path && *p == dir_char)
                                p--;
                        blen = (p+1) - path;
                        break;
                }
        }

        dirpath = xmalloc(blen + 1);
        memcpy(dirpath, path, blen);
        dirpath[blen] = 0;

        for (cfl = cfl_list->next; cfl; cfl = cfl->next)
                if (!strcmp(cfl->path, dirpath))
                        break;
        xfree(dirpath);
       if (!cfl)
                return 0;

        for (fh = cfl->fh; (u_int32_t)((char *)fh - (char *)cfl->fh) <
cfl->fhlen; 
            (char *)fh += flen + SIZEOF_HL_DATA_HDR) {
                L16NTOH(flen, &fh->len);
                L16NTOH(fnlen, &fh->fnlen);
                if (!memcmp(fh->fname, ent, fnlen))
                        return fh;
        }


        return fh;
}

При компиляции я получаю ошибку:

error: lvalue required as left operand of assignment
             (char *)fh += flen + SIZEOF_HL_DATA_HDR) {

Но я не совсем уверен, почему. Эта ошибка появляется в циклах for и в других файлах; Так что это может быть старый C стиль, возможно? Я думаю, что оригинальный код был написан в 2003 году. Я не уверен. Но любая помощь, чтобы исправить это будет принята с благодарностью.

Ответы [ 4 ]

2 голосов
/ 05 апреля 2020

В выражении присваивания должно быть место для хранения присваиваемого значения. Таким образом, левый операнд должен обозначать такое место. Это называется lvalue - lvalue - это выражение, которое обозначает объект, такой как int, double, другой тип basi c, структура и некоторые другие вещи.

Самым распространенным lvalue является просто идентификатор - имя объекта. Например, после int x; определяет объект (память, зарезервированная для x) и идентификатор (имя x), x обозначает объект.

Константы, такие как 37 или 'a' - это просто значения в C. Они ничего не обозначают в памяти. Большинство выражений производят простые значения, а не lvalues. Например, результат 3 * x в три раза превышает значение x (если переполнения нет), и это просто значение, а не lvalue, даже если в нем используется x. Другой пример - результат приведения (char *) p - это просто значение указателя; это не lvalue для p.

Другие lvalue включают в себя:

  • Результаты применения унарных * к указателям. Если p является указателем с допустимым значением, указывающим на некоторый объект, тогда *p является lvalue для объекта, поэтому вы можете написать *p = 37;, предполагая, что тип объекта совместим с назначенным 37.

  • Члены структур. Если s является структурой с членом foo, тогда s.foo является lvalue для этого члена. Это относится и к указателям на структуры; если p указывает на s, тогда p->foo является lvalue для члена foo. (Обратите внимание, что s само по себе должно быть lvalue. Возможно иметь временную структуру, которая является просто значением. В этом случае ссылка на его член является просто значением, а не lvalue.)

В назначении (char *)fh += flen + SIZEOF_HL_DATA_HDR, (char *)fh не определено как lvalue по стандарту C. Если этот код был принят неким C компилятором, то этому компилятору было предоставлено необычное расширение для языка C.

Похоже, целью этого оператора является добавление flen + SIZEOF_HL_DATA_HDR байтов к указанному месту до fh. Если это так, это может быть достигнуто путем преобразования fh в char *, добавления требуемой суммы, преобразования обратно в тип fh, а затем присвоения результата fh:

fh = (struct hl_filelist_hdr *) ((char *) fh + flen + SIZEOF_HL_DATA_HDR)

(Существуют определенные опасности при выполнении такого рода арифметики необработанных указателей c. Этот ответ не говорит с ними; мы предполагаем, что базовый код имеет дело с ними и предназначен для реализации, поддерживающей то, что он делает.)

0 голосов
/ 06 апреля 2020

Поскольку я потратил некоторое время на очистку, я опубликую свой переработанный код здесь. ИМХО код старше 2003 года, он использует alloca-конструкции и "struct hack". Вероятно, код magi c voodoo был скопирован из других сетевых игровых программ и никогда не был переработан. Он может появиться в девяностых годах.


        /* GUESSED structure definitions */
struct hl_filelist_hdr {
        u_int16_t len;          //<< Total length, rounded up modulo 32bit (EXLUDING header??) ; in network byte order.
        u_int16_t fnlen;        //<< string length, presumably, including NUL-byte, rounded up modulo 32bit; in network byte order.
        char fname[1];          //<< "STRUCT HACK" construct???
        };


struct cached_filelist {
        struct cached_filelist  *next;
        unsigned fhlen;
        char *path;
        struct hl_filelist_hdr *fh;
        };

        /* appears to be global */
struct cached_filelist *cfl_list=NULL;

struct hl_filelist_hdr *fh_lookup (const char *path)
{
        struct hl_filelist_hdr *fh;
        struct cached_filelist *cfl;
        unsigned len, namelen, dlen = 0;
        unsigned nstart;

        len = strlen(path);
        if(!len) return NULL; // an empty path would not make sense

                // find final (back)slash
        nstart = 0;
        for (dlen=len ; dlen-- >0; ) {
                if (path[dlen] != dir_char) continue;
                // filename starts after the final slash
                nstart = dlen+1;
                // if the slash was doubled, dont include it in the dirpath.
                while (dlen  > 0 && path[dlen-1] == dir_char) {dlen--;}
                break;
        }
        namelen = len - nstart;
        if(!namelen) return NULL; // an empty name would not make sense

        for (cfl = cfl_list->next; cfl; cfl = cfl->next) {
                if (!memcmp(cfl->path, path, dlen) && !cfl->path[dlen]) break;
                }

                // Directory is not in cache: nothing we could do
       if (!cfl) return NULL;

        /** we dont need this anymore
        dirpath = xmalloc(dlen + 1);
        memcpy(dirpath, path, dlen);
        dirpath[dlen] = 0;
        **/

                /* Without the MACRO definitions, this cannot be
                ** rewritten.
                ** It looks like a "struct-hack" kind of thing, but aligned on 32bit boundaries
                */
        for (   fh = cfl->fh;
                (u_int32_t)((char *)fh - (char *)cfl->fh) < cfl->fhlen; // <<--
                (char *)fh += flen + SIZEOF_HL_DATA_HDR //<<--
                ) {
                u_int16_t fnlen;`               //<<
                L16NTOH(flen, &fh->len);        //<<-- len appears to be a rounded-up length of the variable-size struct-array element, in bytes.
                L16NTOH(fnlen, &fh->fnlen);     //<<--
                if (!memcmp(fh->fname, path+nstart, fnlen)) // fnlen appears to include the NUL-byte
                        return fh; // Found it!
        }

        return NULL; // not cached
        // return fh; <-- would point beyond the allocated size. Is this intended?
}
0 голосов
/ 05 апреля 2020

Ну, выражение (char *)fh не является lvalue для вашего компилятора. Строка неверна:

(char *)fh += flen + SIZEOF_HL_DATA_HDR

Я могу исправить это следующим образом:

fh = (struct hl_filelist_hdr *)((char *)fh + flen + SIZEOF_HL_DATA_HDR))

У нас есть следующий синтаксис для назначения оператор в C:

lvalue = rvalue;

Значение lvalue (значение локатора) представляет объект, который занимает определенное место в памяти или (редко) в самой памяти. Я упомяну некоторые примеры lvalue в современной C:

#define X a int a, b[2], *c, **d; const int e = -1;

  • переменной (например: a = 0;)
  • структуре (например, : structa = structb;)
  • член массива (например: b[0] = 3;)
  • указатель на переменную, структуру, массив или член массива (например: *c = 4;)
  • указатель на ссылку (например: *&c = &a;)
  • указатель на другой указатель (например: **d = 5;)
  • оператор группы вокруг lvalue (например: (a) = 6;)
  • макросы, заменяющие lvalue (например: X = 7;)
  • константа lvalue является lvalue без разрешения присваивания (например: выражение e = 8; неверно)

Значение L обычно не равно:

  • число (например: 9 = a;)
  • любое математическое выражение (например: a + 10 = 11;)

В вашем случае оператор преобразования не вернет lvalue для назначения.

0 голосов
/ 05 апреля 2020

Здесь: { ссылка } возможно, вы найдете лучшее решение, чем объявление fh как символа *.

Этот ответ говорит о том, что

(int *)p++;

отклонено , но

(*(int**)&p)++;

нет.

В комментарии к этому вопросу: При приведении указателя не получается lvalue. Почему? там написано:

Я не понимаю, почему вам нужно было бы разыграть lvalue. В задании вы можете разыграть назначаемое значение r.

Это еще одно разумное решение, как я думаю, как сообщается в 5-м комментарии под вашим вопросом.

В любом случае, оба ссылки выше могут помочь вам лучше понять, в чем проблема; на первый взгляд, для меня ваш состав должен работать, и я считаю, что я также использую подобные вещи - но я обычно использую нестандартные компиляторы и обычно мне не нужна переносимость.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...