C char** Pointer

Aynen oyle.

Mesela char a[1025]'li ornekte veriyi yazdirmadan once char b[1025]'li baska bir fonksiyon cagirmayi deneyebilirsiniz. b’nin icerigi muhtemelen a’nin uzerine yazilacak ve ciktiya b yansiyacaktir.

Hayir, *output_pointer = &data[0]; satiri calismiyor. Fonksiyon dorduncu satirdan return ediyor.

Zaten yaptiginiz seyden bahsediyorum.

int sayi_al(int *cikti1, int *cikti2) {
    if (prng == NULL) { return 1; }
    if (prng->get_int == NULL) { return 2; }
    *cikti1 = prng->get_int();
    *cikti2 = prng->get_int();
    return 0;
}

Tam olarak çalışmamasının sebebi ne? Değişken türlerinin uyumsuzluğu mu?

O zaman dediklerinize göre değişkenin değerinin korunması için değişkeni static olarak mı belirtmeliyim?

Yukarida return ediliyor.

Lifetime’ini uzatmak icin, evet. Veya malloc.

Merhabalar, dediklerinize göre şöyle bir çözüm ürettim. Ancak malloc ile aldığım alanı nasıl serbest bırakabilirim? Ya da serbest bırakacaksam değişkeni scope dışında kullandıktan sonra mı serbest kılmalıyım?

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

int degeral(char** ptr){
    char* deger = (char *)malloc(20*sizeof(char));
    deger = "yazi";
    *ptr = &deger[0];

    return 0;

}

int main(void){

    char* yazi;
 
    degeral(&yazi);

    printf("\nYazi: %s", yazi);
}
  • malloc'un donus degerini cast etmeye (C’de) gerek yok.
  • sizeof(char) = 1

Ornekte deger'in degerini degistirmissiniz, malloc edilen yere isaret etmiyor.

yazi satirini strncpy(deger, "yazi", 20) gibi bir seyle degistirirseniz olur.

Evet, is bitince, mesela printf’ten sonra free(yazi) yapabilirsiniz.
Emin olmak icin malloc’tan donen pointer ile free’ye giden pointer’in degerlerini yazdirin. (%p)

Ana fonksiyonda da yaptığımız gibi allocate edilen değişkenini adresini http request fonksiyonuna gönderirsek değeri allocate edilen yere yazmış olur muyuz?

Merhaba aib;

Hangi fonksiyon için konuşuyoruz netleştirelim. Alıntıladğınız kısımda sanki ben fonksiyona girdi verdikten sonra görevini yapsa dahi main fonksiyonuneq print edilen değişkene değer yüklenmediğini kastediyorum.

O satırın çalışan versiyonunu kod olarak koydum. Bir üst satırında &data ya değer yüklenmemiş derseniz kabul ama bu satırda bir hata yok aslında.

Pointer’ın adresindeki veri nedir? Burada rastgele olan verilen adresin işaret ettiği veri.

Burada pointer to pointer yapmaktan bahsettim. Yaygın olarka pointer to pointer kullanmakla ne kadar sık karşılaşılıyor?Görece size göre sık. Yani doğrudan global bir işaretçi kullanmaktan biraz daha fazlası değil mi bu?

İki farklı dosyada elimizde C gibi bir dil varsa ve namespace anahtar kelimemiz yok ise (c++) extern olmadan nasıl tüm kodda nasıl görünür yapabiliriz değişkeni?

İnitialize edilmeyen değişken? ptr ise riskdir ama sorun değil. &a dan bahsediyorsak evet. İlgili api ayrılmış bir tamponu şart koşuyor ve bu tampona yüklüyor.

Siz bu değeri ptr ye aktardığınızda ptr initialize (yükleniyor) oluyor. Buradaki tek risk, main fonksiyonunda ptr yi fonksiyonu çağırmadan yazdırmak. Bunu dışında gözden kaçırdığım bir durum mu var?

Oy oy örneklerininin çalışan kodunu koydum. Bellek ayırırsanız kod çalışır durumda denenmişi var.

