Przypisanie wskaźnika string char do char array możliwe?

0

Witam mam dość poważny problem piszę aplikację i dane z bazy postgresql mogę otrzymać jedynie w postaci char*, jednak wysłać przez soket da się tylko char array. Wiadomo, że w można użyć strcpy i po sprawie ale co w przypadku jeśli dane są ogromne? Na przykład gdybyśmy mieli wysyłać jakiś duży obiekt, który ma ponad 1GB. Tutaj już widać że zaczynamy mieć problem.
Więc czy istnieje możliwość przypisania wskaźnika char* do char array i odczytu tego stringa za pomoca char array. Oczywiście w mojej kwestii pozostało by pilnowanie bym nie wyszedł poza długość, oraz bym nie nadpisywał elementów stringa. Czy jest możliwe takie zachowanie był bym bardzo wdzięczny za pomoc.

W skróconej formie można powiedzieć, że na odczycie danych po przypisaniu wskaźników by się skończyło. W jaki sposób mogę tego dokonać bez kopiowania znaków!?
char *x = "abc";
char y[4];
y=x;
printf("%c, %c, %c", y[0],y[1],y[2]);

0

To przypisanie jest błędne i bez sensu. Możesz zrobić tak:

char *x = "abc";
printf("%c, %c, %c", x[0],x[1],x[2]);

Ponieważ C/C++ traktuje tablice jak wskaźnik na pierwszy element tablicy (ale tablice != wskaźniki) więc kod *x jest równoważny x[0], a nawet *(i + 0), *(0 + i) i analogicznie kod 0[x] też jest poprawny.

0

strcpy()

0

Dobrze wiem, że takie przypisanie jest błędne. Jest również bez sensu gdy się nie zna powodu związanego z takim użyciem.
Jednak gdybym miał możliwość takiego przypisania bym mógł czytać z przykładowego obiektu y to zdecydowanie usprawnił bym działanie masowego servera, który aktualnie piszę.
Nie jestem jednak nawet pewien czy takie przypisanie dało by się w jakiś sposób napisać i tutaj właśnie po to zgłosiłem się po pomoc byście pomogli mi napisać takie przypisanie o ile jest ono możliwe. :) Dzięki wielkie jeśli komuś się uda.

0

Ale moje pytanie to po co skoro kompilator nie rozróżnia czy to jest tablica czy wskaźnik!

0

Hehe po co i po co bez sensu i nie bez sensu :P myślałem że to ja mam pytanie :D ale ok

Struct X
{
char a[10];
char b[20];
}

Struct Y
{
char a*;
char b*;
}

Funkcja send w sys/socket wyśle strukturę X jako 30B a strukturę Y jako 8B
Więc aby wysłać Strukturę Y prawidłowo należało by ją rzutować na X i dopiero to wysłać czyli coś takiego:
Y y;
X x = (X)y;
i powinniśmy wysłać x
ale w moim przypadku takie coś nie działa bo o dziwo co mnie do teraz zastanawia
wskaznikA1=x.a;
wskaznikA2=y->a;
wskaznikA1 nie rowna sie wskaznikA2 ! nie mam pojęcia dlaczego tak się dzieje będę wdzieczny jeśli ktoś mi powie dlaczego.
Skoro te adresy się u mnie nie zgadzały no to pomyślałem że wystarczy że stworzę obiekt x i przypiszę adresy y.a i y.b do x->a i x->b i wyślę x
taka próba może i was wszystkich dziwić ale dla masowej aplikacji serverowej, która ma wysyłać dużą ilość sporych stringow takie zachowanie było by dużym usprawnieniem zamiast bez sensu spowalniać server niepotrzebnym kopiowaniem, pytanie jak można osiągnąć taki rezultat
przecież char *a i char a[] jest przechowywany w pamięci tak samo.

0

Socket przecież przyjmuje jako argument wielkość danej struktury jaką ma wysłać. Nie możesz tego policzyć i wpisać tam odpowiedniej wartości?

0

Mógłbyś rozwinąć to co masz na myśli bo nie wiem czy dobrze rozumiem?
Jeśli chodzi o liczenie to nawet nie musiał bym tego liczyć bo te stringi miały by zawsze tę samą długość.
Miał bym mieć dane w takiej postaci:
Struct Y
{
char a*;
char b*;
}
i powiedzmy ze:
a ma 4B
b ma 10B

jak by to miało wyglądać przecież funkcja send wyśle tylko adresy tych wskazników wiec nie ma tutaj chyba nic do liczenia?

0

Nie nie nie!

