Странные целые числа в языке c - PullRequest
0 голосов
/ 16 марта 2019

У меня есть код:

#include <stdio.h>

int main() {
  int a = sum(1, 3);

  return 0;
}

int sum(int a, int b, int c) {
  printf("%d\n", c);

  return a + b + c;
}

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

(Скомпилировано gcc v6.3.0)

Я проигнорировал implicit declaration of function warning и запустил программу несколько раз, вывод был такой:

1839551928
-2135227064
41523672
// And more strange numbers

У меня 2 вопроса:

1) Чтоэти числа означают?

2) Как функция main знает, как вызвать функцию sum без ее объявления?

Ответы [ 5 ]

3 голосов
/ 16 марта 2019

Я предполагаю, что код в вашем вопросе - это код, который вы на самом деле компилируете и запускаете:

int main() {
  int a = sum(1, 3);

  return 0;
}

int sum(int a, int b, int c) {
  printf("%d\n", c);

  return a + b + c;
}

Звонок на printf недействителен, поскольку у вас нет требуемого #include <stdio.h>. Но это не то, о чем вы спрашиваете, поэтому мы проигнорируем это. Вопрос был отредактирован для добавления директивы include.

В стандарте C, начиная со стандарта 1999 года, вызов функции (sum в этом случае) без видимого объявления является нарушением ограничения . Это означает, что требуется диагностика (но соответствующий компилятор все еще может успешно скомпилировать программу, если он этого захочет). Наряду с синтаксическими ошибками, нарушения ограничений наиболее близки к тому, чтобы сказать, что что-то незаконно. (За исключением директив #error, которые должны привести к отклонению единицы перевода.)

До C99 у C было «неявное int» правило, которое означало, что если вы вызываете функцию без видимого объявления, то создается неявное объявление. Это объявление предназначено для функции с типом возвращаемого значения int и параметрами (повышенных) типов передаваемых вами аргументов. Ваш вызов sum(1, 3) создаст неявное объявление int sum(int, int) и сгенерирует вызов , как если бы функция была определена таким образом.

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

C99 (издание стандарта ISO C 1999 года) отбросило неявное правило int. Если вы компилируете свой код с помощью соответствующего компилятора C99 или новее, компилятор должен диагностировать ошибку для вызова sum(1, 3). Многие компиляторы для обратной совместимости со старым кодом выводят нефатальное предупреждение и генерируют код, который предполагает , определение соответствует неявному объявлению. Многие компиляторы по умолчанию не соответствуют друг другу и могут даже не выдавать предупреждение. (Кстати, если ваш компилятор напечатал сообщение об ошибке или предупреждение, это очень полезно, если вы включите его в свой вопрос.)

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

1 голос
/ 16 марта 2019

В строго стандартном C, если вы не объявляете функцию перед ее использованием, она принимает определенные типы аргументов по умолчанию для функции. Это основано на ранних версиях C с более слабой системой типов и сохраняется только дляобратная совместимость.Это не должно использоваться вообще.Я пропущу детали здесь, но в вашем случае предполагается, что сумма занимает 2 целых и возвращает целое число.

Вызов функции с неправильным количеством параметров, как вы делаете здесь, является неопределенным поведением.Когда вы вызываете sum, компилятор думает, что он принимает два целых числа, поэтому он передает ему два целых числа.Однако, когда функция действительно вызывается, она пытается прочитать еще одно целое число, c.Поскольку вы пропустили только 2 дюйма, пространство для c содержит случайную чушь, которую вы видите при распечатке.Обратите внимание, что он не должен делать это, так как это неопределенное поведение, он может делать все что угодно.Например, он мог бы дать значения для b & c.

Очевидно, что такое поведение сбивает с толку, и вам не следует полагаться на неопределенное поведение, поэтому вам лучше компилировать с более строгими настройками компилятора, чтобы эта программа нене компилировать.(Правильная версия объявила бы сумму выше основной.)

1 голос
/ 16 марта 2019

Это неопределенное поведение за 6.5.2.2 Вызовы функций , параграф 9 стандарта C :

ЕслиФункция определена с типом, который не совместим с типом (выражения), на который указывает выражение, обозначающее вызываемую функцию, поведение не определено.

Функции без прототипов разрешены в 6.5.2.2 Вызовы функций , абзац 6 :

Если выражение, обозначающее вызываемую функцию, имеет тип, который не включает в себя прототип, целочисленные продвижениявыполняются для каждого аргумента, а аргументы с типом float повышаются до двойного.Это называется продвижением аргументов по умолчанию.Если количество аргументов не равно количеству параметров, поведение не определено....

Обратите внимание: если переданные параметры не соответствуют ожидаемым аргументам, поведение не определено.

0 голосов
/ 16 марта 2019

Я не уверен на 100%, работает ли C именно так, но ваши вызовы функций работают как стек в памяти. Когда вы вызываете функцию, ваши аргументы помещаются в этот стек, поэтому, когда вы находитесь в функции, вы можете получить к ним доступ, выбрав меньше x позиций в памяти. Так: Вы звоните summ(1, 3) стек будет иметь 1, а сверху 3. при выполнении функции fuction он увидит последнюю позицию памяти для аргумента 1º (он восстанавливает 1), а затем позицию перед ней для аргумента 2º (восстановление 3), однако, существует аргумент 3º, поэтому он получает доступ к позиции и до этого тоже. Эта позиция не является положительной для вас и отличается при каждом ее запуске.

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

0 голосов
/ 16 марта 2019

1) Поскольку вы не указали значение параметра "c" при вызове функции "sum", его значение внутри функции не определено.Если вы объявите функцию перед main, ваша программа даже не скомпилируется, и вы получите ошибку «error: слишком мало аргументов для вызова функции».

2) Обычно это не так.Функция должна быть объявлена ​​перед вызовом, чтобы компилятор мог проверить сигнатуру функции.Оптимизация компилятора решила эту проблему для вас в этом случае.

...