Aslında özetlersek. Siz kapsamlardan (scope) ve un initialized değişkenlerden bahsettiğinizde ben de biraz baktım koda.

Yani beni endişelendiren bu söylemininizdi.

Diye düşündüm.

Vaktim olunca koda baktım. Sorun sizin söylediğiniz gibi scope larla alakalı değil. Api çağrılarını yeniden yazıp, ilk koddaki mantığa sadık kalarak denediğimde işaretçilerde/pointerlarda bir sorun göremedim.

Ben microsoft referansındaki örnek ile new ile bellek ayırdım. Standart C de bu iş malloc ile de halledilebilir. C ve C++ için microsft derleyicileri mi gcc mi tartışmaya açık.C standardı hedefse gcc, ama zaten microsoft API “shit” i kullanıyorsak microsoft derleyicisi.

Hala konu anlaşılmadı yada tereddüt oluştu ise. Aynı kodu, aynı pointerlarla pure(saf) C kodu ile yazayım, açıkcası bir gcc derleyicisi kurmaya üşendim elimin altında linux kurulu bir alet yok ve windows a da kurmayı düşünmüyorum.

Yani lifecycle, scope hikaye, olay apinin yükleyeceği data buffer ı ayrılmamış.

Merhabalar, ne kadar temiz, doğru bir çözüm olur bilemiyorum ama şöyle bir çözüm ürettim ve argüman olarak gönderilen pointerdan istediğim değeri alabiliyorum. (gcc’de sıkıntısız derleyebiliyorum):

#include <stdio.h>
#include <windows.h>
#include <winhttp.h>
#include <stdlib.h>

int get_version_from_server(int timeout, BOOL SSLVerify, char** output_pointer){
    HINTERNET winhttp = WinHttpOpen(L"Updater", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);

    if(!winhttp){
        return GetLastError();
    }

    HINTERNET server_connection = WinHttpConnect(winhttp, L"domain", INTERNET_DEFAULT_PORT, 0);

    if(!server_connection){
        return GetLastError();
    }

    HINTERNET server_request = WinHttpOpenRequest(server_connection, L"GET", L"url", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);

    if(!server_request){
        return GetLastError();
    }

    BOOL set_timeout = WinHttpSetTimeouts(server_request, timeout*1000, timeout*1000, timeout*1000, timeout*1000);

    if(!set_timeout){
        return GetLastError();
    }


    BOOL request_response = WinHttpSendRequest(server_request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, NULL, 0, 0, 0);

    if(!request_response){
        return GetLastError();
    }

    request_response = WinHttpReceiveResponse(server_request, NULL);

    if(!request_response){
        return GetLastError();
    }

    unsigned long int data_size;

    BOOL get_available_data_size = WinHttpQueryDataAvailable(server_request, &data_size);

    if(!get_available_data_size){
        return GetLastError();
    }

    char tmp_data_buf[data_size+1];

    BOOL read_data = WinHttpReadData(server_request, &tmp_data_buf, data_size, &data_size);

    if(!read_data){
        return GetLastError();
    }

    char* data = (char*)malloc((data_size+1)*sizeof(char));

    memcpy(data, tmp_data_buf, data_size+1);

    *output_pointer = &data[0];

    printf("%s", data);

    return 0;

}

Ne yaptıysam allocate edilmiş char değişkenini fonksiyona parametre olarak gönderemedim, ben de bu yüzden ara bir değişken kullanmaya karar verdim. Bu kodun tek sıkıntısı bellekte fazle yer kaplayıp kaplamayacağı yönünde. Tavsiyelerinizi bekliyorum.

Bir tane gcc uyumlu derleyici kurunca detaylarına bakarız. char* değişkeninizi main fonksiyonun dışına yazıp global hale getirin, ardından header dosyasında extern olarak tanımlayın bu durumda çalışır diye hatırlıyorum. Kodlar o versiyona da bakarız vakit olursa.

malloc un bir ikiz kardeşi vardır. free()