X x;
Y y;
//skoro chcesz wysłać te napisy z tych struktur to najpierw je trzeba tam wrzucić:
strcpy(x.a, "xa");
strcpy(x.b, "xb");

y.a = new char[3];
y.b = new char[3];
strcpy(y.a, "xa");
strcpy(y.b, "xb");

//jak widać w przypadku struktury x całość jest w jednym ciągłym bloku pamięci,
//w przypadku struktury y w ciągłym bloku są tylko wskaźniki(na stosie), a same napisy mogą być wszędzie(na stercie).

//żeby wysłać x wystarczy coś w tym stylu:
send(&x, sizeof(x));

//ale żeby wysłać y już tak lekko nie ma:
send(y.a, strlen(y.a));
send(y.b, strlen(y.b));
//jeżeli już musisz to zrobić jednym wywołaniem send to bez kopiowania się nie obejdzie
unsigned char *buffor = new unsigned char[strlen(y.a) + strlen(y.b)];
memcpy(buffor, y.a, strlen(y.a));
memcpy(buffor + strlen(y.a), y.b, strlen(y.b));
send(buffor, strlen(y.a) + strlen(y.b));

//edit: jeżeli napisy zawsze są tej samej wielkości to użyj sposobu X, jak różnej długości to sposobu Y

0

Jak zaczynałem ten temat to chciałem wiedzieć czy możliwe jest takie przypisanie tych wskaźników i jeśli tak to w jaki sposób.
Takie przypisanie bym mógł wysłać taką strukturę za jednym sendem.
Jednak dochodzimy do tego, że takie coś czego chciałem dokonać nie uda się zrobić.
Dalej nie mogę wyjść z podziwu dlaczego nie mogę przypisać do char a[10] adresu char *a. przecież ich reprezentacja w pamięci jest (chyba?) taka sama...

To znaczy że wychodzi na to, że pozostaje mi albo do wywołać send'a tyle razy ile jest wskaźników albo kopiować te dane do char array...
Strasznie nie zadowala mnie takie rozwiązanie gdyby, ktoś wpadł jeszcze na jakieś rozwiązanie to był bym wdzięczny.

0

No nie mogę przeboleć tego, że coś co mam już w pamięci jako char* muszę jeszcze raz kopiować do pamięci...
Po prostu nie chce mi się wierzyć aż że w takim języku jak c nie można odpowiednio podstawić tych wskaźników (oczywiście nie naruszając praw dostępu do stałej, a jedynie ją czytając...).
Dlaczego takie przypisanie nie miało by być możliwe to by oznaczało, że te typy jednak się od siebie różnią w jakimś znaczącym stopniu na niższych poziomach... :/

0

Nie nie nie! Tablica to nie jest wskaźnik! Ich reprezentacja w pamięci jest całkowicie, i diametralnie inna. To są 2 zupełnie różne rzeczy.
Tablica jest to ciągły blok pamięci o rozmiarze sizeof(typ_tablicy) * rozmiar_tablicy.

0

Poza tym, że to co podałeś się nie skompiluje to:
Nie, nie wyśle adresów. Wpisujesz:

send(fp, (void*)your_struct, 14, NULL);

Tylko musisz być pewien, że a ma długość 4 a b 10, a w razie potrzeby niedomiar uzupełnić zerami.

0

Jeszcze przy okazji, jak wysyłasz struktury to ich definicje warto by było wrzucić między dyrektywy

#pragma pack(push, 1)
...
#pragma pack(pop)

(to w VC++, gcc też ma odpowiednik). Żeby nie wysyłać nadmiarowych danych(wyrównanie pól struktury)

0

ehh Panowie... co wy sobie myślicie, że jestem w trakcie pisania masowego serwera dla przynajmniej kilku tysięcy graczy i nie wiem czym się jak jest przechowywana tablica oraz czym jest wskaźnik... albo, że nie potrafię wysyłać struktur czy korzystać z sizeof troszeczkę powagi. Nie uważam się za jakiegoś znawcę tylko proszę o pomoc w konkretnej i bardzo nietypowej sprawie.

Nie wiem czy ciągle nie potraficie zrozumieć problemu przed jakim stoję czy co, że piszecie mi jak wysyłać struktury.
Spróbuję przedstawić problem jeszcze raz jak najbardziej jasno. Oczywiście w celu zrozumienia ta wersja którą przedstawię będzie dość uproszczona.
W związku z tym, że jest to uproszczona wersja nie powinniście raczej pytać po co i dlaczego w ten sposób ponieważ w rzeczywistym problemie to co chcę zrobić było by w pełni świadome i pomogło by mi w rozwiązaniu problemu przed którym stoję. Chyba, że nie da się rozwiązać tego tym sposobem wtedy pozostają tylko inne propozycje.

