Требуется объяснение с указателем - PullRequest
0 голосов
/ 23 октября 2011

Я новичок в отношении указателя. Может кто-нибудь объяснить мне разницу в выводе в следующем коде? В следующем коде я присваиваю некоторое значение 3D-указателю. Я напечатал их после назначения. Затем снова напечатал их в другом блоке. Как получается, что результат будет другим?

#include<stdio.h>
#define row 5
#define rw 3
#define col 10

char ***ptr,sh[10];
int i,j,k;

int main()
{
    ptr=(char *)malloc(row*sizeof(char *));

    for(i=0;i<row;i++)
    {
        *(ptr+i)=(char *)malloc(rw*sizeof(char *));
        printf("\t:\n");

        for(j=0;j<rw;j++)
        {
            *(*(ptr+i)+j)=(char *)malloc(col*sizeof(char *));

            if(i==0 && j==0)
            {       
                //  *(*(ptr+row)+rw)="kabul";
                **ptr="zapac";
            }
            else
            {
                sh[0]=i+48;
                sh[1]=',';
                sh[2]=j+48;
                sh[3]='\0';
                *(*(ptr+i)+j)=sh;
            }

            printf("\t%d%d = %s\n",i,j,ptr[i][j]);
        }
        printf("\n");
    }

    for(i=0;i<row;i++)
    {
        for(j=0;j<rw;j++)
        {
            printf("\t%d%d %s\n",i,j,ptr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

Вывод:

    :
    00 = zapac
    01 = 0,1
    02 = 0,2

    :
    10 = 1,0
    11 = 1,1
    12 = 1,2

    :
    20 = 2,0
    21 = 2,1
    22 = 2,2

    :
    30 = 3,0
    31 = 3,1
    32 = 3,2

    :
    40 = 4,0
    41 = 4,1
    42 = 4,2

Мой вопрос: почему следующий результат не согласуется с вышесказанным?

    00 zapac
    01 4,2
    02 4,2

    10 4,2
    11 4,2
    12 4,2

    20 4,2
    21 4,2
    22 4,2

    30 4,2
    31 4,2
    32 4,2

    40 4,2
    41 4,2
    42 4,2

Ответы [ 3 ]

0 голосов
/ 23 октября 2011

Причина, по которой вы получаете вывод, состоит в том, что ваш *(*(ptr+i)+j) = sh устанавливает все значения char * (кроме первого, который вы обработали специально) на одно и то же значение, которое является адресоммассив данных в sh.Существует только одна копия sh, одна копия данных массива;все ваши char * ы указывают на эту единственную копию.Когда вы распечатываете значения позже, вы просто печатаете одну и ту же копию данных снова и снова через разные указатели на нее - так что вы получаете все, что вы положили в sh в последний раз, когда вы изменяли его, а не то, что былов нем, когда вы устанавливаете указатель.

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

Итак ... если вы замените эту строку:

*(*(ptr+i)+j)=sh;

на что-то вроде:

*(*(ptr+i)+j)=malloc(sizeof(sh));
strcpy( *(*(ptr+i)+j), sh );

или

ptr[i][j] = calloc(1,sizeof(sh));
strcpy( ptr[i][j], sh );

вы получите то, что ожидали, потому что каждый char * будет указывать на новую копию строки, которая была в sh на момент копирования.Вы должны будете помнить, чтобы освободить их позже, вместе со всем остальным, что вы malloc() редактировали, хотя.

Большинство ваших malloc() строк также неверны, но с char *, char ** и char ***, вероятно, имеют одинаковый размер в вашей системе, все равно работает.

ptr=(char *)malloc(row*sizeof(char *));

должно быть

ptr = (char ***)malloc(row*sizeof(char **));

, поскольку вы выделяете массив char ** (указатель на указатель на char), доступ к которому осуществляется через char *** (указатель на указатель на указатель на char).Аналогично:

*(ptr+i)=(char *)malloc(rw*sizeof(char *));

должно быть

*(ptr+i)=(char **)malloc(rw*sizeof(char *));

или

ptr[i] = (char **)malloc(rw * sizeof(char *));

для выделения массива char * (указатель на char), доступ к которому осуществляется черезchar ** (указатель на указатель на char).

Преобразования перед вашими malloc() вызовами на самом деле являются необязательными - результат malloc() void * будет преобразован в любой тип указателя, который вы ему присвоили.Однако в этом случае наличие приведений должно было заставить ваш компилятор предупредить вас, что вы ошиблись в типах.

0 голосов
/ 23 октября 2011

@ Дмитрий прав в том, что вызвало симптом, который вы видите, но в вашем коде есть другие проблемы, некоторые серьезные, некоторые менее.

Вот модифицированная версия вашей программы.

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

#define ROW 5
#define RW 3
#define COL 10

int main(void)
{
    char ***ptr;
    char sh[10];
    ptr = malloc(ROW * sizeof *ptr);

    for (int i = 0; i < ROW; i++)
    {
        ptr[i] = malloc(RW * sizeof *(ptr[i]));
        printf("\t:\n");

        for (int j = 0; j < RW; j++)
        {
            ptr[i][j] = malloc(COL * sizeof *(ptr[i][j]));

            if (i==0 && j==0)
            {       
                //  *(*(ptr+ROW)+RW) = "kabul";
                ptr[i][j] = "zapac";
            }
            else
            {
                sh[0] = '0' + i;
                sh[1] = ',';
                sh[2] = '0' + j;
                sh[3] = '\0';
                // ptr[i][j] = sh;
                strcpy(ptr[i][j], sh);

            }

            // printf("\t%d%d = %s\n", i, j, ptr[i][j]);
            printf("\tptr[%d][%d] = \"%s\"\n", i, j, ptr[i][j]);
        }
        printf("\n");
    }

    for (int i = 0; i < ROW; i++)
    {
        for (int j = 0; j < RW; j++)
        {
            // printf("\t%d%d %s\n", i, j, ptr[i][j]);
            printf("\tptr[%d][%d] = \"%s\"\n", i, j, ptr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

Изменения, которые я сделал:

  1. Добавьте #include <stdlib.h>, чтобы получить объявление malloc(). Это не является обязательным (хотя компилятор может позволить вам избежать его пропуска). В отсутствие #include <stdlib.h> компилятор до C99 будет предполагать, что malloc() возвращает int; это приводит к неопределенному поведению. Это означает, что могут произойти произвольно плохие вещи - или это может сработать в некоторых системах.

  2. Следуйте обычному соглашению об использовании имен заглавных букв для макросов.

  3. Добавьте несколько пробелов (я считаю, что это делает код более разборчивым, особенно с пробелом после каждой запятой и вокруг большинства операторов).

  4. Сделать переменные локальными, а не глобальными.

  5. Объявите переменные управления циклами в соответствующих циклах. Это особенность C99, но ваш компилятор, вероятно, ее поддерживает. (Для gcc используйте gcc -std=c99.)

  6. Исправьте вызовы malloc(), чтобы вычислить размер от размера, на который указывает указатель.

  7. Используйте оператор индексации, а не явную арифметику указателя. В общем, x[y] означает *(x+y), поэтому, например, *(*(ptr+i)+j) можно записать просто как ptr[i][j].

  8. Используйте strcpy() для копирования строки, хранящейся в sh, в the string pointed to by ptr [i] [j] `, вместо простого копирования указателя. (Это исправляет проблему, которую Дмитрий уже нашел; вам, вероятно, следует принять его ответ, поскольку он упомянул об этом первым.)

  9. Измените вывод, полученный при вызовах printf, чтобы было легче увидеть, что происходит.

0 голосов
/ 23 октября 2011

Поскольку sh [10] определено глобально, и во время первого цикла вы печатаете текущие значения этого массива - содержимое этого массива не сохраняется - меняется на каждой итерации. Для второго цикла вы печатаете последнее значение, записанное во время первого цикла (т. Е. На последней итерации).

Вы бы лучше распределить на каждой итерации первого цикла:

       for(j=0;j<rw;j++)
       {
            *(*(ptr+i)+j)=(char *)malloc(col*sizeof(char *));
            char* psh = (char*)malloc(10*sizeof(char));
            if(i==0 && j==0)
            {       
               strcpy(psh, "zapac");
            }
            else
            {
                psh[0]=i+48;
                psh[1]=',';
                psh[2]=j+48;
                psh[3]='\0';
                *(*(ptr+i)+j)=psh;
            }

PS имейте в виду жестко запрограммированный размер из 10 символов, возможно, вам потребуются дополнительные проверки / утверждения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...