Что входит в образовательный инструмент для демонстрации необоснованных предположений, которые люди делают в C / C ++? - PullRequest
118 голосов
/ 11 августа 2010

Я хотел бы подготовить небольшой обучающий инструмент для SO, который должен помочь начинающим (и средним) программистам распознавать и оспаривать их необоснованные предположения в C, C ++ и их платформах.

Примеры:

  • "Обтекание целых чисел"
  • "у каждого есть ASCII"
  • "Я могу сохранить указатель на функцию в пустоте *"

Я подумал, что небольшая тестовая программа может быть запущена на различных платформах, которая основывается на «правдоподобных» предположениях, которые, исходя из нашего опыта в SO, обычно делаются многими неопытными / опытными мейнстримовыми разработчиками, и записывают способы их взлома на различных машины.

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

Чтобы достичь этого, я хотел бы попросить вас:

  • Как можно улучшить эту идею?
  • Какие тесты были бы хорошими и как они должны выглядеть?
  • Будете ли вы запускать тесты на платформах, которые вы можете получить, и публиковать результаты, чтобы мы получили базу данных платформ, как они отличаются и почему это различие допускается?

Вот текущая версия тестовой игрушки:

#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <stddef.h>
int count=0;
int total=0;
void expect(const char *info, const char *expr)
{
    printf("..%s\n   but '%s' is false.\n",info,expr);
    fflush(stdout);
    count++;
}
#define EXPECT(INFO,EXPR) if (total++,!(EXPR)) expect(INFO,#EXPR)

/* stack check..How can I do this better? */
ptrdiff_t check_grow(int k, int *p)
{
    if (p==0) p=&k;
    if (k==0) return &k-p;
    else return check_grow(k-1,p);
}
#define BITS_PER_INT (sizeof(int)*CHAR_BIT)

int bits_per_int=BITS_PER_INT;
int int_max=INT_MAX;
int int_min=INT_MIN;

/* for 21 - left to right */
int ltr_result=0;
unsigned ltr_fun(int k)
{
    ltr_result=ltr_result*10+k;
    return 1;
}

