Powiedzmy, że mam dwie (lub więcej) funkcje c func1() i func2(), obie wymagające zmiennej bufora int buff. Jeśli obie funkcje są przechowywane w oddzielnych plikach, func1.c i func2.c, Jak sprawić, aby buff był dostępny tylko dla func1() i func2(), a nie do procedury wywołującej (lub jakiejkolwiek innej procedury).
Oto przykładowa konfiguracja:

Plik func1.c:

/*func1.c*/
static int buff;

int *func1(int x)
{
    buff = x;
    return &buff;
}

Plik func2.c:

/*func2.c*/
static int buff;

int *func2(int x)
{
    buff = x;
    return &buff;
}

Nagłówek nagłówek.h:

/*header for func1.c and func2.c*/
//multiple inclusion guard not present.
int *func1(int);
int *func2(int);

Plik główny.c:

#include<stdio.h>
#include"header.h"

int main()
{
    int *ptr;

    ptr = func1(1);
    printf("&buff = %p , buff = %d\n", ptr, *ptr);
    ptr = func2(2);
    printf("&buff = %p , buff = %d\n", ptr, *ptr);

    return 0;
}

Zgodnie z oczekiwaniami dane wyjściowe pokazują różne lokalizacje pamięci dla wzmocnienia.

&buff = 0x55b8fd3f0034 , buff = 1
&buff = 0x55b8fd3f0038 , buff = 2

Ale potrzebuję tylko jednego wzmocnienia kopiowania, nie więcej.
Mógłbym oczywiście umieścić obie funkcje w tym samym pliku i zdefiniować buff jako static int, ale wtedy straciłbym możliwość oddzielnej kompilacji funkcji.
Jeśli umieszczę int buff w oddzielnym buff.c i zadeklaruję go extern w func1.c i func2.c , ale wtedy byłby łatwo dostępny dla procedury wywołującej (w tym przypadku main).


Zasadniczo muszę stworzyć bibliotekę funkcji, które działają na tym samym zewnętrznym obiekcie, do którego mają dostęp. Procedura wywołująca może nie potrzebować wszystkich funkcji, więc nie chcę umieszczać ich w jednym pliku i tworzyć nieużywanego kodu. Ale musi istnieć tylko jedna kopia obiektu.

Proszę o pomoc, jak mógłbym zrobić to samo, jeśli jest to osiągalne.

1
Siddharth Bhat 19 czerwiec 2021, 13:17

4 odpowiedzi

Najlepsza odpowiedź

Typowym podejściem do tego problemu jest nadanie zmiennej globalnej nazwy zaczynającej się od _.

Oznacza to, że w func1.c możesz napisać

int _mylib_buff;

A potem w func2.c oczywiście miałbyś

extern int _mylib_buff;

Oczywiście w tym przypadku _mylib_buff jest technicznie zwykłą zmienną globalną. To wcale nie jest naprawdę „prywatne”. Ale zmienne globalne zaczynające się od _ są prywatne „zgodnie z konwencją” i powiedziałbym, że w praktyce działa to dobrze. Ale oczywiście nic nie stoi na przeszkodzie, aby jakiś inny plik źródłowy mógł oszukać i zajrzeć do zmiennej nominalnie prywatnej, aw Standard C nie ma sposobu, aby temu zapobiec.

Inną komplikacją jest to, że niektóre identyfikatory zaczynające się od _ są zarezerwowane dla implementacji i nie powinieneś ich używać we własnym kodzie. (Oznacza to, że komponenty implementacji – takie jak kompilator C i biblioteka C – mają zmienne półglobalne, które próbują przed tobą ukryć, i zazwyczaj używają wiodącego _, aby to osiągnąć, również.) Jestem prawie pewien, że zasady mówią, że możesz zdefiniować zmienną globalną zaczynającą się od wiodącego podkreślenia, po którym następuje mała litera, ale zasady są nieco skomplikowane i nigdy nie pamiętam wszystkich niuansów. Zobacz pytania 1.9 i 1,29 na liście często zadawanych pytań C.

