C char указатель против указателя на массив символов, инкрементное динамическое размещение - PullRequest
6 голосов
/ 01 июня 2011

Я новичок в C, и мне трудно понять причину, по которой блок кода ниже не работает.

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

int main()
{
    char *src = "http://localhost";

    /* THIS WORKS
       char scheme[10];
       char *dp = scheme;
     */

    //DOESN'T WORK
    char *dp = malloc(10);

    while (*src != ':') {
        *dp = *src;
        src++;
        dp++;
    }
    *dp = '\0';

    /* WORKS
       puts(scheme)
     */

    //DOESN'T WORK
    puts(dp);
}

Ожидаемый вывод http в стандартный вывод.В обоих случаях dp должен быть указателем на массив символьных указателей (char **).Однако при использовании метода malloc ничего не печатается.Я запускал код через GDB, и мои src и dp стираются по 1 символу за раз.Если я заключаю цикл while в вызов функции, он работает.Я понял, что причина в том, что параметры оцениваются как копия.Однако потом я прочитал, что массивы являются исключением и передаются как указатели.Теперь я в замешательстве.Я могу обойти это, но я пытаюсь понять, почему этот способ не работает.

Ответы [ 4 ]

10 голосов
/ 01 июня 2011

Вы меняете dp внутри цикла

dp = malloc(10);

Допустим, dp имеет значение 0x42000000

while () {
    dp++;
}

Допустим, цикл прошел 4 раза, поэтомуdp имеет значение 0x42000004

*dp = 0;

, теперь вы помещаете нулевой символ по адресу, на который указывает dp

puts(dp);

, а затем пытаетесь напечатать этот нулевой:)

Сохранить dp и распечатать сохраненное значение

dp = malloc(10);
saveddp = dp;
/* ... */
puts(saveddp);
free(saveddp); /* for completeness */

Он работает с scheme, поскольку scheme является массивом, и вы не можете изменить этот адрес!

4 голосов
/ 01 июня 2011

После окончания цикла, dp указывает на конец выделенной строки. Вам нужно сохранить dp сразу после malloc и увеличить копию, а не исходный указатель на начало.

2 голосов
/ 01 июня 2011

Перед началом цикла dp указывает на начало выделенной памяти.На каждой итерации вы копируете символ, обозначенный src, в текущую указанную позицию dp и переходите на одну позицию памяти, указанную dp.В конце цикла dp указывает на область памяти сразу после символа p, которому вы присвоили '\0'.Когда вы пытаетесь напечатать строку с puts (dp), потому что содержимое dp изменилось и теперь указывает на местоположение справа после последнего скопированного символа , оно начнет печать с этого места.Будет напечатана пустая строка, поскольку самое первое местоположение, на которое указывает dp, является нулевым символом.

До цикла

+----------+
|   src    |
+----------+
   |
   | 
   V
+-----+-----+-----+-----+-----+-----+-----+----       ----+----+
|  h  |  t  |  t  |  p  |  :  |  /  |  /  |     . . .   ? |  ? |
+-----+-----+-----+-----+-----+-----+-----+----       ----+----+

+----------+
|    dp    |
+----------+
   |
   | 
   V
+-----+-----+-----+-----+-----+-----+-----+----       ----+----+
|     |     |     |     |     |     |     |     . . .     |    |
+-----+-----+-----+-----+-----+-----+-----+----       ----+----+

После цикла (с dp = malloc (10))

                      +----------+
                      |   src    |
                      +----------+
                           |
                           | 
                           V
+-----+-----+-----+-----+-----+-----+-----+----       ----+----+
|  h  |  t  |  t  |  p  |  :  |  /  |  /  |     . . .     |  ? |
+-----+-----+-----+-----+-----+-----+-----+----       ----+----+

                      +----------+
                      |    dp    |
                      +----------+
                           |
                           | 
                           V
+-----+-----+-----+-----+-----+-----+-----+----       ----+----+
|  h  |  t  |  t  |  p  |  \0 |     |     |     . . .     |    |
+-----+-----+-----+-----+-----+-----+-----+----       ----+----+

Примечание puts (dp) начнет печатать печать с места, указанного выше.Это не даст ожидаемый результат.Кроме того, поскольку вы не сохранили исходный адрес dp, который вы фактически присвоили.Вы не можете восстановить его после цикла.

После цикла (с dp = & схема)

                      +----------+
                      |   src    |
                      +----------+
                           |
                           | 
                           V
+-----+-----+-----+-----+-----+-----+-----+----       ----+----+
|  h  |  t  |  t  |  p  |  :  |  /  |  /  |     . . .     |  ? |
+-----+-----+-----+-----+-----+-----+-----+----       ----+----+

                              +----------+
                              |    dp    |
                              +----------+
                                   |
                                   | 
                                   V
        +-----+-----+-----+-----+-----+-----+-----+----       ----+----+
scheme[ |  h  |  t  |  t  |  p  |  \0 |     |     |     . . .     |    | ]
        +-----+-----+-----+-----+-----+-----+-----+----       ----+----+

puts (scheme) will work because it still refers to the base of the array
puts (dp) will not work because it does not point to the base of the array
         and currently points to a location pointing to null character

В вышеупомянутом комментируемом вашем решении работает, потому что вы используете schemeмассив для печати строки.scheme относится к массиву, который вы хотите напечатать, а scheme относится к базовому адресу массива, потому что вы не изменили его (и он не может быть изменен).Вот почему он начинается с базы и печатается до '\0', назначенного вами после цикла.

Вы можете сделать

 int i;
 for (i=0; (src[i] != ':') && (src[i] != '\0'); i++)
 {
    dp[i] = src[i];
 }

или выполнить следующие действия

 char *dp_bak;
 char *dp = malloc(10);
 dp_bak = dp; /* Backup the base address */

 while (*src != ':')
 {
     *dp = *src;
     src++;
     dp++;
 }
 *dp = '\0';
 dp = db_bak; /* Restore the base address */

 puts (dp);
0 голосов
/ 01 июня 2011

Действительно, как указывалось выше, «схема» была вашим указателем на начало строки, а dp был вашим итератором.

char * схема = malloc (10), * dp = схема;

...

пут (схема);

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