LINUX.ORG.RU

Глюк GCC?


0

1

Программа ниже, если собирать с -O2 (или -O3), то вывод получается примерно такой:

------- 0x08724018 0x00000000 0x08724008
А если с -O1, то такой (какой и ожидался):
------- 0x09917018 0x09917008 0x09917008

Это я где-то не доглядел или глюк GCC?

Художественные достоинства кода просьба не обсуждать, если они не связанны непосредственно с проблемой - это вырезка из большой программы.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

typedef struct List_t
{
  struct List_t *next;
} List_t;

typedef struct Symbol_t
{
  struct Symbol_t *next;
} Symbol_t;

typedef struct First_t
{
  Symbol_t       *set;
} First_t;

static void list_add(void **list, void *element)
{
  ((List_t *)element)->next = NULL;

  if (*list)
  {
    List_t *last = *list;
    while (last->next)
      last = last->next;
    last->next = element;
  }
  else
    *list = element;
}

int main(void)
{
  First_t *item;
  Symbol_t *res;

  res = malloc(sizeof(Symbol_t));
  res->next = NULL;

  item = malloc(sizeof(struct First_t));
  item->set = NULL;
  list_add((void *)&item->set, res);

  printf("------- 0x%08lx 0x%08lx 0x%08x\n", item, item->set, res);
  return 0;
}
★★★★

Последнее исправление: alexru (всего исправлений: 1)

Не понял юмора.

anonymous
()

похоже на баг c GCC 4.4.x и 4.5.x

/tmp :$cc3 -O1 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc3 -O2 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc41 -O1 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc41 -O2 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc42 -O1 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc42 -O2 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc43 -O1 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc43 -O2 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc44 -O1 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc44 -O2 code.c ; ./a.out
------- 0x0804a018 0x00000000 0x0804a008
/tmp :$cc45 -O1 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc45 -O2 code.c ; ./a.out
------- 0x0804a018 0x00000000 0x0804a008
/tmp :$cc46 -O1 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc46 -O3 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc41 -O3 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc42 -O3 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc43 -O3 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc44 -O3 code.c ; ./a.out
------- 0x0804a018 0x00000000 0x0804a008
/tmp :$cc45 -O3 code.c ; ./a.out
------- 0x0804a018 0x00000000 0x0804a008
/tmp :$cc46 -O3 code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008

Sylvia ★★★★★
()
Ответ на: комментарий от Sylvia

code.c:73:23: warning: conversion specifies type 'unsigned long' but the argument has type 'First_t *' (aka 'struct First_t *') [-Wformat]
printf("------- 0x%08lx 0x%08lx 0x%08x\n", item, item->set, res);
~~~~^ ~~~~
code.c:73:31: warning: conversion specifies type 'unsigned long' but the argument has type 'Symbol_t *' (aka 'struct Symbol_t *') [-Wformat]
printf("------- 0x%08lx 0x%08lx 0x%08x\n", item, item->set, res);
~~~~^ ~~~~~~~~~
code.c:73:38: warning: conversion specifies type 'unsigned int' but the argument has type 'Symbol_t *' (aka 'struct Symbol_t *') [-Wformat]
printf("------- 0x%08lx 0x%08lx 0x%08x\n", item, item->set, res);
~~~^ ~~~
3 warnings generated.

Sylvia ★★★★★
()

Всё верно.

добавь -fno-inline при компиляции с -O2 - покажет результат тот же, что и с -O0 или -O1.

Почему - сам догадаешся или разжевать?:)

Led ★★★☆☆
()
Ответ на: комментарий от Sylvia

Нету никакой баги - item->set не инициализируется после «item->set = NULL;», потому как нигде не используется:)

Led ★★★☆☆
()
Ответ на: комментарий от Led

Выводится неверное значение (0), я хочу там увидеть адрес, пусть даже после этого он и не будет использоваться.

alexru ★★★★
() автор топика
Ответ на: комментарий от Led

я код не смотрела :)
в любом случае 4.4 и 4.5 ведут себя иначе чем
3.4.6
4.1.2
4.3.5
4.6-prerelease
Clang 2.8
ICC 11.1

Sylvia ★★★★★
()
Ответ на: комментарий от alexru

Проблема даже не в том, выводится или нет, в реальной программе он используется, что приводит к сегфолту :)

alexru ★★★★
() автор топика
Ответ на: комментарий от alexru

>Выводится неверное значение (0)

Почему неверное? Вполне верное: адрес неиспользуемый нигде в коде вполне может быть NULL.

Led ★★★☆☆
()
Ответ на: комментарий от alexru

>в реальной программе он используется, что приводит к сегфолту

Ну, если в «реальной программе»... Но пока что это всего лишь ваши слова. Приведённый же вами пример на тесткейс не тянет - там всё корректно.

Led ★★★☆☆
()
Ответ на: комментарий от alexru

/tmp :$cc45 -O3 -fno-strict-aliasing code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008
/tmp :$cc44 -O3 -fno-strict-aliasing code.c ; ./a.out
------- 0x0804a018 0x0804a008 0x0804a008


