Не знаю, как реализовать указатель на массив указателей на символ - PullRequest
0 голосов
/ 26 сентября 2018

Я не очень хорош с указателями.Я знаю достаточно, чтобы заставить работать массив указателей на char, как в первом примере ниже.Но я не хочу передавать весь массив указателей, потому что он занимает слишком много места в стеке.Я хотел бы передать единственный указатель на память, выделенную для массива указателей.Я понятия не имею, как это сделать.

Эта программа работает:

#include "pch.h"
#include "$StdHdr.h"
#include "TmpTstPtr1.h"

#define SRC_LIN_SIZ 150

int main(int ArgCnt, char * ArgVal[])
{
    char        InpFilPth[MAX_PATH + 1];
    FILE        * InpFilPtr;
    char        ** SrcArr;
    unsigned    Sub1;
    unsigned    SrcArrCnt = 0;

    strncpy_s(InpFilPth, "TmpTstPtr1.cpp", strlen("TmpTstPtr1.cpp"));
    fopen_s(&InpFilPtr, InpFilPth, "r");
    SrcArr = (char **)malloc(999999 * sizeof(char *));
    LodSrcArr(InpFilPtr, SrcArr, &SrcArrCnt);
    for (Sub1 = 0; Sub1 < SrcArrCnt; Sub1++) {
        printf("SrcArr[%d] = %s\n", Sub1, SrcArr[Sub1]);
    }
    fclose(InpFilPtr);

    return 0;
}

void LodSrcArr(FILE * InpFilPtr, char ** SrcArr, unsigned * SrcArrCnt)
{
    char        SrcLin[SRC_LIN_SIZ + 1];
    char        * GetStrPtr;


    GetStrPtr = GetStr(SrcLin, SRC_LIN_SIZ, InpFilPtr);
    while (GetStrPtr != NULL) {
        SrcArr[*SrcArrCnt] = (char *)malloc(SRC_LIN_SIZ + 1);
        //      CpySiz(SrcArr[*SrcArrCnt], strlen(SrcLin) + 1, SrcLin);
        errno = strncpy_s(SrcArr[*SrcArrCnt], SRC_LIN_SIZ + 1, SrcLin, strlen(SrcLin));
        (*SrcArrCnt)++;
        GetStrPtr = GetStr(SrcLin, SRC_LIN_SIZ, InpFilPtr);
    }
}