işi bittikten sonra free() ile ilgili bellek bölgesini serbest bırakabilirsiniz.

Sizin dediğinizi de deneyeceğim.

Yok hocam benim kastettiğim sorun tmp_data_buf ve data değişkenin aynı anda memoryde saklanmasının bir sıkıntı oluşturup oluşturmayacağıydı:

Burada alttaki ptr değişkeni başka bir scope içinde, değeri üstteki pointer’ın adresi. Initialize edilmemiş bir pointer dereference edilmiyor ama en başta da söylediğiniz gibi dangling pointer var.

Merhabalar, fonksiyondan hem http isteğinin durum kodunu almak, hem windows api hatası hem de request datasını alabilmek için struct yapısını kullanmaya karar verdim. Bilgilerinizle çok yardımcı oldunuz, teşekkür ederim. Aşağıdaki kodda yanlış yaptığım bir şey varsa söylerseniz sevinirim.

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

struct response{
    int api_error;
    int status_code;
    char* data;
};

struct response foo(void){
    char buf[123] = "data";
   
    char* ptr = &buf[0];
    
    struct response response;
    
    response.status_code = 0;
    response.api_error = 12007;
    response.data = (char*)malloc(sizeof(ptr));
    memcpy(response.data, ptr, strlen(ptr)+1);
    
    return response;
}

int main(void){
    struct response result = foo();
    
    printf("%s", result.data);
    
    return 0;
}

Bu zorunlu bir çözüm değil, global değişkenler çok sevilmez ama içinden çıkamadığınızda hızlı çözüm için faydalıdır.

Tabi ki sıkıntı oluşturur, şöyle ki fonksiyon iki kopyayı bellekte tutar ama bunu windows bellek yönetimi halledecektir. Zaten, fonksiyon sonlandığında kendini kaldıracaktır. Ama sizin de önlem almanız gerekir.

Buna ayrıca değineceğim.

Kendime alıntılayım.

Baktım:

Özellikle char* version_data; yı NULL a bile eşitlemedim. Nereye dalacak görelim diye.

Pointer’ı deklare ettik ama bellekte yer ayırmadık. Sorun yok.

Sonra bu pointer ı fonksiyon çağrısına parametre olarak verdik ve yine deklare etmeye devam ettik, hala bellekte yar ayırmadık.

Peki nasıl çalışacak?

DevC++ Editörünü kullandım. Eski bir gcc sürümü ile birlikte gelir. Bununla ilgili ayrıntılara girmeyeceğim pragma ile .lib dosyası ekleyebilmek varken bana elle winhttp.lib ekletti ya neyse.

main.c

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

#include "server.h"

   

int main(void){
    char* version_data;
    
    get_version_from_server(3, TRUE, &version_data);

    printf("Version string: %s", version_data);
    
    return 0;
}

Şimd dönelim server.h dosyasına. Ben yine #include lar da düzelterek, ikisini aynı klasörde çalıştırdım. Yalnız sizin, hata takip kodlarını kaldırdım siz tekrar ekleyebilirsiniz. Değişkenler gözümün önünde dursun diye.

server.h

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