Więc tak mamy dwa duże stringi których na których nie chcemy wykonywać strcpy:
char *a;
char *b;

chcemy te stringi przesłać za jednym sendem (ze względu na to że tych stringów będzie dużo więcej)!
Wiadomo, że nie można użyć tutaj prostej struktury.
struct X
{
char *a;
char *b;
};
Wiadomo to bo zamiast stringów wyślą nam się tylko adresy pierwszych elementów;

Jeżeli jednak znamy długości tych stringów i wiemy że string a ma zawsze 4 znaki a b ma 10 znaków.
To mogli byśmy utworzyć drugą strukturę która przez kompilatora była by widoczna inaczej:
struct Y
{
char a[4]; // zapomnijmy o '\0'
char b[10]; // zapomnijmy o '\0'
};

Dla pewnej świadomości zróbmy:
X x;
x.a = "aaaa";
x.b = "0123456789";
(choć normalnie byśmy tego nie robili bo nasze stringi (char*) otrzymaliśmy już gotowe z bazy wiec to tylko przypisanie do przykładu)

mamy więc obiekt x i chcemy go wysłać wykonując tylko jeden raz funkcję send
jak już mówiłem nie możemy zrobić send((int)socket,&x,sizeof(x),0); bo wyślą się tylko adresy pierwszych elementów tych stringów, zresztą nawet sizeof nie będzie zgodny z oczekiwaniami.

Wiem, że poniższy zapis jest nieprawidłowy, bo na już na samą myśl o tym kompilator zaczyna krzyczeć, że nie można rzutować na char[].
chodzi jednak o to by zapisać coś takiego:
Y *y;
y->a = x.a;

w uproszczonej jeszcze formie mogło by ty być równie dobrze:
char *a = "cdef";
char b[4] = a;

Problem polega na oszukaniu komilatora w taki sposób by wziąć adres na literke 'c' co jest trywialne bo jest to po prostu a (i juz mamy adres).
i wpisać ten znaleziony adres w miejsce (char b[4] = "ghij";) adresu pod literką 'g' czyli po prostu w miejsce adresu b.

Próbowałem i też bez skutków:
((char*)b) = a;
i brakuje mi więcej pomysłów.

....
Zresztą po dość długich poszukiwaniach zaczynam wątpić w powodzenie tej misji...
pozostanie więc chyba strcpy lub wiele sendów no i ewentualnie przerobienie źródeł postgres tak by zwracały typ danych char array ale z tym pewnie było by masę pracy więc to jak już przy działającej wersji beta...

0
szymko napisał(a)

ehh Panowie... co wy sobie myślicie, że jestem w trakcie pisania masowego serwera dla przynajmniej kilku tysięcy graczy i nie wiem czym się jak jest przechowywana tablica oraz czym jest wskaźnik... albo, że nie potrafię wysyłać struktur czy korzystać z sizeof troszeczkę powagi. Nie uważam się za jakiegoś znawcę tylko proszę o pomoc w konkretnej i bardzo nietypowej sprawie.

Taka nietypowa to ona nie jest.

szymko napisał(a)

mamy więc obiekt x i chcemy go wysłać wykonując tylko jeden raz funkcję send
jak już mówiłem nie możemy zrobić send((int)socket,&x,sizeof(x),0); bo wyślą się tylko adresy pierwszych elementów tych stringów, zresztą nawet sizeof nie będzie zgodny z oczekiwaniami.

send()owi wisi co on wysyła. Dasz mu wskaźnik do początku bloku pamięci i jego rozmiar, to send() to wyśle. Nawet nie musi to być wskaźnik na char.

szymko napisał(a)

Problem polega na oszukaniu komilatora w taki sposób by wziąć adres na literke 'c' co jest trywialne bo jest to po prostu a (i juz mamy adres).
i wpisać ten znaleziony adres w miejsce (char b[4] = "ghij";) adresu pod literką 'g' czyli po prostu w miejsce adresu b.

Ale kombinujesz...

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


struct combined_char {
    char    a[4];
    char    b[11];
};


int  fake_send(int  fd, char  *data, int  size, int  flags) {
    char    *to_send;


    /* Do celów demonstracyjnych wykorzystujemy "size".
     * Nie jest to konieczne w przypadku fprintf(), gdyż
     * on działa na cstringach i jedzie póki nie odbije się
     * od bajtu zerowego */
    to_send = (char*)calloc(size, 1);
    memcpy(to_send, data, size);
    fprintf(stdout, "Combined string: %s\n", to_send);
    free(to_send);
    return size;
}