2
Steve Summit 19 czerwiec 2021, 14:07

Standard C nie zapewnia na to sposobu. Zwykle odbywa się to za pomocą funkcji kompilatorów i linkerów wykraczających poza standard C. Oto przykład korzystania z narzędzi programistycznych Apple na macOS. Aby uzyskać opcje odpowiednie dla twojego środowiska, powinieneś określić narzędzia do budowania i wersje, których używasz, na przykład czy używasz narzędzi Apple, narzędzi GNU, narzędzi Microsoft lub czegoś innego.

Z tym w a.c:

#include <stdio.h>

int x = 123;

void a(void)
{
    printf("In a.c, x is %d.\n", x);
}

A to w b.c:

#include <stdio.h>

extern int x;

void b(void)
{
    printf("In b.c, x is %d.\n", x);
}

Kompilujemy pliki źródłowe do modułów obiektowych:

clang -c a.c b.c

A następnie połącz je z nowym modułem obiektów r.o, jednocześnie żądając, aby symbol x (_x w widoku konsolidatora) nie był eksportowany:

ld -r -o r.o -unexported_symbol _x a.o b.o

Następnie, jeśli mamy inny plik źródłowy c.c, który próbuje użyć x:

#include <stdio.h>

extern int x;

extern void a(void);
extern void b(void);

int main(void)
{
    a();
    b();
    printf("In c.c, x is %d.\n", x);
}

Próba zbudowania pliku wykonywalnego za pomocą clang -o c c.c r.o daje:

Undefined symbols for architecture x86_64:
  "_x", referenced from:
      _main in c-139a35.o
ld: symbol(s) not found for architecture x86_64

Jeśli jednak usuniemy dwie linie w c.c, które odnoszą się do x, kompilacja się powiedzie i program wypisze:

In a.c, x is 123.
In b.c, x is 123.
3
Eric Postpischil 19 czerwiec 2021, 11:39

Odpowiedź brzmi: to niemożliwe.

C nie ma możliwości powiedzenia "ta zmienna może być używana przez plik źródłowy x, y, z, a nie przez inne pliki źródłowe".

Więc jeśli chcesz, aby buff był "prywatny" dla wielu funkcji, będziesz musiał umieścić te funkcje w tym samym pliku źródłowym.

1
4386427 19 czerwiec 2021, 11:00

Musisz zdefiniować zmienną niestatyczną w jednym z plików, na przykład:

int buff;

int *func1(int x)
{
    buff = x;
    return &buff;
}

W pliku nagłówkowym zadeklaruj go jako extern:

/*header for func1.c and func2.c*/
//multiple inclusion guard not present.

extern int buff;

int *func1(int);
int *func2(int);

Uwzględnij go we wszystkich innych plikach:

/*func2.c*/
#include "header.h"

int *func1(int x)
{
    buff = x;
    return &buff;
}

Jeśli nie chcesz, aby zmienna była widoczna, musisz stworzyć funkcję, która pobierze i ustawi zmienną "ukrytą".

typedef enum
{
    GET,
    SET,
    REF,
}OP_t;


#define CREATE(type, name) type getset##name(OP_t oper, type val, type **ref) \
{\
    static type buff;\
    switch(oper)\
    {\
        case GET:\
            return buff;\
        case SET:\
            buff = val;\
            break;\
        case REF:\
            if(ref) *ref = &buff;\
            break;\
    }\
    return 0;\
}\

#define HEAD(type, name) type getset##name(OP_t oper, type val, type **ref)
#define GETVAL(name) getset##name(GET, 0, NULL)
#define SETVAL(name,val) getset##name(SET, val, NULL)
#define GETREF(name,ref) getset##name(REF, 0, ref)
0
0___________ 19 czerwiec 2021, 11:02