只是无法理解指针

时间:2015-10-15 09:40:45

标签: c arrays pointers

我在尝试理解指针时遇到了非常令人沮丧的时间,当编码完全围绕指针旋转时,我得到的错误实际上是90%

所以我有一个char *数组,mallocced和持有字符

strcpy由于某种原因只接受指针,所以如果我想将array [x] strcpy到目的地,我不能只使用

strcpy(destination,array[x])

因为这给了我一个错误,说array [x]是char,而不是char *

现在这是我没有得到的东西 - 如何制作指向数组[x]的指针?我根本不懂指针

当我使用

char i;
char *j;
i = array[0];
j = &i;
strcpy(destination,j);

它给了我这封信,但伴随着垃圾字符,尽管事实上我在将它分配给& i

之前我已经设置了j。

当我使用

char *j;
j = &array[0];
strcpy(destination,j);

它添加整个数组

有什么区别?在两种情况下,我都将J分配给x [0]的地址不是吗?

7 个答案:

答案 0 :(得分:6)

您已经忘记了变量的位置以及您要复制的内容。这是您的代码注释它正在做什么。

char i;          // reserves a variable of size char on the stack
char *j;         // reserves a variable of size ptr on the stack
i = array[0];    // copies the first char from the array 
                 // into variable i
j = &i;          // copies the stack address of variable i into j.. 
                 // note that this is not the array as we 
                 // previously copied the char out of the array  
strcpy(destination,j);  // you have not supplied destination, so 
                        // no comment there but j is a pointer 
                        // onto the stack AND NOT a pointer 
                        // into the array.. thus who knows
                        // what characters it will copy afterwards

其中版本2不同:

char *j;               // reserves a variable on the stack of size ptr
j = &array[0];         // sets the contents of variable j to the address 
                       // of the first element of the array.. note
                       // that this is the same as `j = array`
strcpy(destination,j); // starts copying the chars from j into 
                       // destination, incrementing the pointer j 
                       // one at a time until a null is found and 
                       // the copying will stop

总而言之,版本2正在复制数组..其中版本1在将数组的第一个字符复制到堆栈之后从堆栈中复制数据。因此行为是不同的。

答案 1 :(得分:2)

strcpy (dest, src)将尝试将src字符串复制到目标,直到找到null终止符(\ 0)。

char i;
char *j;
i = array[0];
j = &i;
strcpy(destination,j);

i是在堆栈上分配的新变量(记住iarray具有不同的内存位置),并且您已指定指向它的指针j。这就是你看到一些垃圾字符的原因,因为strcpy会从i的内存位置开始复制,直到找到'\ 0'。

char *j;
j = &array[0];
strcpy(destination,j);

在这种情况下,j指向数组(第一个字符),strcpy将通过上述相同的逻辑将整个array复制到目的地。

不同之处在于,第一种情况下的j不是指向分配array的相同内存位置的指针。您已明确定义了一个新变量i,变量j指向变量i的内存位置。

答案 2 :(得分:1)

字符串复制功能需要这样:

strcpy(destination,array)

因此,它会将array[0]中的字节复制到\0数组中的终止destination字符。

如果您需要复制单个字符,请不要使用strcpy。顺便说一下,&array[0]array完全相同。它们都包含数组第一项的地址。

答案 3 :(得分:1)

理解指针的关键是要意识到它们并不神奇,而只是一个普通的整数变量,意味着保存一个内存地址。程序中的每个变量都有一个内存地址,因为这就是计算机的工作方式。出于多种原因,这些地址非常便于访问。

strcpy不要指望"指针",而是期望字符串。 C中的字符串是以空终止字符\0结尾的字符数组。

C中有一条规则,即无论何时在表达式中使用数组名称,例如destination,它都会转换为指向该数组中第一个元素的指针。这就是strcpy()想要指针的原因。

实际上,你甚至无法将数组传递给C中的函数,因为这会使程序变得非常慢,每次都必须复制数组。只需将地址传递给数组中的第一项就可以快得多。

strcpy(destination,array[x])无法正常工作,因为array[x]是一个字符,而不是一个字符串。

现在,当您将该字符复制到变量i时,您仍然只有一个字符,但它不是字符串,因为字符串为空终止。当您通过指针获取i的地址并将其提交给strcpy时,您就会"谎言"到strcpy并告诉函数"这是一个字符串"。但它不是字符串,因为没有空终止。

strcpy然后期望在角色之后的某处找到空终止。因此,它打印字符,并继续在内存中打印恰好在变量i之后分配的随机内容。它将继续执行此操作,直到找到值为零的存储器单元。通常这会导致程序崩溃。

您可以通过以下方式解决此问题:

char i[2];
i[0] = array[x];
i[1] = '\0'; // null termination
strcpy(destination, i); 

strcpy(destination, i);中,数组i衰变为指向第一个元素的指针,因此您甚至不需要单独的指针变量。

答案 4 :(得分:1)

当你这样写:

j = &array[0];

这意味着您将从第一个字符开始直到结束。