int get_version_from_server(int timeout, BOOL SSLVerify, char** output_pointer){
    HINTERNET winhttp = WinHttpOpen(L"Updater", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    HINTERNET server_connection = WinHttpConnect(winhttp, L"www.google.com", INTERNET_DEFAULT_PORT, 0);
    HINTERNET server_request = WinHttpOpenRequest(server_connection, L"GET", L"url", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
    BOOL set_timeout = WinHttpSetTimeouts(server_request, timeout*1000, timeout*1000, timeout*1000, timeout*1000);
    BOOL request_response = WinHttpSendRequest(server_request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, NULL, 0, 0, 0);
    request_response = WinHttpReceiveResponse(server_request, NULL);
    unsigned long int data_size;

    BOOL get_available_data_size = WinHttpQueryDataAvailable(server_request, &data_size);
 
    
    char data[data_size+1];
    
    *output_pointer =(char*)malloc((data_size+1)*sizeof(char));
 
	BOOL read_data = WinHttpReadData(server_request, &data, data_size, &data_size);
    
    strcpy(*output_pointer,data);
    
	//printf("%s", data);

    return 0;

}

Size baştan beri anlatmaya çalıştığım şuydu. Pointer to pointer yaparsanız, bellek ayırırken ve değerleri yüklerken bir şeyler gözden kaçırabilirsiniz.

Bu nedenle bu işi C++ daha kolay bir metodla yapar, new anahtarı ve delete anahtarı bunu hallederdi.

Ama gcc dediğinizde ve saf c kodu yazdığınızda malloc, realloc gibi bellek yönetim fonksiyonlarına başvurmanız gerekir.

Ama pointer to pointer lara malloc yaparken de dikkat etmek gerekiyor.

main fonksiyonunda pointer ı yüklemedik, sorun yok, yüklenmemiş pointer ı fonksiyondaki işaretçiye attık yine sorun yok.

Ama baştan beri söylediğim iki şey eksikti.
Birincisi, bellek ayırmadınız.

*output_pointer =(char)malloc((data_size+1)*sizeof(char));

İle belleği ayırdık.

Daha önceden de main fonksiyonu içinde bellek ayırabilirdik mesela 1 karakterlik. Sonra realloc ile yeniden yeni boyuta göre yeniden bellek tahsisi yapabilirdik.

Dönelim kodun patladığı iki yere;

Evet yüklenemiyor.

Yüklemenin ise basit bir yolu var.

strcpy(*output_pointer,data);

Ben de yükledim hepsi bu.

İkincisi

	BOOL read_data = WinHttpReadData(server_request, &data, data_size, &data_size);
    

Burada son parametrenin senkron veya asenkron haberleşmenize göre yeniden gözden geçirilmesi gerekebilir vakit ayırıp bakamadım.

Özetlersek, pointerlarınızda sorun yok, pointerlarınızı aldıktan sonra düzgün bellek ayırmanız gerekir, henüz ayrılmamış.

Üzerine de aldığınız veriyi pointer ın gösterdiği belleğe kopyalayamamışsınız.

Ayrıca print ettikten sonra bence free ile belleği serbest bırakın. Bunun yanısıra, api referansalarana bakıp, aldığınız api handle larını serbest bırakın. Yani kodun hala toparlanacak kısımları var. Malum gece bir yarısı bakabildim onlara dokunmadım.

O bu yeni filmmiş buna henüz bakmadım.

Verdiğim kodu incelerseniz, sorununuz çözüleceğini tahmin ediyorum. Kolay gelsin.

Dediklerinizle ilerledim ve şöyle bir şey yapabilmiştim:

int get_version_from_server(int timeout, BOOL SSLVerify, char** output_pointer){
    HINTERNET winhttp = WinHttpOpen(L"Updater", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);

    if(!winhttp){
        return GetLastError();
    }

    HINTERNET server_connection = WinHttpConnect(winhttp, L"domain", INTERNET_DEFAULT_PORT, 0);

    if(!server_connection){
        return GetLastError();
    }

    HINTERNET server_request = WinHttpOpenRequest(server_connection, L"GET", L"url", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);

    if(!server_request){
        return GetLastError();
    }

    BOOL set_timeout = WinHttpSetTimeouts(server_request, timeout*1000, timeout*1000, timeout*1000, timeout*1000);

    if(!set_timeout){
        return GetLastError();
    }


    BOOL request_response = WinHttpSendRequest(server_request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, NULL, 0, 0, 0);

    if(!request_response){
        return GetLastError();
    }

    request_response = WinHttpReceiveResponse(server_request, NULL);

    if(!request_response){
        return GetLastError();
    }

    unsigned long int data_size;

    BOOL get_available_data_size = WinHttpQueryDataAvailable(server_request, &data_size);

    if(!get_available_data_size){
        return GetLastError();
    }

    printf("A\n");

    char tmp_data_buf[data_size+1];

    BOOL read_data = WinHttpReadData(server_request, &tmp_data_buf, data_size, &data_size);

    if(!read_data){
        return GetLastError();
    }

    char* data = (char*)malloc((data_size+1)*sizeof(char));

    memcpy(data, tmp_data_buf, data_size+1);

    *output_pointer = &data[0];

    return 0;

}

Ancak gene de sonradan struct yapısı kullanmak daha mantıklı geldi:

server.h

#include <stdio.h>
#include <windows.h>
#include <winhttp.h>
#include <stdlib.h>
#include <string.h>
#include "types.h"


INTERNET_RESPONSE send_request(wchar_t* domain, wchar_t* url, int timeout, BOOL SSLVerify){
    INTERNET_RESPONSE return_request;

    HINTERNET winhttp = WinHttpOpen(L"Updater", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);

    if(!winhttp){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    HINTERNET server_connection = WinHttpConnect(winhttp, domain, INTERNET_DEFAULT_PORT, 0);

    if(!server_connection){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    HINTERNET server_request = WinHttpOpenRequest(server_connection, L"GET", url, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);

    if(!server_request){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    BOOL set_timeout = WinHttpSetTimeouts(server_request, timeout*1000, timeout*1000, timeout*1000, timeout*1000);

    if(!set_timeout){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }


    BOOL request_response = WinHttpSendRequest(server_request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, NULL, 0, 0, 0);

    if(!request_response){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    request_response = WinHttpReceiveResponse(server_request, NULL);

    if(!request_response){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    long unsigned int status_code = 0;
    long unsigned int status_code_size = sizeof(status_code);

    WinHttpQueryHeaders(server_request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &status_code, &status_code_size, WINHTTP_NO_HEADER_INDEX);

    unsigned long int data_size;    

    BOOL get_available_data_size = WinHttpQueryDataAvailable(server_request, &data_size);

    char data[data_size+1];
        
    BOOL read_data = WinHttpReadData(server_request, &data, data_size, &data_size);

    return_request.api_code = 0;
    return_request.server_status_code = status_code;
    return_request.data = (char*)malloc(sizeof(data+1));

    memcpy(return_request.data, data, data_size+1);

    return return_request;
}

Böyle yapınca da programı sıkıntısız çalıştırabildim ancak büyük sonuçlar döndüren requestlerde dönen veriyi eksik alıyorum. Şöyle:

main.c

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include "run/file.h"
#include "run/server.h"

int main(void){
    INTERNET_RESPONSE req = send_request(L"www.google.com", L"/", 60, TRUE);

    printf("Status code: %d\n", req.server_status_code);
    printf("Api error code: %d\n", req.api_code);
    printf("Data size is: %d\n", strlen(req.data));
    printf("Data is:\n///\n %s", req.data);

}

Mesela Google’a request attığımda;

böyle eksik bir sonuç alıyorum. Bunun aynısını internetten binary dosya indirirken de yaşıyorum. Öneriniz, tavsiyeniz nedir? İnternette do while ile yapılmış örnekler gördüm ancak daha deneyemedim.

Kısaca özetlemeye çalışayım.

Hangi yapıyı kullandığınızdan bağımsız olarak, ayırdığınız tampondan fazla veriyi bir seferde alamazsınız.

Bu tamponun bir sınırı vardır.

Ben şöyle düşünmüştüm. domain adında bir server’ınız var ve biz buradan version kontrolü yapıyoruz. Version farkı varsa dosyayı indiriyoruz.

Siz ise burada bir dosya varsa (binary dahil) doğrudan indiriyormuşsunuz gibi tasarlamışsınız kodunuzu.

Hani bellekte yer olsa dahi en fazla unsigned long int uzunluğu kadar byte(char için 8 bit kullanıldığını varsaydım.) veri alabilirsiniz buffer tanımınız gereği. Bunun üzerindeki bir boyutta veri çekerseniz bu şekilde kodun çalışmaması doğaldır.

Küçük bir tampon tanımlayıp,

WinHTTPReadData çağrısında,

data_size değeri sıfır olana kadar bu apiyi tekrar çağırmanız gerekir. Bunu ister while ister, for döngüsüyle isterseniz if koşulu ile kontrol ederek çağırmalısınız.

Her seferinde de de tamponu flush gibi bir komut ile sıfırlamanız ve yeniden yüklemeniz gerekir.

Peki buffer ile parça parça aldığınız bu verileri nasıl tutacaksınız?

Bana verdiğiniz kodlarda hep printf ile ekrana yazdırıyorsunuz.

meseala binary bir dosyada birden çok yerde /0 /n gibi /00 gibi karakter dizileriyle karşılaşabilirsiniz. Bunlara denk gelirse zaten printf yazmayı sonlandırır yani sonrasında veri bile olsa printf doğası gereği sıfır sonlandırımış bir dizi ile karşılaşırsa yazma işlemini durdurur.

Dönelim küçük bir tampon ile parça parça indirdiğimizi varsayalım, bunları bellekte birleştirdiğinizde mesela 3GB bir veri, devasa bir değişkene mi atayacaksınız?

Bence hem veriyi alıp aldıkça bir dosyaya yazdırmak basit bir çözüm olabilir.

Yada FTP kullanmayı düşünebilirsiniz.

FTP Sessions - Win32 apps | Microsoft Learn

FtpGetFileA function (wininet.h) - Win32 apps | Microsoft Learn

Yani aynı anda birden çok farklı konuda bir arada sorularınız. Ekrana yazdırlamayacak karakterleri olan yada içinde birden çok yerde sıfır ve null ile sonlandırılmış diziler içeren verilerle çalışırken ekrana yazamayabilirsiniz.

Bunun yanı sıra tamponlarınız/bufferlarınız sınırlıdır tek seferde tüm veriyi çekemezsiniz. Haberleşme türünüze göre senkron ve asenkron haberleşem faktörlerini bir kenara bıraktım.

Öncelikle çektiğiniz büyük miktarda veriyi nerede tutacağınıza karar vermelisiniz.

Öncelikle sorularınızın birkaçına cevap vereyim.

Binary dosyasını ve versiyon kontrolünü Github’tan yapıyorum.

GitHub’dan maksimum 60 mb boyutlarında zip dosyası indiriyorum. Bu yüzden pek sıkıntı olacağını sanmıyorum. O kadar devasa olmasa da allocate edilen değişkene bu yeni gelen verileri yazmak için değişkeni reallocate mi etmeliyim?

Request atarken gelen verinin boyutunu da kontrol ediyorum zaten. Denediğim kadarıyla tamponda maksimum 2000 karakter alabiliyorum.

Uygulama senkron çalışacak. İlk önce programın kurulu olup olmadığını kontrol edecek. Kuruluysa programın güncellemesini denetleyecek, kurulu değilse programı bilgisayara kuracak.

Bir tane do while döngüsü ile denemiştim, ancak data_size değişkeni veri gelmemesine her zaman sabit bir değer döndürüyordu. Bufferı mı data_size’ı mı sıfırlamalıyım?

Ben kod yapısı olarak zaten çok büyük olmadığı için tüm veriyi alıp ondan sonra dosyaya yazmayı düşünmüştüm.

Dinamik allocate edilmis char pointer’indan mi bahsediyoruz?

char *ptr = malloc(42);
func(ptr);

Allocate edilmis char degiskeni soyle:

char c = 'x';
func2(c);

Bu bahsedilen basarisiz denemelerin kaydi var mi? Kod uzerinden gidelim.

Bunun yerine neden dogrudan WinHttpReadData(data, data_size) yapmiyoruz?

Hayir, nasil bir sikinti vardi aklinizda?
Ama dedigim gibi, gerek de yok.

Oncelikle dedigim gibi cast kullanilmis.
Ikinci olarak sadece pointer boyu kadar yer ayriliyor. Sonra buna strlen(ptr)+1 kadar byte yaziliyor. strlen(ptr)+1 > sizeof(ptr) (tipik olarak 4 veya 8) ise buffer overflow olur.

Program kapanana kadar Windows bellek yonetimi devreye girmiyor. C bellek yonetimi tmp_data_buf’in free edilecegini garantiliyor. malloc ile alinan alan kaliyor/leak ediyor.

Burada fonksiyonun basarili olup olmadigina bakilmadan data_size kullanilmis.

Burada fonksiyonun sonucuna bakmiyoruz.

Burada bir pointer kadar yer ayiriyoruz? data_size+1 > sizeof(char *) (>~ 4 veya 8) ise sikinti olur.

Fonksiyon dokumentasyonu bu konuda ne diyor?
0 byte donene kadar loop’la diyordu sanki. @semtex de aynisini soyledi.

O bir yontem.
Bir kerede atiyorum 128 MiB allocate edip surekli icine okumak da baska bir yontem.

Cagrilan fonksiyonun limitasyonu olabilir.

Kodu gorelim mi?

Hic bir seyi sifirlamaya gerek yok, ReadData’nin yazdigini soyledigi seyleri okuyoruz zaten, eski degerleri kimsenin umrunda degil.

Mantikli.

Gerek olmadığını ben de biliyorum ancak kullandığım derleyiciden mi (gcc) yoksa derleyicinin başka bir özelliğinden mi değişken türü ile ilgili uyarı verdiği için bir türlü WinHttpReadData’ya allocate edilmiş değişkenin adresini gönderemedim.

char* data = (char *)malloc((data_size+1)*sizeof(char))

WinHttpReadData(server_request, data, data_size, &data_size) 

Bu da aynı şekilde, derleyici cast kullanılmayınca derleyici ya uyarı veriyor derlemeye devam ediyordu yahut direkt hata verip derlemeyi durduruyor diye cast kullanmıştım.

Büyük bir veri geldiği zaman (4gb gibi mesela) aynı anda iki farklı kopyası farklı adreslerde saklanacağı için scope bitene kadar performans sorunu olur diye düşünmüştüm.

Maalesef kodu sildim, bir şey deneyip burada paylaşırım.

bkz: Soru Sorarken Sıkça Düşülen Hatalar #13

Cast kullanmama sebebimiz tam olarak bu. Dogru header’lari include etmediysek veya C++ derliyorsak bu hatayi maskeliyor.

Daha beteri, bir tanesi stack’te tutuluyor. C standardinin disinda konusuyorum, fakat pratikte foo bar[baz] seklinde deklare ettigimiz seyler 1-2 MiB’in ustune cikamaz. C sanirim 65k eleman filan garantiliyordu minimum.

Merhabalar, kodu şu şekilde düzenledim ve şaşırtıcı şekilde bu sefer size geçen mesajımdaki söylediğim gibi hata almadım.

#include <stdio.h>
#include <windows.h>
#include <winhttp.h>
#include <stdlib.h>
#include <string.h>
#include "types.h"


INTERNET_RESPONSE send_request(wchar_t* domain, wchar_t* url, int timeout, BOOL SSLVerify){
    INTERNET_RESPONSE return_request;

    HINTERNET winhttp = WinHttpOpen(L"Updater", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);

    if(!winhttp){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    HINTERNET server_connection = WinHttpConnect(winhttp, domain, INTERNET_DEFAULT_PORT, 0);

    if(!server_connection){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    HINTERNET server_request = WinHttpOpenRequest(server_connection, L"GET", url, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);

    if(!server_request){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    BOOL set_timeout = WinHttpSetTimeouts(server_request, timeout*1000, timeout*1000, timeout*1000, timeout*1000);

    if(!set_timeout){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }


    BOOL request_response = WinHttpSendRequest(server_request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, NULL, 0, 0, 0);

    if(!request_response){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    request_response = WinHttpReceiveResponse(server_request, NULL);

    if(!request_response){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    long unsigned int status_code = 0;
    long unsigned int status_code_size = sizeof(status_code);

    BOOL query_status_code = WinHttpQueryHeaders(server_request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &status_code, &status_code_size, WINHTTP_NO_HEADER_INDEX);

    if(!query_status_code){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    unsigned long int data_size;   

    BOOL get_available_data_size = WinHttpQueryDataAvailable(server_request, &data_size);

    if(!get_available_data_size){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    return_request.data = malloc((data_size+1)*sizeof(char));
        
    BOOL read_data = WinHttpReadData(server_request, return_request.data, data_size, &data_size);

    //memcpy(return_request.data, data, data_size+1);

    if(!read_data){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    return_request.api_code = 0;
    return_request.server_status_code = status_code;

    return return_request;


}

Aynı şeyleri yapıp bu sefer hata yahut uyarısız derlemeyi nasıl başarabildim bilmiyorum ancak bu sefer kod sıkıntısız çalışıyor (Eksik veri vermesi dışında). Gelen tüm verileri çekmek için şöyle bir şey denedim:

#include <stdio.h>
#include <windows.h>
#include <winhttp.h>
#include <stdlib.h>
#include <string.h>
#include "types.h"


INTERNET_RESPONSE send_request(wchar_t* domain, wchar_t* url, int timeout, BOOL SSLVerify){
    INTERNET_RESPONSE return_request;

    HINTERNET winhttp = WinHttpOpen(L"Updater", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);

    if(!winhttp){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    HINTERNET server_connection = WinHttpConnect(winhttp, domain, INTERNET_DEFAULT_PORT, 0);

    if(!server_connection){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    HINTERNET server_request = WinHttpOpenRequest(server_connection, L"GET", url, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);

    if(!server_request){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    BOOL set_timeout = WinHttpSetTimeouts(server_request, timeout*1000, timeout*1000, timeout*1000, timeout*1000);

    if(!set_timeout){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }


    BOOL request_response = WinHttpSendRequest(server_request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, NULL, 0, 0, 0);

    if(!request_response){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    request_response = WinHttpReceiveResponse(server_request, NULL);

    if(!request_response){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    long unsigned int status_code = 0;
    long unsigned int status_code_size = sizeof(status_code);

    BOOL query_status_code = WinHttpQueryHeaders(server_request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &status_code, &status_code_size, WINHTTP_NO_HEADER_INDEX);

    if(!query_status_code){
        return_request.api_code = GetLastError();
        return_request.server_status_code = 0;
        return_request.data = "";
        return return_request;
    }

    unsigned long int data_size = 0;   

    return_request.data = malloc(0);

    printf("Allocated variable: %s\n",return_request.data);

    do{
        BOOL get_available_data_size = WinHttpQueryDataAvailable(server_request, &data_size);

        if(!get_available_data_size){
            return_request.api_code = GetLastError();
            return_request.server_status_code = 0;
            return_request.data = "";
            return return_request;
        }

        if(!data_size){
            break;
        }

        return_request.data = realloc(return_request.data, (strlen(return_request.data)+data_size+1)*sizeof(char));

        memset(&return_request.data[strlen(return_request.data)], 0, (data_size+1)*sizeof(char));
            
        BOOL read_data = WinHttpReadData(server_request, &return_request.data[strlen(return_request.data)], data_size, &data_size);

        //memcpy(return_request.data, data, data_size+1);

        if(!read_data){
            return_request.api_code = GetLastError();
            return_request.server_status_code = 0;
            return_request.data = "";
            return return_request;
        }

    }while(data_size > 0);

    return_request.api_code = 0;
    return_request.server_status_code = status_code;

    WinHttpCloseHandle(server_request);
    WinHttpCloseHandle(server_connection);
    WinHttpCloseHandle(winhttp);

    return return_request;
}

Bu kod sıkıntısız çalışıyor ancak tam olarak anlamadığım bir şekilde (Not: Fark ettiğim kadarıyla bir adres değeri olmalı) bir değer arrayın başına ekleniyor. O değerden nasıl kurtulabilirim? Yoksa tamamen yanlış bir şey mi yapıyorum?