int main()
{
    printf("We like to think that:\n");
    /* characters */
    EXPECT("00 we have ASCII",('A'==65));
    EXPECT("01 A-Z is in a block",('Z'-'A')+1==26);
    EXPECT("02 big letters come before small letters",('A'<'a'));
    EXPECT("03 a char is 8 bits",CHAR_BIT==8);
    EXPECT("04 a char is signed",CHAR_MIN==SCHAR_MIN);

    /* integers */
    EXPECT("05 int has the size of pointers",sizeof(int)==sizeof(void*));
    /* not true for Windows-64 */
    EXPECT("05a long has at least the size of pointers",sizeof(long)>=sizeof(void*));

    EXPECT("06 integers are 2-complement and wrap around",(int_max+1)==(int_min));
    EXPECT("07 integers are 2-complement and *always* wrap around",(INT_MAX+1)==(INT_MIN));
    EXPECT("08 overshifting is okay",(1<<bits_per_int)==0);
    EXPECT("09 overshifting is *always* okay",(1<<BITS_PER_INT)==0);
    {
        int t;
        EXPECT("09a minus shifts backwards",(t=-1,(15<<t)==7));
    }
    /* pointers */
    /* Suggested by jalf */
    EXPECT("10 void* can store function pointers",sizeof(void*)>=sizeof(void(*)()));
    /* execution */
    EXPECT("11 Detecting how the stack grows is easy",check_grow(5,0)!=0);
    EXPECT("12 the stack grows downwards",check_grow(5,0)<0);

    {
        int t;
        /* suggested by jk */
        EXPECT("13 The smallest bits always come first",(t=0x1234,0x34==*(char*)&t));
    }
    {
        /* Suggested by S.Lott */
        int a[2]={0,0};
        int i=0;
        EXPECT("14 i++ is strictly left to right",(i=0,a[i++]=i,a[0]==1));
    }
    {
        struct {
            char c;
            int i;
        } char_int;
        EXPECT("15 structs are packed",sizeof(char_int)==(sizeof(char)+sizeof(int)));
    }
    {
        EXPECT("16 malloc()=NULL means out of memory",(malloc(0)!=NULL));
    }

    /* suggested by David Thornley */
    EXPECT("17 size_t is unsigned int",sizeof(size_t)==sizeof(unsigned int));
    /* this is true for C99, but not for C90. */
    EXPECT("18 a%b has the same sign as a",((-10%3)==-1) && ((10%-3)==1));

    /* suggested by nos */
    EXPECT("19-1 char<short",sizeof(char)<sizeof(short));
    EXPECT("19-2 short<int",sizeof(short)<sizeof(int));
    EXPECT("19-3 int<long",sizeof(int)<sizeof(long));
    EXPECT("20 ptrdiff_t and size_t have the same size",(sizeof(ptrdiff_t)==sizeof(size_t)));
#if 0
    {
        /* suggested by R. */
        /* this crashed on TC 3.0++, compact. */
        char buf[10];
        EXPECT("21 You can use snprintf to append a string",
               (snprintf(buf,10,"OK"),snprintf(buf,10,"%s!!",buf),strcmp(buf,"OK!!")==0));
    }
#endif

    EXPECT("21 Evaluation is left to right",
           (ltr_fun(1)*ltr_fun(2)*ltr_fun(3)*ltr_fun(4),ltr_result==1234));

    {
    #ifdef __STDC_IEC_559__
    int STDC_IEC_559_is_defined=1;
    #else 
    /* This either means, there is no FP support
     *or* the compiler is not C99 enough to define  __STDC_IEC_559__
     *or* the FP support is not IEEE compliant. */
    int STDC_IEC_559_is_defined=0;
    #endif
    EXPECT("22 floating point is always IEEE",STDC_IEC_559_is_defined);
    }

    printf("From what I can say with my puny test cases, you are %d%% mainstream\n",100-(100*count)/total);
    return 0;
}

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

ОБНОВЛЕНИЕ Спасибо за ваш вклад. Я добавил несколько случаев из ваших ответов и посмотрю, смогу ли я настроить github для этого, как предложил Грег.

UPDATE : Для этого я создал репозиторий github, файл "gotcha.c":

Пожалуйста, ответьте здесь с исправлениями или новыми идеями, чтобы их можно было обсудить или уточнить здесь. Я объединю их в gotcha.c тогда.

Ответы [ 23 ]

90 голосов
/ 11 августа 2010

Порядок вычисления подвыражений, в том числе

  • аргументы вызова функции и
  • операнды операторов (например, +, -, =, *, /), за исключением:
    • двоичные логические операторы (&& и ||),
    • троичный условный оператор (?:) и
    • оператор запятой (,)

is Unspecified

Например

  int Hello()
  {
       return printf("Hello"); /* printf() returns the number of 
                                  characters successfully printed by it
                               */
  }

  int World()
  {
       return printf("World !");
  }

  int main()
  {

      int a = Hello() + World(); //might print Hello World! or World! Hello
      /**             ^
                      | 
                Functions can be called in either order
      **/
      return 0;
  } 
37 голосов
/ 11 августа 2010

sdcc 29,7 / ucSim / Z80

We like to think that:
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..19-2 short<int
   but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
   but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
From what I can say with my puny test cases, you are Stop at 0x0013f3: (106) Invalid instruction 0x00dd

printf вылетает. "О_О"


gcc 4.4@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 79% mainstream

gcc 4.4@x86_64-suse-linux (-O2)

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 82% mainstream

clang 2.7@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 72% mainstream

open64 4.2.3@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 75% mainstream

intel 11.1@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 75% mainstream

Turbo C ++ / DOS / Small Memory

We like to think that:
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 81% mainstream

