Я освежал свои шаблоны проектирования на C здесь, и пока пытался запустить этот пример дизайна метода объекта, который у них на сайте:
#include <stdio.h>
// Define a structure to represent an object
typedef struct {
// Data members
int value;
// Function pointer for a method
void (*print)(const struct MyObject *);
} MyObject;
// Method to print the value of the object
void printValue(const MyObject *obj) {
printf("Object value: %d\n", obj->value);
}
// Function to initialize an object
void initializeObject(MyObject *obj, int initialValue) {
obj->value = initialValue;
obj->print = &printValue; // Assign the function pointer
}
int main() {
// Create an instance of MyObject
MyObject myObj;
// Initialize the object
initializeObject(&myObj, 42);
// Use the object's method
myObj.print(&myObj);
return 0;
}
Я получаю эти ошибки:
main.c:9:30: warning: declaration of 'struct MyObject' will not be visible outside of this function [-Wvisibility]
void (*print)(const struct MyObject *);
^
main.c:20:14: error: incompatible function pointer types assigning to 'void (*)(const struct MyObject *)' from 'void (*)(const MyObject *)' [-Wincompatible-function-pointer-types]
obj->print = &printValue; // Assign the function pointer
^ ~~~~~~~~~~~
main.c:31:15: warning: incompatible pointer types passing 'MyObject *' to parameter of type 'const struct MyObject *' [-Wincompatible-pointer-types]
myObj.print(&myObj);
^~~~~~
2 warnings and 1 error generated.
Для меня это имеет смысл, поскольку структура MyObject не имеет необходимой видимости. Он отлично работает, когда я меняю его на:
typedef struct MyObject {
// Data members
int value;
// Function pointer for a method
void (*print)(const struct MyObject *);
} MyObject;
Мне просто интересно, почему он компилируется во всех онлайн-IDE
Редактировать:
Он компилируется в примере, приведенном в ссылке:
А также эти: туториалпойнт
Это были первые три, которые я попробовал, но я вижу, что некоторые сейчас выдают одни и те же ошибки.
В C вы ссылаетесь на две разные структуры: анонимную структуру с псевдонимом typedef MyObject
и совершенно отдельный тип структуры с тегом MyObject
, который вы никогда не определяли. Это не должно компилироваться. Вы уверены, что используете онлайн-компилятор C, а не C++? В C пространство имен тега структуры отличается от глобального пространства имен.
Пожалуйста, измените вопрос и предоставьте хотя бы один экземпляр онлайн-IDE, в которой он компилируется.
Он компилируется в примере, приведенном в ссылке: geeksforgeeks А также эти: tutorialspoint codechef Это были первые три, которые я пробовал, но я вижу, что некоторые сейчас выдают одни и те же ошибки.
Вы хотите отредактировать свой вопрос, добавив дополнительные данные, вместо того, чтобы отвечать комментарием.
«Почему он компилируется во всех онлайн-IDE» явно ложно. Вы не пробовали их все, и @3CxEZiVlQ предоставил вам противоположный пример. Выше вы показываете одну ошибку, присвоение несовместимых типов и два предупреждения. Сообщение об ошибке совершенно ясно показывает, что obj->print
и &printValue
— совершенно не связанные типы указателей. Это может работать и действительно работает в данном случае, но эти онлайн-IDE должны генерировать ошибку.
Вы можете заставить это скомпилироваться без каких-либо ошибок и предупреждений с помощью предварительного объявления и пары приведений. Это связано с тем, что разыменование происходит объявленного типа и struct MyObject *
является сквозным.
#include <stdio.h>
struct MyObject;
typedef struct {
int value;
void (*print)(const struct MyObject *);
} MyObject;
void printValue(const MyObject *obj) {
printf("Object value: %d\n", obj->value);
}
void initializeObject(MyObject *obj, int initialValue) {
obj->value = initialValue;
obj->print = (void (*)(const struct MyObject *)) &printValue;
}
int main() {
MyObject myObj;
initializeObject(&myObj, 42);
myObj.print((struct MyObject *) &myObj);
}
Было бы более понятно использовать const void *
вместо const MyObject void *
, и теперь вам нужно только выполнить приведение перед разыменованием:
#include <stdio.h>
typedef struct {
int value;
void (*print)(const void *);
} MyObject;
void printValue(const void *obj) {
printf("Object value: %d\n", ((MyObject *) obj)->value);
}
void initializeObject(MyObject *obj, int initialValue) {
obj->value = initialValue;
obj->print = &printValue;
}
int main() {
MyObject myObj;
initializeObject(&myObj, 42);
myObj.print(&myObj);
}
Конечно, лучший вариант — объявить struct MyObject
, как вы указали.
В этом объявлении typedef
typedef struct {
// Data members
int value;
// Function pointer for a method
void (*print)(const struct MyObject *);
} MyObject;
в этой строке в объявлении безымянной структуры введены два разных спецификатора типа: спецификатор типа безымянной структуры с псевдонимом MyObject
и спецификатор неполного типа структуры struct MyObject
.
void (*print)(const struct MyObject *);
^^^^^^^^^^^^^^^
Более того, областью действия этого спецификатора типа struct MyObject
является область действия прототипа функции. То есть это объявление не видно за пределами списка параметров функции.
Эти типы несовместимы.
Чтобы избежать ошибок, просто измените объявление следующим образом:
typedef struct MyObject{
// Data members
int value;
// Function pointer for a method
void (*print)(const struct MyObject *);
} MyObject;
Обратите внимание, что этот оператор присваивания
obj->print = &printValue; // Assign the function pointer
также может быть переписано как
obj->print = printValue; // Assign the function pointer
поскольку обозначения функций, используемые в выражениях, неявно преобразуются в указатели на них.
Что касается компиляторов, то реализация определяет, будут ли компиляторы выводить предупреждения как ошибки, и это зависит от опций компилятора.
почему он компилируется во всех онлайн-IDE. Это неправда: godbolt.org/z/nevzWbohW