вам там кстати правильно про aliasing написали

Sylvia ★★★★★
()
Ответ на: комментарий от Sylvia

Т.е. быдлоко^W"программеры", кастящие в 'void*' всё-что-не-попадя, а потом удивляющиеся «почему не работает» - ССЗБ?:)

Led ★★★☆☆
()
Ответ на: комментарий от Led

Скорее всего да, я себя до последнего борол, но колличество списков таких списков превысило все разумные пределы и я сделал для них одну функцию. Если подскажите как сделать это правильно - спасибо скажу :)

alexru ★★★★
() автор топика
Ответ на: комментарий от Led

в смысле - убрать эту строку.

Но это тоже не выход, а воркэраунд. Кастить всё в void* - это всегда плохо. В 99% случаев это говорит о неправильном проектировании и заменой кода здесь, по хорошему, не обойдёшся

Led ★★★☆☆
()
Ответ на: комментарий от Led

Кастить всё в void* - это всегда плохо.

Что-же теперь без очередей для произвольных типов совсем жить?

alexru ★★★★
() автор топика
Ответ на: комментарий от alexru

а зачем тогда вобще дёргать list_add(), если заведомо известно, что работать будет только ветка 'else'?

Led ★★★☆☆
()
Ответ на: комментарий от Sylvia

Это похоже на баг.

$ gcc --version
gcc (Gentoo 4.4.3-r2 p1.2) 4.4.3
$ gcc -O3 1.c && ./a.out
------- 0x00602030 0x00000000 0x00602010
$ gcc -O3 1.c -fno-inline && ./a.out 
------- 0x00602030 0x00602010 0x00602010

linuxfan
()
Ответ на: комментарий от simplex

>В GTK и GLib void* используется часто, и вроде все работает.

«Вроде» GTK и GLib врядли можно назвать примером правильной архитектуры:)

Led ★★★☆☆
()
Ответ на: комментарий от Led

Потому что в следующий раз для этого же списка сработает не else. Писать эти проверки каждый раз в код может и можно, но это такая нечитаемая каша, что лучше с -fno-strict-aliasing жить, хоть и идеологически неверно.

alexru ★★★★
() автор топика
Ответ на: комментарий от linuxfan

>Это похоже на баг.

Да. Это похоже на баг в коде

Led ★★★☆☆
()
Ответ на: комментарий от linuxfan

Разобрались уже, это не баг, это действительно нарушение алиасинга. -fno-inline просто заставляет делать код, в котором такая оптимизация не возможна, вот компилятор ее и не делает.

alexru ★★★★
() автор топика
Ответ на: комментарий от Led

Да, в следующий раз там будет значение от предыдущего. Короче, это стандартный код для работы со списками, чего-бы там ни гоаорили про не правильную архитектуру.

alexru ★★★★
() автор топика
Ответ на: комментарий от alexru

вот ваш код в упрощенном виде:

$ cat test.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct Symbol_t
{
    Symbol_t *next;
};

struct First_t
{
    Symbol_t *set;
};

int main(void)
{
    Symbol_t *res;
    res = (Symbol_t*)malloc(sizeof(Symbol_t));
    if (res == NULL) { printf ("ERROR malloc()\n"); exit(1); }

    First_t *item;
    item = (First_t*)malloc(sizeof(struct First_t));
    if (item == NULL) { printf ("ERROR malloc()\n"); exit(1); }

    item->set = NULL;


    void **list = (void **)&item->set;
    *list = res;


    printf("------- 0x%08lx 0x%08lx 0x%08x\n", item, item->set, res);


    return 0;
}

Вам не кажется странной эта конструкция:

void **list = (void **)&item->set;
*list = res;

quest ★★★★
()

Ну так и пиши -item->set = NULL; +list_init((void *)&item->set);

anonymous
()
Ответ на: комментарий от quest

Вам не кажется странной эта конструкция:

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

alexru ★★★★
() автор топика
Ответ на: комментарий от quest

вы точно хотели написать это а не list_add((void **)&item, res);

Нет, не точно :) Я хотел от warning-ов избавиться, хотябы таким кривым способом.

В конечном итоге я остановлюсь на чем-то подобном реализации в Linux-e, покрайней мере этих приведений не будет.

alexru ★★★★
() автор топика
Ответ на: комментарий от alexru

Я не могу в list_add() использовать не void, она работает с несколькими списками разных типов.

Тогда либо С++ и шаблоны, либо С и имитация шаблонов на препроцессоре.

Begemoth ★★★★★
()
}
else
{
 *list = element;
 printf("------- 0x%08lx 0x%08lx\n", *list, element);
}

------- 0x00000000 0x00602010

агрессивная оптимизация printf

ACR
()
Ответ на: комментарий от alexru

>В конечном итоге я остановлюсь на чем-то подобном реализации в Linux-e, покрайней мере этих приведений не будет.

А где в исходниках Linux?

Yareg ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.