Diskuze: Nechtěná změna hodnoty délky pole a prvních 4 bytů pole
V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.

Člen

Zobrazeno 17 zpráv z 17.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.
Nečteš dokumentaci. Je naprosto v pořádku že se hodnota proměnné přidané jako argument mění, neboť funkce vrací ukazatel na tuto proměnnou. Poslední parametr je totiž výstupní nikoli vstupní.
Jinak je zbytečné ukládat si velikost pole jehož rozměr znáš. Používej operátor preprocesoru sizeof.
Dále jsem si všiml že dost často nevytváříš vazby mezi velikostí pole a polem. Takovýto postup brzy povede k chybě v programu.
Stále mi nejsou moc jasné rozdíly v definici polí. Nicméně jsem problém vyřešil zadáním posledních dvou parametrů NULL, NULL (inspirovalo mě pár odkazů na použití funkce na Internetu) a přiřazení výsledku funkce do pole:
unsigned char* digest16 = NULL;
digest16 = HMAC(EVP_sha1(), (const char*)digest, 32, (const unsigned char*)PMKID_Salt, sizeof(PMKID_Salt), NULL, NULL);
Přiřazování hodnot, například:
unsigned char digest[32];
const char MAC_AP[] = "c0c1c04bfc68";
char* password = { (char*)("MTRTogrt") };
Takže inicializace řetězců. Nic na tom není.
První případ je pole pro uložení 32 znaků nebo řetězce o délce 31 znaků (ukončeno nul znakem). Pokud je globální, obsahuje nuly, pokud je statické, obsahuje nuly, pokud je lokální, obsahuje odpad.
Druhý případ je pole obsahující řetězec který nelze měnit, protože const. Délku si umí spočítat překladač, je rovná počtu znaků + 1 nul znak. Funguje to tak že se vytvoří řetězec v rabulce řetězců, vytvoří se pole a zkopíruješ se řetězec do tohoto pole. Lépe je konstantní řetězec vytvořit takto:
char* str ="retezec";
Takovýto zápis je rychlejší neboť se řetězec vytvoří pouze v tabulce řetězců, a ty držíš pouze ukazatel na řetězec. Opět takovýto řetězec nelze měnit.
Pokud chceš měnit řetězec, musíš jej deklarovat jako pole:
#define SIZE 10
char str[SIZE];
Nebo vytvořit dynamicky:
char *str = NULL;
A pak použít malloc() nebo calloc() a nakonec uvolnit pomocí free().
Nelze deklarovat pole takto:
char str[];
Chybí rozměr.
Dále když máš řetězec velké délky tak nechej délku na kompilátoru a nepočítej délku sám. Nikdy!
Když deklaruješ a inicializujes pole pro uložení řetězce takto:
char str[] = "test";
Tak str zabírá v paměti 5 bytů a lze uložit až 4 znaky.
Pokud takto:
char str[10] = "test";
tak str zabírá v paměti 10 bytů a lze uložit až 9 znaků. Ale lze změnit obsah třeba na "testuji" pomocí strcpy() protože se rám nový řetězec vejde. To u prvního případů nelze.
Nepřišel jsem na to jak rovnou převést string na pole:
test = random_string(8);
password = &test[0];
kde je definováno
string test;
char* password = NULL;
Je mnoho způsobů..
Máme-li řetězec:
string str = "muj retezec";
Pak pro konstantní řetězce:
const char* arr1 = str.c_str();
nebo
char* arr2 = &str[0];
Pro modifikovatelný řetězec:
char* arr3 = new char[str.length() + 1];
str.copy(arr3, str.length());
arr3[str.length()] = '\0';
Neví, jak to zadat na jednom řádku:
password = &random_string(8)[0];
Hlásí mi chybu:
C26815 Ukazatel je nepropojený, protože ukazuje na dočasnou instanci, která byla zničena
Jak chceš udržovat adresu objektu který si ani neuložíš?
Takto:
std::string str = random_string(8);
const char* password = str.c_str();
// const char* password = &str[0];
Nicméně toto není to co potřebuješ, to je určeno pro konstantní řetězce, ty potřebuješ pole modifikovat.
Navíc generování řetězců není správná cesta k dosažení toho čeho chceš dosáhnout. Je třeba postupovat systematicky, obsah pole měnit jasně daným způsobem. Tak jak fungují např. staré tachometry nebo mechanické elektroměry. Dále je třeba optimalizovat na rychlost. To znamená že jakákoli konverze nepřichází v úvahu. Zapomeň. Používej C-čkové řetězce a nepoužívej string z C++ které jsou sice pohodlné ale pomalé. Vytvářej si své vlastní funkce a nekopíruj kde co někdo napsal. Práci s polem budeš dělat prakticky pořád, takže dle toho vytvoř funkce. Zadávej co nejméně dat, vracej co nejméně dat. Neduplikuj. Ač je v 99% logické a správné tvořit funkce s parametry a vracet hodnoty, tak ty se musíš zamyslet a v některých případech to obejít. Podívej se např. na funkci random string(). Vrací řetězec, popř. referenci či ukazatel, má parametr. Takto tu funkci teď psát nebudeš. Funkce musí přebírat data zvenčí. Natahování argumentů a vrácení hodnot stojí procesorový čas, ty jej nemáš nazbyt.
Nakonec Ti doporučím zainvestovat více času do studia problematiky ukazatelů, budeš pak postupovat mnohem snáz a nebude to styl pokus omyl.
O rychlost mi samozřejmě jde. Zkouším upravit program z Internetu, který mi pod Linuxem & GPU běží rychlostí 2 bilióny hashí/s (SHA256) na Windows.
U funkce:
void hostRandomGen(unsigned long* x) {
*x ^= (*x << 21);
*x ^= (*x >> 35);
*x ^= (*x << 4);
}
Mi překladač hlásí: Upozornění #63-D shift count is too large
Volání funkce:
hostRandomGen(&rngSeed);
¨
Definice:
unsigned long rngSeed = timems();
V čem je problém ?
Shiftuješ s příliš vysokou hodnotou.
Pro generování náhodného čísla můžeš použít xorshift32.
uint32_t xorshift32(uint32_t* x) {
*x ^= *x << 13;
*x ^= *x >> 17;
*x ^= *x << 5;
return *x;
}
kde x je ukazatel na seed, kterému jeho počáteční hodnotu můžeš nastavit např. takto:
time_t t;
uint32_t seed;
time(&t);
seed = (uint32_t)(t);
Díky, nechal jsem původní kód. Zjistil jsem, že je v Linuxu
unsigned long
8 bytů, ve Win64 jen 4
byty. Přejmenoval jsem proto definice unsigned long
na
uint64_t
Mám také problém s úpravou funkce timems(), kterou jsme zde již řešili v příspěvku "Diskuze: Jak upravit program pro hledání hesla ?"
Udělal jsem si krátký testovací program a něco je tam špatně.
#include <iostream>
//Replacement of the Linux gettimeofday(&end, NULL); function and #include <sys/time.h>
#include <stdio.h>
#include <winsock2.h> // struct timeval
#include <windows.h> // timeGetTime()
typedef unsigned long DWORD;
int gettimeofday(struct timeval* tp, void* tzp);
long long timems(void);
int gettimeofday(struct timeval* tp, void* tzp) {
DWORD t;
t = timeGetTime();
tp->tv_sec = t / 1000;
tp->tv_usec = t % 1000;
return 0;
}
long long timems(void) {
struct timeval end;
gettimeofday(&end, NULL);
return end.tv_sec * 1000LL + end.tv_usec / 1000;
}
int main()
{
while (1) {
long long rngSeed = timems();
printf("%lld\n", rngSeed);
Sleep(2);
}
}
Hlásí mi to chybu:
LNK2019 Nerozpoznaný externí symbol __imp_timeGetTime odkazovaný ve funkci "int __cdecl gettimeofday(struct timeval *,void *)" (?gettimeofday@@YAHPEAUtimeval@@PEAX@Z) timems
To je chyba linkeru, dost pravděpodobně si v projektu nezastavil vše potřebné, jako cesty a soubory. V již zmiňované diskuzi máš celý program, který jsem ti poslal. Ve tvém nejnovějším programu Ti chybí následující řádek který já v programu mám.
#pragma comment( lib, "winmm.lib")
Zobrazeno 17 zpráv z 17.