2017-02-04 25 views
1

Предполагая, что у меня есть следующий код:C - struct * cast во время выполнения?

struct str1 
{ 
    int common1; 
    char common2; 
    char *common3; 
    long int aaaaaaaa; 
} 

struct str2 
{ 
    char bbbb; 
    char *common3; 
    int common1; 
    char common2; 
} 

struct str3 
{ 
    char ccccccccc[200]; 
    int common1; 
    char common2; 
    int dddddddd; 
    int eeeeeeee; 
    char *common3; 
} 

void somefunc1(struct str1 var) 
{ 
    printf("%d %c %s", var.common1, var.common2, var.common3); 
} 

void somefunc2(struct str2 var) 
{ 
    printf("%d %c %s", var.common1, var.common2, var.common3); 
} 

void somefunc3(struct str3 var) 
{ 
    printf("%d %c %s", var.common1, var.common2, var.common3); 
} 

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

+0

, что это слишком плохо, вы исключили макросы. это лучшее решение здесь. –

+0

@ Jean-FrançoisFabre Я исключил их из-за того, что предположил, что они не полезны для решений времени исполнения.? –

+0

Вся разница между функциями заключается в смещениях членов внутри структур, поэтому код в 'somefunc2()' сильно отличается от кода в 'somefunc1()', а в 'somefunc3()' другой от обеих других функций. Если вы действительно хотите, чтобы одна из трех функций выполняла работу трех, вам нужно написать код, использующий макрос 'offsetof' от' ', а задействованные структуры данных будут намного больше, чем повторение minuscule в показанном коде , –

ответ

0

Похоже, я был неправ об ограничении использования препроцессора:

#include <stdio.h> 

struct str1 
{ 
    int common1; 
    char common2; 
    char *common3; 
    long int aaaaaaaa; 
}; 

struct str2 
{ 
    char bbbb; 
    char *common3; 
    int common1; 
    char common2; 
}; 

struct str3 
{ 
    char ccccccccc[200]; 
    int common1; 
    char common2; 
    int dddddddd; 
    int eeeeeeee; 
    char *common3; 
}; 

#define somefunc(var) \ 
    printf("%d %c %s\n", var.common1, var.common2, var.common3); 

int main() 
{ 
    struct str1 var1 = {1,'a',NULL, 4}; 
    struct str2 var2 = {'b',NULL,2,'b'}; 
    struct str3 var3; 
    var3.common1 = 3; 
    var3.common2 = 'c'; 
    var3.common3 = NULL; 


    somefunc(var1); 
    somefunc(var2); 
    somefunc(var3); 

} 

выход:

1 a (null) 
2 b (null) 
3 c (null) 
+0

Вы не сказали - 'Функциональные вызовы должны решаться во время выполнения'? Как это можно решить в «время выполнения»? – army007

+0

Это вместо проверки ввода пользователем внутри корпуса коммутатора и принятия решения о том, какую функцию вызывать. @ army007 –

4

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

3

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

struct base 
{ 
    int common1; 
    char common2; 
    char *common3; 
}; 

struct str1 
{ 
    struct base b; 
    long int aaaaaaaa; 
}; 

struct str2 
{ 
    struct base b; 
    char bbbb; 
}; 

struct str3 
{ 
    struct base b; 
    char ccccccccc[200]; 
    int dddddddd; 
    int eeeeeeee; 
}; 

Примечание: переменная struct base структуры должен быть первым членом каждой struct, в противном случае этот метод не будет работать.

Теперь объявите функцию, которая принимает указатель на struct base.

void somefunc(struct base* var) 
{ 
    printf("%d %c %s\n", var->common1, var->common2, var->common3); 
} 

Usages:

struct str1 s1 = { 1, 'a', "sfad"}; 
struct str2 s2 = { 2, 'b', "sdfazx"}; 
struct str3 s3 = { 3, 'c', "oiurotu"}; 

somefunc((struct base*) &s1); 
somefunc((struct base*) &s2); 
somefunc((struct base*) &s3); 
+0

