Oppure

Loading
10/05/20 16:13
AldoBaldo
Ieri m'è balenata un'idea bislacca per poter disporre in qualche modo di stringhe dinamiche in C da usare anche come stringhe comuni. L'idea e' questa...

Appurato che una stringa allocata in memoria dinamica è individuata da un puntatore char*, e che quel puntatore arriva da malloc(), calloc() o realloc() sotto forma di void*, e che una stringa dinamica dovrebbe disporre sostanzialmente di un valore per la lunghezza corrente e uno per la capacità massima, aggiungendo un terzo valore per usarlo come "firma" del mio tipo di stringa particolare, mi son detto...

...perché non mettere i dati in questione PRIMA del puntatore ai char* veri e propri, con dei fattori di scostamento negativi per reperirli? in questo modo potrei evitare di creare un tipo dati sotto forma di struttura con i campi necessari, sostituendoli sostanzialmente con un unico array di char.

Con un disegnino, potrei rappresentare la situazione come si vede nell'allegato.

Avendo typedef char* dStr; sarebbe possibile usare direttamente le dStr come comuni stringhe con le funzioni standard del C, e fare cose tipo...

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

int main() {
    dStr ds;

    ds = dStr_CreateCopyCString( "Prima" );

    if( ds ) {
        int ok;

        printf( "\"%s\" (%u caratteri)\n", ds, dStr_GetLength(ds) );

        ok = dStr_AppendCString( &ds, " dopo" );

        if( ok )
            printf( "\"%s\" (%u caratteri)\n", ds, dStr_GetLength(ds) );

        ds = dStr_Destroy( ds );
    }

    ds = dStr_Create( 256 );

    if( ds ) {
        printf( "\"%s\" (%u caratteri)\n", ds, dStr_GetLength(ds) );

        strcpy( ds, "Prima" );
        strcat( ds, " dopo" );
        dStr_UpdateLength( ds );

        printf( "\"%s\" (%u caratteri)\n", ds, dStr_GetLength(ds) );

        ds = dStr_Destroy( ds );
    }

    return 0;
}
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
10/05/20 16:14
AldoBaldo
Il codice, con i commenti, è lunghetto, quindi per non "intasare" questa pagina lo inserisco come allegato.
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
10/05/20 19:27
Vuoi reinventare le BSTR di OLE COM di Microsoft?

Arrivato tardi ...

docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/…

Ultima modifica effettuata da 10/05/20 19:28
10/05/20 20:16
AldoBaldo
Non so neppure cosa siano, le BSTR, ad essere proprio sinceri. C'è una parentela? Vado a vedere.
OLE COM è una sigla che ho letto non so quante volte nella documentazione della quale dispongo, ma non sono mai riuscito a capire veramente in cosa consista. Visto che mi ci indirizzi, vedo anche quello, almeno per averne un'idea di massima.

Nel frattempo, ho trovato e sistemato un paio di schifezze (molto probabilmente introducendone altre, per buona misura). Ad esempio, finché non ho attivato la modalità "pedantic" del "warning", il compilatore accettava zitto zitto una cosa che non pensavo fosse "sconveniente", cioè "spostare" aritmeticamente dei void*. Se lo segna come "warning" vorrà dire che non è un errore, ma una possibile fonte di errori della quale non ho mai letto sui testi sui quali ho studiato (tanti anni fa). Comunque ho eliminato i warning spargendo qua e là cast come fossero concime, trasformando i puntatori in semplici valori numerici di tipo size_t.

Inoltre, ho trovato un errore grave che mandava tutto in palla nel momento in cui si cercava di aggiungere/inserire una stringa in se stessa. Non si trattava di un errore nel codice, ma proprio nella logica dell'operazione. Risolto con un semplice flag e un'assegnazione "strategica" in una sola funzione alla quale si appoggiano molte altre, ma mi era proprio sfuggito.

EDIT: Ho letto la pagina che descrive il concetto di massima di BSTR. Be', trovo lusinghiero che in Microsoft abbiano avuto un'idea simile a quella che è venuta in mente a me senza averne mai sentito parlare! Vuol dire che non è un'idea così disastrosa, no? Comunque ci sono delle differenze. Ad esempio, io non uso caratteri a due byte, ma a byte singolo. Inoltre aggiungo nell'intestazione altri due dati: un identificatore (che spero possa ridurre la possibilità di usare malamente quel tipo di stringa) e un dato che memorizza la capienza massima del buffer nel quale è contenuta la stringa. E ancora... nella pagina introduttiva non ho trovato cenno al fatto che quelle stringhe siano dinamiche, nè che possano essere usate "in parallelo" con string.h. Magari leggendo più a fondo salta fuori che han già fatto anche quello, chissà. Vado a vedere cos'è OLE COM.

EDIT 2: Bene, ho letto su stackoverflow una pagina descrittiva su OLE e su COM. Il concetto non mi era estraneo, ma non ho mai preso in considerazione l'idea di usare quel tipo di caratteristica, quindi non sapevo che rientrasse sotto quella denominazione. Certo è che se dovessi provare a metterci le mani avrei non pochi grattacapi, perché mi sa che dev'essere un materiale non proprio "lineare"...
Ultima modifica effettuata da AldoBaldo 10/05/20 20:39
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
10/05/20 20:30
AldoBaldo
Un altro esempio di uso di dStr. Non fa niente di utile, mi è servito per fare un po' di test. Ho scoperto il difetto grave quando ho tentato di inserire nel ciclo for dStr_Append(&s1,s1) -- copiare s1 in coda a se stessa faceva "esplodere" tutto per via di un bel pastrocchio con i puntatori in fase di riallocazione.

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