Turbo C ++ / DOS / Medium Memory

We like to think that:
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..10 void* can store function pointers
but 'sizeof(void*)>=sizeof(void(*)())' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 78% mainstream

Turbo C ++ / DOS / Compact Memory

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..20 ptrdiff_t and size_t have the same size
but '(sizeof(ptrdiff_t)==sizeof(size_t))' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 75% mainstream

cl65 @ Commodore PET (вице-эмулятор)

альтернативный текст http://i34.tinypic.com/2hh0zmc.png


Я буду обновлять их позже:


Borland C ++ Builder 6.0 в Windows XP

..04 a char is signed
   but 'CHAR_MIN==SCHAR_MIN' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09 overshifting is *always* okay
   but '(1<<BITS_PER_INT)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..16 malloc()=NULL means out of memory
   but '(malloc(0)!=NULL)' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 71% mainstream

Visual Studio Express 2010 C ++ CLR, Windows 7, 64-битная

(должен быть скомпилирован как C ++, потому что компилятор CLR не поддерживает чистый C)

We like to think that:
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 78% mainstream

MINGW64 (предварительная версия gcc-4.5.2)

- http://mingw -w64.sourceforge.net /

We like to think that:
..05 int has the size of pointers
   but 'sizeof(int)==sizeof(void*)' is false.
..05a long has at least the size of pointers
   but 'sizeof(long)>=sizeof(void*)' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 67% mainstream

64-битная Windows использует модель LLP64: и int, и long определены как 32-битные, что означает, что ни один не достаточно длинный для указателя.


avr-gcc 4.3.2 / ATmega168 (Arduino Diecimila)

Неудачные предположения:

..14 i++ is structly left to right
..16 malloc()=NULL means out of memory
..19-2 short<int
..21 Evaluation is left to right
..22 floating point is always IEEE

Atmega168 имеет 16-битный ПК, но код и данные находятся в разных адресных пространствах. У более крупного Atmegas 22-битный ПК!


gcc 4.2.1 на MacOSX 10.6, скомпилированный с -arch ppc

We like to think that:
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits come always first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 78% mainstream

24 голосов
/ 11 августа 2010

Давным-давно я преподавал C из учебника, в котором было

printf("sizeof(int)=%d\n", sizeof(int));

как пример вопроса. Это не удалось для студента, потому что sizeof дает значения типа size_t, а не int, int в этой реализации было 16 битов и size_t было 32, и это был big-endian. (Платформа была Lightspeed C на Macintosh на базе 680x0. Я сказал, что это было давно.)

15 голосов
/ 11 августа 2010

Вы должны включить ++ и -- предположения, которые делают люди.

a[i++]= i;

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

Любая инструкция, которая имеет ++ (или --) и переменную, которая встречается более одного раза, является проблемой.

8 голосов
/ 11 августа 2010

Очень интересно!

