我在尝试理解指针时遇到了非常令人沮丧的时间,当编码完全围绕指针旋转时,我得到的错误实际上是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]的地址不是吗?
答案 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
是在堆栈上分配的新变量(记住i
且array
具有不同的内存位置),并且您已指定指向它的指针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
。
请注意,在上面的输出中,表达式&arr
和arr
返回相同的值 - 数组的地址与第一个元素的地址相同数组 - 但它们有不同的类型。 &arr
的类型为&#34;指向char
&#34;或char (*)[6]
的6元素数组,而arr
的类型为&#34;指针{ {1}}&#34 ;.
变量char
和buf
是ptr
的简单指针;他们将其他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"
,但我们获得了不同的输出,因为转换说明符buf
和printf( " value of arr variable: %p\n", (void *) arr );
printf( " contents of arr variable: %s\n", arr );
处理指针价值不同。 printf
期望其对应的参数具有类型%p
,并以平台相关的方式显示指针值 5 。 %s
期望其对应的参数具有类型%p
并期望它指向一个字符串,并且它将输出该字符串中的字符序列,直到它看到字符串终止符。
<小时/>
void *
,%s
,{{1}的数组},或char *
(对于UTF-8),终结符适用于类型和编码。
答案 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;。