требуется объяснение указателя c

#c #pointers

#c #указатели

Вопрос:

Я новичок в отношении указателя, кто-нибудь может объяснить мне разницу в выводе в следующем коде? В следующем коде я присваиваю некоторое значение 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 amp;amp; j==0)
            {       
                //  *(*(ptr row) rw)="kabul";
                **ptr="zapac";
            }
            else
            {
                sh[0]=i 48;
                sh[1]=',';
                sh[2]=j 48;
                sh[3]='';
                *(*(ptr i) j)=sh;
            }

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

    for(i=0;i<row;i  )
    {
        for(j=0;j<rw;j  )
        {
            printf("t%d%d %sn",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
 

Комментарии:

1. ptr это не указатель на указатель, а указатель на указатель, указывающий на другой указатель. char ***ptr; ptr=(char *)malloc(row*sizeof(char *)); Вы уверены, что это утверждение прошло без какой-либо ошибки компиляции?

2. Тогда как я должен назначить что-то вроде ptr [0] [0]= «mahesh» ptr [0] [1]= «maverick» ptr [1] [0]=»pqrst» с представлением указателя?

3. да, это так … и я просто скопировал вывод

4. предупреждение: присвоение из несовместимого типа указателя в Ideone

5. @Махеш, я действительно новичок в отношении указателя. У меня было предупреждение о «подозрительном преобразовании указателя». хотя ошибка не была показана. можете ли вы тогда сказать мне, как я должен назначить память и в процессе получить ожидаемый результат?

Ответ №1:

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

Комментарии:

1. @Dimitri спасибо, дмитрий … я все еще недостаточно хорош, чтобы проголосовать за вас …. я использовал приведение, чтобы оно работало на каждом компиляторе ….. это действительно помогло … 🙂

2. @Maverick_Mrt: без приведения он будет работать на каждом компиляторе C — по крайней мере, если вы помните #include <stdlib.h> , чтобы компилятор знал, как malloc() это выглядит.

3. @Keith, я обнаружил, что без приведения и с помощью stdlib Dev-C выдает ошибку компилятора: недопустимое преобразование

4. @Maverick_Mrt: тогда Dev-C не является компилятором C. Судя по названию, я бы сказал, что это компилятор C . Не совершайте ошибку, думая, что C и C не являются двумя разными языками. И если вы хотите программировать на C, вам нужно использовать компилятор C (возможно, есть способ сообщить Dev-C действовать как один).

Ответ №2:

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

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

 #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 amp;amp; j==0)
            {       
                //  *(*(ptr ROW) RW) = "kabul";
                ptr[i][j] = "zapac";
            }
            else
            {
                sh[0] = '0'   i;
                sh[1] = ',';
                sh[2] = '0'   j;
                sh[3] = '';
                // ptr[i][j] = sh;
                strcpy(ptr[i][j], sh);

            }

            // printf("t%d%d = %sn", 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 %sn", 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> a компилятор pre-C99 будет предполагать, что malloc() возвращает an 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 вызовами, чтобы было легче видеть, что происходит.

Комментарии:

1. Я намеренно использовал *(*(ptr i) j) вместо ptr[i][j] просто чтобы понять, как это работает на самом деле… обычно я использую обычный. наряду с ответом Димитриса, ответ ur тоже удобен. большое спасибо

Ответ №3:

Поскольку 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 amp;amp; j==0)
            {       
               strcpy(psh, "zapac");
            }
            else
            {
                psh[0]=i 48;
                psh[1]=',';
                psh[2]=j 48;
                psh[3]='';
                *(*(ptr i) j)=psh;
            }
 

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

Комментарии:

1. если sh[10] назначается локально … я получаю тот же ответ. кстати, в первом блоке после того, как я присвоил значения sh для ptr, разве значение sh не становится неактуальным? т. Е. Предполагается, что ptr содержит все данные независимо от состояния sh. не так ли?

2. Независимо от того, как он назначен, суть в том, что (*(ptr i) j) — все равно — они указывают на одно и то же местоположение, которое является sh[10] . У вас должно быть разное значение sh [10] для каждого i, j, т. Е. ij экземпляров в памяти