int  main(void) {
    char                    *a, *b;
    struct combined_char    cc;


    /* Symulacja wskaźnikow-wskaźnikow, żebyś się nie czepiał, że "przykład jest na tablicach" (chociaż nie ma to znaczenia) */
    a = (char*)calloc(4, 1);
    b = (char*)calloc(11, 1);

    strcpy(a, "aaa");
    strcpy(b, "0123456789");

    memcpy(cc.a, a, 4);
    memcpy(cc.b, b, 11);

    /* Ponieważ fprintf() jest wrażliwy na bajty zerowe, do celów demonstracji zamieniamy
     * jeden z nich na znak "_". Przy send() nie trzeba tego robić, bo on nie działa na cstringach
     * lecz na obszarach pamięci */
    cc.a[3] = '_';

    free(a);
    free(b);

    /* Udajemy send()a */
    fake_send(0, cc.a, 15, 0);

    return 0;
}

Wynik działania:

beorn@reki ~/tmp $ gcc -o test -Wall -pedantic test.c
beorn@reki ~/tmp $ ./test
Combined string: aaa_0123456789
0

Jeszcze jedno, bo wydaje mi się, że tego nie rozumiesz. Funkcja send() musi dostać ciągły blok pamięci. Jeśli masz dwa wskaźniki, to nie możesz polegać na tym, że będą wskazywały na następujące po sobie obszary, zatem kombinowanie z kopiowaniem wskaźników na końce tablic jest nieprawidłowe. Jeśli chcesz wysłać dane zapisane pod kilkoma wskaźnikami jednym send()em, powinieneś zaalokować bufor, do którego kopiujesz dane i przekazujesz go do send(). Wykorzystywanie struktury to jest rzeźba bez większego sensu.

0

mamy więc obiekt x i chcemy go wysłać wykonując tylko jeden raz funkcję send
jak już mówiłem nie możemy zrobić send((int)socket,&x,sizeof(x),0); bo wyślą się tylko adresy pierwszych elementów tych stringów, zresztą nawet sizeof nie będzie zgodny z oczekiwaniami.

NIE NIE NIE. Wyślą się stringi. jeśli jest zmienna, albo pole klasy

char napis[256];

to sizeof(napis) wynosi 256, a nie 4!

Bo deklaracja tablicy oznacza, że tu i w tym miejscu ma zostać zarezerwowane miejsce na n elementów tej tablicy. Nie że ma być tutaj wskaźnik.

Jest natomiast dozwolone użycie tablicy (bez żadnego indeksu) tam, gdzie spodziewany jest wskaźnik, np.

char napis[256];
char* wskaznik = napis;

i takie użycie oznacza „pobierz wskaźnik na początkowy element tablicy”. Powyższy zapis jest więc równoważny

char* wskaznik = &napis[0];

Zauważ też, że wskaźniki &napis i &napis[0] są liczbowo równe, ale pierwszy jest typu „wskaźnik na tablicę 256 charów” a drugi typu „wskaźnik na char”.

Wadą języka C jest, że wraz z przekazaniem tablicy poprzez wskaźnik na pierwszy element tracimy rozmiar tej tablicy. Dlatego potrzeba jest przesyłania rozmiaru jako osobnego parametru. Takiego problemu nie ma np. Pascal (Delphi).
w C++ jest na to (niezbyt ładne) obejście:

#include <stdio.h>

template <size_t N>
void pisz_rozmiar(char (&tablica)[N]) {
   printf("podano tablicę %d elementów\n",N);
}

int main() {
   char napis[256];
   pisz_rozmiar(napis);
}
0

Już rozumiem dlaczego takie zachowanie nie jest możliwe.
Dzięki i sory za mój upór.... :)

0

Myślę, że w Twoim przypadku problemem nie jest "jak wysłać kilka cstringów jednym send()em" lecz "dlaczego nie mogę użyć wielu wywołań send()". Wskazuje to na jakiś problem projektowy, lub błędną obsługę protokołów strumieniowych po drugiej stronie. Może powinieneś zmienić kierunek rozwiązywania problemu?

0

ja myślę że strach przed jednym dodatkowym kopiowaniem bufora w pamięci jest nieuzasadniony, bo bufor będzie jeszcze fafnaście razy skopiowany zanim wyjdzie drutem albo po eterze z komputera, do tego każde łącze będzie i tak wielokrotnie wolniejsze od pamięci RAM.