但是当你写下来的时候:

i = array[0];
j = &i;

这意味着您只需要分配数组第一个字符的值。

答案 5 :(得分:1)

所以,让我们花点时间谈谈字符串,数组,指针以及它们如何相互关联。

我们将从一个简短的示例程序及其输出开始,以说明一些概念:

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

int main( void )
{
  char arr[] = "Hello";
  char *buf = malloc( strlen( arr ) + 1 );
  if ( buf )
    strcpy( buf, arr );
  char *ptr = "Hello";

  printf( "    address of \"Hello\" string literal: %p\n", (void *) "Hello" );
  putchar( '\n' );

  printf( "              address of arr variable: %p\n", (void *) &arr );
  printf( "                value of arr variable: %p\n", (void *) arr );
  printf( "             contents of arr variable: %s\n", arr );
  putchar( '\n' );

  printf( "              address of buf variable: %p\n", (void *) &buf );
  printf( "                value of buf variable: %p\n", (void *) buf );
  printf( "    string pointed to by buf variable: %s\n", buf );
  putchar( '\n' );

  printf( "              address of ptr variable: %p\n", (void *) &ptr );
  printf( "                value of ptr variable: %p\n", (void *) ptr );
  printf( "    string pointed to by ptr variable: %s\n", ptr );
  putchar( '\n' );

  char *names[] = {"\"Hello\"", "arr", "buf", "*buf", "ptr"};
  void *addrs[] = {"Hello", arr, &buf, buf, &ptr};
  size_t lens[] = {sizeof "Hello", sizeof arr, sizeof buf, strlen( buf ), sizeof ptr };

  dumper( names, addrs, lens, 5, stdout );

  free( buf );

  return 0;
}

以及输出:

address of "Hello" string literal: 0x400de8

          address of arr variable: 0x7fff7018cc30
            value of arr variable: 0x7fff7018cc30
         contents of arr variable: Hello

          address of buf variable: 0x7fff7018cc28
            value of buf variable: 0x502010
string pointed to by buf variable: Hello

          address of ptr variable: 0x7fff7018cc20
            value of ptr variable: 0x400de8
string pointed to by ptr variable: Hello

       Item        Address   00   01   02   03
       ----        -------   --   --   --   --
    "Hello"       0x400de8   48   65   6c   6c    Hell
                  0x400dec   6f   00   00   00    o...

        arr 0x7fff7018cc30   48   65   6c   6c    Hell
            0x7fff7018cc34   6f   00   00   00    o...

        buf 0x7fff7018cc28   10   20   50   00    ..P.
            0x7fff7018cc2c   00   00   00   00    ....

       *buf       0x502010   48   65   6c   6c    Hell
                  0x502014   6f   00   00   00    o...

        ptr 0x7fff7018cc20   e8   0d   40   00    ..@.
            0x7fff7018cc24   00   00   00   00    ....

在C中,字符串是一系列字符值,后跟一个0值终结符。字符串存储N + 1个char数组,其中N是字符串的长度,不包括终结符 1 。上面三个字符串示例是字符串文字"Hello",数组arr和表达式*buf。每个表达式的上面转储输出显示对应于"Hello"的ASCII值序列,后跟一个0值字节(终结符后面还有几个0值字节,但它们不是数组)。

C中的大多数字符串处理函数(例如strcpy)期望指向字符串的第一个元素(char *);然后他们会走路&#34;直到他们看到终结者。

C中的数组只是给定类型元素的连续序列;数组不存储关于它们的大小或元素属性的任何元数据,也不存储指针值到数组的第一个元素(同样,这从上面的转储输出中可以看出)。数组可能不是=赋值运算符的目标,函数也不能返回数组类型。

除非它是sizeof或一元&运算符的操作数,或者是用于初始化数组的字符串文字(例如在上面的代码中),表达式类型&#34; N - 元素数组T&#34;将被转换(&#34;衰减&#34;)到类型为&#34;指向T&#34;的表达式,其值将是数组 3 。所以在行

strcpy( buf, arr );

数组表达式arr不是sizeof或一元&运算符的操作数,并且它不是用于在声明中初始化另一个数组的字符串文字,所以编译器隐式地将其转换为类型&#34; 6元素数组char&#34; to&#34;指向char&#34;的指针(char *),其值是数组的第一个元素的地址。 strlen也是如此。

数组索引是根据指针操作完成的。表达式a[i] 已定义*(a + i)。我们从中获取地址a,偏移i元素( not bytes!)以获取新地址,然后取消引用结果。这就是buffer[i]类型char而不是char *的原因。这也意味着a + i会根据a的类型给出不同的结果。假设如下:

int *a    = 0x1000;
char *b   = 0x1000;
double *c = 0x1000;

表达式a + 1将评估为0x1000 + sizeof (int);假设一个4字节int,您将获得0x1004。表达式b + 1将评估为0x1001(根据定义,sizeof (char)为1)。表达式c + 1将评估为0x1001 + sizeof (double);假设一个8字节的双精度,这将导致0x1008