char * GetStr(char * Str, const int MaxChr, FILE * InpFilPtr)
{
    char        * InpRtnVal = NULL;
    unsigned    Sub1;

    //  Get string from input file.  Find the end of the string if something entered.
    InpRtnVal = fgets(Str, MaxChr + 1, InpFilPtr);
    if (InpRtnVal != NULL) {
        Sub1 = 0;
        while (Str[Sub1] != '\n' && Str[Sub1] != '\0') {
            Sub1++;
        }
        //  Replace newline with null.
        if (Str[Sub1] == '\n') {
            Str[Sub1] = '\0';
        }
    }
    return InpRtnVal;

Следующая программа даже близко не подходит:

#include "pch.h"
#include "$StdHdr.h"
#include "TmpTstPtr2.h"

#define SRC_LIN_SIZ 150

int main(int ArgCnt, char * ArgVal[])
{
    char        InpFilPth[MAX_PATH + 1];
    FILE        * InpFilPtr;
    char        ** SrcArr;
    unsigned    Sub1;
    unsigned    SrcArrCnt = 0;
    char        *** SrcArrPtr = NULL;

    strncpy_s(InpFilPth, "TmpTstPtr2.cpp", strlen("TmpTstPtr2.cpp"));
    fopen_s(&InpFilPtr, InpFilPth, "r");
    SrcArr = (char **)malloc(999999 * sizeof(char *));
    SrcArrPtr = &SrcArr;
    LodSrcArr(InpFilPtr, SrcArrPtr, &SrcArrCnt);
    SrcArrPtr = &SrcArr;
    for (Sub1 = 0; Sub1 < SrcArrCnt; Sub1++) {
//      printf("SrcArr[%d] = %s\n", Sub1, SrcArr[Sub1]);    // got "Exception thrown: read access violation. it was 0xCDCDCDCD."
        printf("SrcArr[%d] = %s\n", Sub1, **SrcArrPtr);     // get 75 lines of garbage
        (**SrcArrPtr) += sizeof(char *);
    }
    fclose(InpFilPtr);

    return 0;
}


void LodSrcArr(FILE * InpFilPtr, char *** SrcArrPtr, unsigned * SrcArrCnt)
{
    char        SrcLin[SRC_LIN_SIZ + 1];
    char        * GetStrPtr;


    GetStrPtr = GetStr(SrcLin, SRC_LIN_SIZ, InpFilPtr);
    //  while (GetStrPtr != NULL and *SrcArrCnt == 0) {
    while (GetStrPtr != NULL) {
        **SrcArrPtr = (char *)malloc(SRC_LIN_SIZ + 1);
        //      CpySiz(SrcArr[*SrcArrCnt], strlen(SrcLin) + 1, SrcLin);
        errno = strncpy_s(**SrcArrPtr, SRC_LIN_SIZ + 1, SrcLin, strlen(SrcLin));
        (**SrcArrPtr) += sizeof(char *);
        (*SrcArrCnt)++;
        GetStrPtr = GetStr(SrcLin, SRC_LIN_SIZ, InpFilPtr);
    }
}


char * GetStr(char * Str, const int MaxChr, FILE * InpFilPtr)
{
    char        * InpRtnVal = NULL;
    unsigned    Sub1;

    //  Get string from input file.  Find the end of the string if something entered.
    InpRtnVal = fgets(Str, MaxChr + 1, InpFilPtr);
    if (InpRtnVal != NULL) {
        Sub1 = 0;
        while (Str[Sub1] != '\n' && Str[Sub1] != '\0') {
            Sub1++;
        }
        //  Replace newline with null.
        if (Str[Sub1] == '\n') {
            Str[Sub1] = '\0';
        }
    }
    return InpRtnVal;
}

Как комментариискажем, когда я пытаюсь получить доступ к SrcArr через индекс, я получаю ошибку во время выполнения.Когда я пытаюсь получить доступ через указатель, я получаю мусор.Проблема может быть там, где я говорю SrcArrPtr = &SrcArr;.Я не знаю, насколько это важно, но напечатанный мусор на 4 символа короче с каждой последующей строкой.Как будто он на самом деле печатает сам массив указателей, а не строки, на которые они указывают.Я не знаю.

Причина, по которой я закодировал его, как указано выше, состоит в том, чтобы заставить программу компилироваться.Я никогда не пытался использовать 3 указателя раньше.Возможно ли то, что я пытаюсь сделать?Если так, может кто-нибудь показать мне, как?Объяснение того, как это работает, было бы неплохо, но не обязательно.(Я использую Visual Studio 2017, хотя не думаю, что это имеет значение.)

TIA.

Ответы [ 3 ]

0 голосов
/ 26 сентября 2018

Я хотел бы передать один указатель на память, выделенную для массива указателей.

Предположим, у вас есть несколько целых чисел в куче, например:

int *integers = (int*)malloc(4 * sizeof(int));

А теперь предположим, что у вас есть несколько указателей, также в куче:

int **pointers = (int**)malloc(4*sizeof(int*));

Теперь давайте назначим указатели на адреса целых чисел:

pointers[0] = &integers[0];
pointers[1] = &integers[1];
pointers[2] = &integers[2];
pointers[3] = &integers[3];

В этом примере pointers является указателем на массив указателей (в куче), указывающих на некоторые целые числа (также в куче).Вы можете свободно передавать pointers и использовать его в другой функции.

Или, если вы хотите, чтобы массив указателей находился в стеке:

int* pointers[4];
pointers[0] = &integers[0];
pointers[1] = &integers[1];
pointers[2] = &integers[2];
pointers[3] = &integers[3];
int **ppointer = pointers;

Теперь ppointer isтакже указатель, указывающий на массив указателей, которые указывают на некоторые целые числа в куче.Просто обратите внимание, что на этот раз эти указатели находятся в стеке, а не в куче.Поэтому, если вы вернетесь из этой функции, они выйдут из области видимости и вы больше не сможете к ним обращаться.

0 голосов
/ 26 сентября 2018

Вы действуете по неверному представлению.Ни C, ни C ++ не передают копию массива в функцию, и они не могут вернуть массив из функции.

За исключением случаев, когда это операнд оператора sizeof или унарный & или строковый литерал, используемый для инициализации массива символов в объявлении, выражение типа "N-элементный массив T "будет преобразован (" распад ") в выражение типа" указатель на T ", а значением выражения является адрес первого элемента.

Таким образом, если вы объявляете массив наподобие

char *ptrs[N];

и передаете его функции как

foo( ptrs );

, выражение ptrs неявно преобразуется из "N-элемент массива char * "to" указатель на char * ", и то, что foo фактически получает, это указатель на первый элемент массива - это фактически то же самое, что запись

foo( &ptrs[0] );

Прототип может быть записан в виде

void foo( char **ptrs )

или

void foo( char *ptrs[] )

или

void foo( char *ptrs[N] )

В объявлении параметра функции объявители массива "настраиваются" так, чтобыобъявления указателей - IOW, T a[N] и T a[] интерпретируются как означающие T *a.Это only true в объявлении параметра функции.

По стилю ...

В C приведение к malloc не нужно 1 , а в C89 оно может подавить полезную диагностику, если вызабудьте включить stdlib.h или иначе не иметь декларации для malloc (или calloc или realloc) в области видимости.В C99 и более поздних версиях вы получите диагностику отсутствия объявлений, но C89 по-прежнему допускает неявные объявления int, а приведение не позволит вам кричать на компилятор, потому что int и типы указателей несовместимы.Я упоминаю об этом, потому что поддержка MS C C в прошлом C89 немного неровная.

Чтобы минимизировать затраты на обслуживание, лучше избегать явного именования типов в вызове malloc.Вы можете переписать

SrcArr = (char **)malloc(999999 * sizeof(char *));

как

SrcArr = malloc( 999999 * sizeof *SrcArr ); // you sure you need that many elements??!

Поскольку SrcArr имеет тип char **, выражение *SrcArr имеет тип char *, поэтомуsizeof *SrcArr совпадает с sizeof (char **).Как правило, вызов malloc может быть записан

T *p = malloc( N * sizeof *p );

или

T *p;
...
p = malloc( N * sizeof *p );

То же самое верно для calloc и realloc.


Это не приведение в C ++, поскольку C ++ не допускает неявное преобразование из void * в другие типы указателей, но если вы 'переписывая C ++, вы не должны использовать malloc в любом случае .
0 голосов
/ 26 сентября 2018
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>

void foo(char* bar[10]) { // a real array
    for (int i = 0; i < 10; ++i) {
        bar[i] = calloc(2, 1);
        bar[i][0] = '0' + i;
    }
}

void xox(char **qux) { // pointer to some char-pointers on the heap
    for (int i = 0; i < 10; ++i) {
        qux[i] = calloc(2, 1);
        qux[i][0] = '0' + i;
    }
}

int main(void)
{
    char* bar[10]; // a "real" array
    foo(bar);

    for (size_t i = 0; i < 10; ++i)
        puts(bar[i]);
    putchar('\n');

    // cleanup:
    for (size_t i = 0; i < 10; ++i)
        free(bar[i]);

    // plan b:
    char **qux = calloc(10, sizeof(*qux));
    xox(qux);

    for (size_t i = 0; i < 10; ++i)
        puts(qux[i]);
    putchar('\n');

    // cleanup:
    for (size_t i = 0; i < 10; ++i)
        free(qux[i]);
    free(qux);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...