0

Przy danych o wielkości rzędu gigabajtów to ja też bym robił w portki ze strachu przed kopiowaniem... No może nie zupełnie, ale na pewno bolałyby mnie zęby przy pisaniu tego. Przy wysyłaniu w mniejszych paczkach, kopiowanie będzie też miało mniej do roboty (ale częściej) i zużycie pamięci mniejsze, więc może i na Sapera jeszcze starczy.

0

Błąd projektowy hmmm... nigdy nie wiadomo czy ktoś nie zrobił by za Ciebie czegoś lepiej :) ale bardzo dużo czasu poświęciłem na samo projektowanie i przemyślenie aplikacji i od tej strony myślę, że raczej wszystko jest w porządku. Danych będzie sporo jednak nie na tyle dużo by samo kopiowanie miało doprowadzić do ogromnej tragedii. Gdyby jednak nie ono to możliwe, że był bym wstanie obsłużyć około 10-15% graczy więcej co jak na masówkę dało by duży rezultat. Stąd też moje starania w tym a nie innym kierunku. Poświęciłem dzisiaj jakieś 1,5h na czytanie kodów biblioteki postgre i nie zbyt byłem zadowolony bo tam też widziałem, że dane są kopiowane. Wszystkiego jednak przecież sam nikt nie napisze, więc to chyba czas aby pogodzić się z losem i pisać dalej sumiennie kopiując dane...
Trochę namieszałem bo w sumie gdy ktoś napisał że send musi dostać spójny sektor danych do wysłania to trafiło do mnie, że przecież nawet przypisanie wskaźników nic tu nie da...
Wielkie dzięki.
Byliście mi bardzo pomocni
Pozdro!

0

Jeśli musisz używać tylko jednego send() w protokole strumieniowym, to jest to raczej błąd. Myślisz, że np. taki serwer FTP wypycha cały wielki plik jednym wywołaniem funkcji send()? A przy strumieniowaniu np. radia internetowego nie masz możliwości zbuforowania wszystkiego na raz, bo takie dane nie mają ani początku, ani końca :)
Wspominałeś, że chcesz przesłać dane o wielkości GB. Libpq alokuje Ci już pamięć na wynik, a ty chcesz to jeszcze zdublować. Przy pliku o wielkości 1GB cała operacja zajmie Ci 2GB RAMu przez czas potrzebny na wypchnięcie tego do klienta (co może trwać dłuuuugo). Jeśli w tym czasie podepnie Ci się drugi koleś, aplikacja zeżre Ci już 4GB RAMu. Zapomnij zatem o "obsłużeniu około 10-15% graczy więcej", bo 2-4 klientów zajedzie Ci serwer, jeśli w ogóle system umożliwi Ci zaalokowanie tyle RAMu (hint: limity).
Z bazy powinieneś ciągnąć dane partiami (libpq powinien coś takiego udostępniać do obsługi BLOBów) i wysyłać je też partiami. Jedynie w specyficznych przypadkach alokowanie dużej ilości pamięci (nawet setek MB) jest konieczne.
Na Twoim miejscu poprawiłbym kod po drugiej stronie, tak aby możliwe było przesłanie danych więcej niż jednym send(). W przypadku TCP i tak taka możliwość powinna być uwzględniona w kodzie sieciowym.

BTW, co to za gra, która po sieci przepycha GB danych? Trzymasz tekstury w hires na serwerze?

0

Może się mylę, bo z socketów korzystałem głównie przez wrappery mfc, ale w ogóle pomysł, że kiedykolwiek musi być jeden send jest chyba słaby. To, że wyślesz jednym sendem pakiet nie oznacza wcale, że po drugiej stronie jeden send go odbierze w całości (np. nieblokujące sockety). I vice versa, to że coś zostanie odebrane jednym receive nie znaczy, że zostało wysłane jednym send. Dane i tak są dzielone/łączone w pakiety przez system w celach optymalizacji wykorzystania łącza.

Podsumowując: możesz wysłać coś i 10 sendami, a z drugiej strony odebrać to jednym receive.

0

BTW, co to za gra, która po sieci przepycha GB danych? Trzymasz tekstury w hires na serwerze?
Takie rozmiary to powinny mieć NAJWYŻEJ główne update'y, a te i tak powinny iść po ftp, http czy jeszcze lepiej bittorrent – a do tych protokołów są gotowe biblioteki i należy ich używać.

1 użytkowników online, w tym zalogowanych: 0, gości: 1