C字符指针与字符数组指针,增加动态分配

6

我对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运行了代码,发现我的srcdp每次都被擦除一个字符。如果我将while循环封装成函数调用,则可以正常工作。我认为原因是参数被评估为副本。但是,后来我读到数组是例外,并且作为指针传递。现在我感到困惑。我可以解决这个问题,但我想理解为什么这种方式不起作用。
4个回答

10

您正在循环内部更改dp

dp = malloc(10);

假设dp的值为0x42000000

while () {
    dp++;
}

假设循环次数为4,那么dp的值为0x42000004

*dp = 0;

现在你需要将一个空字符放置在dp所指向的地址处。

puts(dp);

然后您尝试打印那个null :)

保存dp并打印保存的值

dp = malloc(10);
saveddp = dp;
/* ... */
puts(saveddp);
free(saveddp); /* for completeness */
它适用于 scheme,因为 scheme 是一个数组,您无法更改该地址!

4
循环结束后,dp 指向分配字符串的结尾。你需要在 malloc 之后保存 dp,并递增副本而不是原始指针到开头。

3
或许可以将其声明为char *const dp = malloc(10);,这样编译器就不会让您犯这个错误了。 - Nemo

2
循环开始前,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 = &scheme)

                      +----------+
                      |   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);

-2

正如上面指出的那样,“scheme”是字符串开头的指针,dp是迭代器。

char *scheme = malloc(10), *dp = scheme;

...

puts(scheme);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接