请注意,在上面的输出中,表达式&arrarr返回相同的 - 数组的地址与第一个元素的地址相同数组 - 但它们有不同的类型&arr的类型为&#34;指向char&#34;或char (*)[6]的6元素数组,而arr的类型为&#34;指针{ {1}}&#34 ;.

变量charbufptr的简单指针;他们将其他char个对象的地址存储在内存中(请参阅转储输出 - char的内容构成ptr的地址,存储在endian order)。 "Hello"被赋予buf调用的输出,该调用是堆上动态分配的缓冲区的第一个元素的地址。 malloc被赋予ptr字符串文字的第一个元素的地址。

鉴于所有这些,这里有各种表达式,它们的类型和相应值的表格:

"Hello"

请注意 Expression Type "Decays" to Value ---------- ---- ----------- ----- "Hello" char [6] char * Address of first element in string literal &"Hello" char (*)[6] n/a Address of string literal object (yes, you can do this) *"Hello" char n/a Value of first element of string literal object ('H') (yes, you can do this) "Hello"[i] char n/a Value of i'th element of string literal (yes, you can do this) &"Hello"[i] char n/a Address of i'th element of string literal arr char [6] char * Address of first element in array &arr char (*)[6] n/a Address of array object *arr char n/a Value of first element in array ('H') arr[i] char n/a value of i'th element in array &arr[i] char * n/a Address of i'th element in array buf char * n/a Address of first element in dynamically allocated buffer &buf char ** n/a Address of buf variable *buf char n/a Value of first element in dynamically allocated buffer buf[i] char n/a Value of i'th element in dynamically allocated buffer &buf[i] char * n/a Address of i'th element in dynamically allocated buffer ptr char * n/a Address of first element in string literal &ptr char ** n/a Address of ptr variable *ptr char n/a Value of first element in string literal ptr[i] char n/a Value of i'th element in string literal &ptr[i] char * n/a Address of i'th element in string literal &"Hello"的类型和值与&arr&buf的类型和值不同。同样,&ptr&arr指的是相同的内存位置(数组的地址是数组的第一个元素的地址),但类型是不同的。 arr&buf不 - buf是指针变量&buf的地址,它包含内存中另一个对象的地址。另请注意,buf&buf的类型为&ptr - &#34;指针指向char **&#34;,这是与{{1}不同的类型}。

正如我上面提到的,几乎所有字符串处理例程都希望它们的参数具有类型char 4 ,并且它们希望指针对应于字符串的第一个元素的地址 - 即,由0值字节终止的字符序列。上表将显示哪些表达式具有此类操作的正确类型。如果您想将char (*)[6]的最后3个字符复制到char *,则可以写

"Hello"

buf的类型为strcpy( buf, &arr[2] ); ,是&arr[2]中第一个char *字符的地址,因此会将子字符串'l'复制到{{ 1}}。

还有一件事:

"Hello"

我们将相同的指针传递给"llo",但我们获得了不同的输出,因为转换说明符bufprintf( " value of arr variable: %p\n", (void *) arr ); printf( " contents of arr variable: %s\n", arr ); 处理指针价值不同。 printf期望其对应的参数具有类型%p,并以平台相关的方式显示指针值 5 %s期望其对应的参数具有类型%p并期望它指向一个字符串,并且它将输出该字符串中的字符序列,直到它看到字符串终止符。

<小时/>

  1. C还允许宽字节和多字节(例如UTF-8)字符串表示扩展和非英语字符集,这些字符集将存储为void *%s,{{1}的数组},或char *(对于UTF-8),终结符适用于类型和编码。
  2. 地址值的广泛差异与字符串文字的存储位置和方式有关,与堆栈上分配的变量有关。这因实施而异,我们不打算在此详细讨论。
  3. 还有其他一些例外,但我们不需要在此担心。
  4. 实际上,大多数字符串处理例程的原型使用`const char * restrict`作为它们的参数类型,这意味着例程假定它不能通过指针修改输入(它指向`const char`)并且多个输入在内存中不重叠(`restrict`)。
  5. 这可能是C中唯一一个应该将指针值显式地转换为`void *`的地方。输出可能因系统而异,因为指针值的内部表示可能因系统而异。有些人将其视为无符号整数,有些人将其视为表示页码和页面偏移的一对值,有些可以使用完全不同的表示。指向不同类型的指针可能具有不同的大小和表示。

答案 6 :(得分:0)

不确定您的推理错误的确切位置,但是:

指针就像是&#34;数组&#34;的索引。那是整个机器的记忆。

不同的变量位于机器内存的不同位置。

所以&#34;&amp; i&#34;总是与&#34;&amp; array [0]&#34;。

不同

你的&#34; i =数组[0]&#34;是一个char赋值(双方都是&#34; char&#34;) - &amp; array [0]中的单个char被复制到location&amp; i。

机器内存中跟随&amp; i的字符是不确定的 - 它们超出了任何变量的定义范围,因此&#34;垃圾&#34;。