2014-08-12 2 views
0

вот пример кода, который будет напоминать вопрос я сC порядок объявления функций

#include <stdio.h> 

    int cube_then_square(int x){ 
     x = cube(x); 
     return x*x; 
    } 

    int cube(int y){ 
     return y*y*y; 
    } 

    int main(int argc, char *argv[]){ 
     printf("5 cubed then squared is: %d\n", cube_then_square(5)); 
     return 0; 
    } 

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

+0

Это c или C++? –

+3

Речь идет не о порядке размещения функций в памяти (что в основном связано с компоновщиком). Речь идет о однопроходном компиляторе, который видит объявление функции перед вызовом этой функции. C++ в некоторой степени нарушает предположение «одного прохода», но только в некоторой степени. –

+0

@chris no им не обязательно, он был объявлен, когда он был вызван (если это C) –

ответ

1

Компилятор читает файл сверху вниз. Когда он достигает функции, он проверяет, знает ли он это. В этом случае он не видел функцию cube(int), поэтому он возвращает ошибку.

Вы можете сделать две вещи: 1. Вы перемещаете функцию cube перед функцией cube_then_square. 2. Вы создаете вперед декларацию до cube_then_square:

int cube(int y); 
+0

имеет смысл , Спасибо! – user96454

1

К верхней части файла, под включает в себя, добавьте int cube(int y);

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

0

Чтобы устранить эту проблему, вы можете объявить cube перед использованием:

#include <stdio.h> 

int cube(int y){ 
    return y*y*y; 
} 

int cube_then_square(int x){ 
    x = cube(x); 
    return x*x; 
} 

int main(int argc, char *argv[]){ 
    printf("5 cubed then squared is: %d\n", cube_then_square(5)); 
    return 0; 
} 

Вы также можете добавить функцию заголовка int cube(int y); на вершине:

#include <stdio.h> 

int cube(int y); 

int cube_then_square(int x){ 
    x = cube(x); 
    return x*x; 
} 

int cube(int y){ 
    return y*y*y; 
} 

int main(int argc, char *argv[]){ 
    printf("5 cubed then squared is: %d\n", cube_then_square(5)); 
    return 0; 
} 
+0

Решает проблему, проверьте. Забытая проблема была примером, проверьте. Не касается вопросов, проверьте. Просто добавьте немного юмора, помните, чтобы не идти слишком быстро! –

3

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

Старый C89 разрешен для неявного объявления всего. Когда вы вызываете функцию, она становится неявным объявлением как int func(). Это работает в этой ситуации, потому что вы неявно объявить функцию int cube() с линией:

x = cube(x); 

, а позже вы определяете функцию int cube(int). int cube() и int cube(int) имеют совместимые типы, поэтому это прекрасный вызов.

Проблема, с которой вы можете столкнуться, - это вызов несовместимой функции из неявно объявленной функции (это действительно почему предупреждение существует). int cube(float) - это несовместимый тип функции, который определенно может существовать, и если вы назвали его с неявно объявленной функцией, вы, вероятно, могли бы ожидать некоторые довольно странные эффекты (прочитайте undefined). Как указано в mafso, строгий C99 больше не допускает неявно объявленных функций, поэтому многие компиляторы содержат предупреждение независимо.

Помните, что объявленная функция неявно объявлена ​​BAD PRACTICE, но вы должны знать о существовании для таких сценариев.

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

#include <stdio.h> 

cube1(int x){return x*x*x;} 
main(){float y = 9.; printf("%d\n%d\n", cube1(y), cube2(y));} 
cube2(int x){return x*x*x;} 

выход:

729 
1 

Эти функции идентичны в ассемблере

00000000004004dc <cube1>: 
    4004dc: 55      push %rbp 
    4004dd: 48 89 e5    mov %rsp,%rbp 
    4004e0: 89 7d fc    mov %edi,-0x4(%rbp) 
    4004e3: 8b 45 fc    mov -0x4(%rbp),%eax 
    4004e6: 0f af 45 fc    imul -0x4(%rbp),%eax 
    4004ea: 0f af 45 fc    imul -0x4(%rbp),%eax 
    4004ee: 5d      pop %rbp 
    4004ef: c3      retq 
0000000000400540 <cube2>: 
    400540: 55      push %rbp 
    400541: 48 89 e5    mov %rsp,%rbp 
    400544: 89 7d fc    mov %edi,-0x4(%rbp) 
    400547: 8b 45 fc    mov -0x4(%rbp),%eax 
    40054a: 0f af 45 fc    imul -0x4(%rbp),%eax 
    40054e: 0f af 45 fc    imul -0x4(%rbp),%eax 
    400552: 5d      pop %rbp 
    400553: c3      retq 

Но в callsite ожидаемый переход от поплавка до целого числа никогда не выполняется для неявного вызова.

0

Совершенно неуместно, как эти функции «помещаются в память». Фактически, это никоим образом не раскрывается на уровне языка. Это не имеет значения.

Когда вы организуете файл реализации, у вас есть выбор сделать это как сверху вниз, так и снизу вверх.

В первом случае функции верхнего уровня определены первыми. Функции верхнего уровня вызывают функции нижнего уровня, которые определены позже в файле. Чтобы это нормально работало, низкоуровневые функции должны быть дополнительно прототипированы до точки первого вызова (например, в самой верхней части файла). (Формально прототип не требуется, но требуется хотя бы заявление.)

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

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

Что у вас в вашем примере выглядит как сочетание этих двух подходов. main (функция наивысшего уровня) определяется внизу, но cube_then_square определяется до cube. В этом нет ничего плохого, но чтобы сохранить этот заказ, вы должны предоставить прототип для cube в начале файла.

Я лично предпочитаю подход снизу вверх. Например. в вашем конкретном примере я переместил бы все определение cube в начало файла. Это исключило бы необходимость предоставления дополнительного прототипа.

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

Смежные вопросы