Другие вещи, которые я могу придумать, могут быть полезны для проверки:

  • существуют ли указатели функций и указатели данных в одном и том же адресном пространстве? (Разрывы в машинах гарвардской архитектуры, таких как DOS small mode. Однако не знаю, как вы можете это проверить.)

  • если взять указатель данных NULL и привести его к соответствующему целочисленному типу, имеет ли он числовое значение 0? (Разрывы на некоторых действительно древних машинах - см. http://c -faq.com / null / machexamp.html .) То же самое с указателем на функцию. Также они могут иметь разные значения.

  • вызывает ли приращение указателя за концом соответствующего объекта хранения, а затем обратно, разумные результаты? (Я не знаю ни одной машины, на которой это действительно работает, но я полагаю, что спецификация C не позволяет вам даже думать об указателях, которые не указывают ни на (а) содержимое массива или (b) элемент сразу после массива или (c) NULL. См. http://c -faq.com / aryptr / non0based.html .)

  • Приведет ли сравнение двух указателей к разным объектам хранения с <и> согласованными результатами? (Я могу представить себе это нарушение на экзотических машинах на основе сегментов; спецификация запрещает подобные сравнения, поэтому компилятор будет иметь право сравнивать только смещенную часть указателя, а не часть сегмента.)

Хм. Я попробую еще немного подумать.

Редактировать: Добавлены некоторые поясняющие ссылки на превосходный FAQ по C.

5 голосов
/ 11 августа 2010

Я думаю, вам следует попытаться провести различие между двумя очень разными классами «неправильных» предположений.Хорошая половина (сдвиг вправо и расширение знака, ASCII-совместимое кодирование, память линейна, данные и функциональные указатели совместимы и т. Д.) Являются довольно разумными предположениями для большинства C-кодеров, которые могут быть созданы, и даже могут бытьвключен как часть стандарта, если C разрабатывался сегодня, и если у нас не было устаревшего барахла IBM.Другая половина (вещи, связанные с псевдонимами памяти, поведением библиотечных функций, когда память ввода и вывода перекрывается, 32-битные предположения, подобные указателям, помещаются в int или что вы можете использовать malloc без прототипа, это соглашение о вызовах идентичнодля функций с переменными и не-переменными значениями ...) либо конфликт с оптимизациями, которые хотят выполнять современные компиляторы, либо с переходом на 64-разрядные машины или другие новые технологии.

4 голосов
/ 12 августа 2010
EXPECT("## pow() gives exact results for integer arguments", pow(2, 4) == 16);

Еще один текстовый режим в fopen. Большинство программистов предполагают, что текст и двоичный код одинаковы (Unix) или что текстовый режим добавляет \r символов (Windows). Но C был портирован на системы, которые используют записи фиксированной ширины, в которых fputc('\n', file) в текстовом файле означает добавление пробелов или чего-то еще, пока размер файла не будет кратен длине записи.

А вот мои результаты:

gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3 на x86-64

We like to think that:
..05 int has the size of pointers
   but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
From what I can say with my puny test cases, you are 78% mainstream
4 голосов
/ 11 августа 2010
  • Ошибки дискретизации из-за представления с плавающей запятой.Например, если вы используете стандартную формулу для решения квадратных уравнений, или конечные разности для приближенных производных, или стандартную формулу для вычисления отклонений, точность будет потеряна из-за вычисления различий между подобными числами.Алгоритм Гаусса для решения линейных систем плох, потому что накапливаются ошибки округления, поэтому используются разложение QR или LU, разложение Холецкого, SVD и т. Д. Добавление чисел с плавающей запятой не ассоциативно.Существуют денормальные, бесконечные и NaN-значения. a + b - a b .

  • Строки: Разница между символами,кодовые точки и кодовые единицы.Как Unicode реализован в различных операционных системах;Unicode кодировки.Открытие файла с произвольным именем файла Unicode невозможно в C ++ в переносимом виде.

  • Условия гонки, даже без многопоточности: если вы проверите, существует ли файл, результат может статьнедействителен в любое время.

  • ERROR_SUCCESS = 0

4 голосов
/ 11 августа 2010

Ну, классические предположения о переносимости, которые еще не подразумеваются,

  • предположения о размере целых типов
  • * 1006 порядок байт *
4 голосов
/ 12 августа 2010

Вот забавно: что не так с этой функцией?

float sum(unsigned int n, ...)
{
    float v = 0;
    va_list ap;
    va_start(ap, n);
    while (n--)
        v += va_arg(ap, float);
    va_end(ap);
    return v;
}

[Ответ (rot13): Inevnqvp nethzragf borl gur byq X & E cebzbgvba ehyrf, juvpu zrnaf lbh pnaabg hfr 'sybng' (be 'pune)'быть' fubeg ') va in_net!Нак гур пбзцвире vf erdhverq abg gb gerng guvf nf n pbzcvyr-gvzr reebe.(ТЭС qbrf rzvg n jneavat, gubhtu.)]

...