const char kStrErr[] = "Errore!\n";

void mostra( dStr s ) {
    if( s ) {
        printf( "Validita': %s\n", dStr_IsValid(s)?"si":"no" );
        printf( "Contenuto: \"%s\"\n", s );
        printf( "Lunghezza: %u\n", dStr_GetLength(s) );
        printf( "Capacita': %u\n\n", dStr_GetCapacity(s) );
    } else puts( "dStr NULL\n\n" );
}

int main() {
    dStr s1 = NULL;
    dStr s2 = NULL;

    // crea una stringa dinamica vuota,
    // con spazio utile per 40 caratteri
    // e un terminatore
    s1 = dStr_Create( 40 );

    // crea una seconda stringa dinamica,
    // con dimensioni adatte a contenere
    // esattamente la stringa C passata
    s2 = dStr_CreateCopyCStr( ", e poi un'altra" );

    if( s1 && s2 ) { // la creazione e' riuscita?
        int i, ok;

        mostra(s1);

        // copia una stringa C in s1
        // la capacita' di s1 resta 40
        ok = dStr_CopyCStr( &s1, "Una" );
        if( ok ) mostra(s1); else puts( kStrErr );

        // aggiunge per 5 volte una stessa stringa
        // C in coda ad s1; quando la lunghezza della
        // stringa risultante supera i 40 caratteri,
        // la capacita' della stringa dinamica viene
        // incrementata quanto basta
        for( i=0; i<3; ++i ) {
            if( dStr_Append(&s1,s2) ) mostra(s1);
            else { puts( kStrErr ); break; }
        }

        // senza modificare il testo contenuto,
        // aumenta la capacita' della stringa dinamica
        ok = dStr_Resize( &s1, 256 );
        if( ok ) mostra(s1); else puts( kStrErr );

        // usa direttamente s1 con strcat(), per
        // aggiungere tre puntini; si crea una
        // discrepanza tra la lunghezza del testo
        // contenuto in s1 e la lunghezza registrata
        // nel "campo" apposito
        strcat( s1, "..." );
        mostra( s1 );
        printf( "Il testo in s1 ha in realta' %u caratteri.\n\n", strlen(s1) );

        // "riallinea" la lunghezza del testo
        // contenuto in s1 e la lunghezza registrata
        // nel "campo" apposito
        ok = dStr_UpdateLength( s1 );
        if( ok ) mostra(s1); else puts( kStrErr );

        // elimina lo spazio disponibile in s1
        // per la parte che eccede la lunghezza del
        // testo contenuto
        ok = dStr_Minimize( &s1 );
        if( ok ) mostra(s1); else puts( kStrErr );
    } else puts( kStrErr ); // creazione fallita

    // se esistono, distrugge s1 e s2
    s1 = dStr_Destroy( s1 );
    s2 = dStr_Destroy( s2 );

    return 0;
}
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
10/05/20 21:21
Goblin
Come mi dispiace (rosiko) di non "masticare" il C (sono depresso :P )... è una discussione interessante, ma molto distante dal mio "sapere", cmq ho letto tutto, e seguo ...
G.
Ibis redibis non morieris in bello
11/05/20 5:00
Le BSTR sono Unicode perche' non ha senso oggi come oggi (a parte gli esercizietti per la didattica) usare stringhe che non lo siano. Ed erano Unicode tanto tempo fa. Sono praticamente le stringhe che internamente usava VB6.

Il prefisso con la lunghezza e' sufficiente, tutto il resto mi sembra proprio inutile. E' ovvio che l'allocazione e' dinamica come potrebbe essere diversamente?

Divertiti, io continuero' ad usare la SysAllocString e le BSTR anche se OLE COM sta per essere completamente soppiantato da .NET

Comunque se vuoi avere un'idea di cosa gia' esiste e si usa nel campo professionistico e da molto tempo

docs.microsoft.com/it-it/previous-versions/windows/desktop/automat/…

docs.microsoft.com/it-it/previous-versions/windows/desktop/automat/…

docs.microsoft.com/it-it/previous-versions/windows/desktop/automat/…
Ultima modifica effettuata da 11/05/20 5:48
11/05/20 7:31
AldoBaldo
Di essere professionale non mi interessa nulla, in questo campo, però sono curioso e senz'altro leggerò quel che mi hai indicato. Non ora, che sto lavorando. Grazie per i suggerimenti. Se riguardano caratteristiche datate, magari trovo le stesse informazioni anche sulla documentazione che ho sul mio disco rigido, il che sarebbe molto più comodo. In caso siano "avvicinabili" per il mio livello, magari finirò pure per usare quel che viene descritto.

Dopo aver letto, saprò (forse) farmi un'idea di quanto utili o inutili possano essere i due "campi" in più che ho messo nelle mie stringhette. Secondo me quello che riguarda la "capacità" proprio inutile non è, ma vedremo. Inoltre ho come la sensazione che le stringhe usate come BSTR non siano compatibili con le funzioni in string.h (è giusto una "sensazione", vedrò leggendo).

Una cosa so per certo: per i miei scopi i 256 caratteri dell'ASCII esteso sono più che sufficienti. Non che voglia snobbare le altre soluzioni, ma ognuno compra le scarpe in relazione alla misura dei propri piedi, non di quelli altrui. ;)
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.