Тип «struct base» отбрасывает три байта - отходы, которых во многих случаях можно избежать, разместив сначала указатель, затем int и затем char, а затем используя правило Common Initial Sequence (если «переменная», часть структуры содержит любые поля или массивы 8-битных или 16-битных типов, их можно поместить для «заполнения» слова, первый байт которого является общим2. – supercat

0

Могу ли я каким-то образом избежать дублирования кода и использовать единую обобщенную функцию?

Нет. Это звучит аналогично шаблону C++, который, конечно, не имеет и не имеет аналогов для, если вы не включили препроцессор. C11 имеет _Generic, но это приводит к некоторой перегрузке функции: по одной функции для каждого типа, с поддержкой препроцессора для вызова их одним именем.

вызовов функций должны быть приняты во время выполнения

В C и C++, функция вызова - путь контроля - определяются во время компиляции. Нет никакого решения времени исполнения.

Вся разница между функциями - это имена структур, а не их членов.

На самом деле ключевым отличием является расположение каждой структуры. Имена находятся в разных местах. Компилятор C преобразует имена в местоположения. После компиляции нет имен и типов, ничто не указывает, что местоположение было названо common1 или было частью структуры. Существует только ссылка на ячейку памяти. Вы должны правильно использовать язык, чтобы убедиться, что ссылка на выбранное вами местоположение.

поэтому макрос не имеет значения.

Препроцессор позволяет манипулировать именами до того, как компилятор преобразует их в числа. Если вы хотите что-либо сделать в C "по имени", макрос - единственная игра в городе.

+0

'В C и C++ вызов функции - путь управления - определяется во время компиляции. «Решение» не выполняется. Это верно для 'C'. Но в' C++ '' virtual' функция определяется во время выполнения в соответствии с фактическим типом объекта, на который указывает указателем – army007

+0

Да, но функция, которая должна быть вызвана на основе этого фактического типа, была определена во время компиляции.Вся ссылка была построена компилятором и не поддерживает * runtime *. Динамический внешний вид - это созданная иллюзия (для выгоды программиста и с его согласия), скрывая множество «искалеченных» имен за одной и отправляя их по типу аргументов. Очень отличается от таких языков, как Lisp, Smalltalk и Python, которые могут изменять привязки имен под программным управлением. –

+0

Я говорил о переопределении функций - производные классы, переопределяющие «виртуальные» функции, определенные в базовом классе, а не перегрузка функций. Если функция «virtual» вызывается через указатель базового класса, тогда фактическая функция, которая должна быть вызвана, определяется во время выполнения. У компилятора нет способа узнать, на какой объект указывается указатель во время компиляции. – army007

0

В C89, если две или более структуры начинаются с элементов совпадающих типов в порядке совпадения (они имеют общую начальную последовательность), и оба являются частью одного и того же объединения, любая часть Common Initial Sequence может быть проверена с использованием соответствующих именованных членов любого типа, разделяющих СНГ. В вашем примере не используются соответствующие типы, поэтому он не подходит, но если он будет использовать соответствующие типы в порядке совпадения, это будет. Насколько я могу судить, компиляторы C89 в 1990-х годах единогласно применяли тот же принцип с указателями на структуры (поэтому, если у структурных типов S1 и S2 есть СНГ, указатель любого типа можно было бы использовать для доступа к членам СНГ). Хотя в Стандарте явно не указано такое обращение, самым простым способом для компилятора обеспечить, чтобы правило применялось во всех случаях с участием профсоюзов, было заставить его применять во всех случаях также и указатели, и многие люди (вероятно, в том числе авторы Стандарта) ожидали, что составители, естественно, сделают это, явно или нет.

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

Если вы хотите использовать правило Common Initial Sequence, может быть необходимо использовать флаг -fno-strict-aliasing при использовании компиляторов, у которых есть один (даже если он не использует CIS, используя флаг, может обеспечить защиту от ошибок компилятора). Код, который использует aliasing, должен стремиться сделать его очевидным для компилятора (например, чтобы убедиться, что тип подходящего профсоюза виден), но до тех пор, пока разработчики компилятора не начнут обращать внимание на такие вещи, -fno-strict-aliasing будет необходим для их неспособности сделать это.