C:字符指针和数组之间的差异
考虑:C:字符指针和数组之间的差异,c,arrays,pointers,C,Arrays,Pointers,考虑: char amessage[] = "now is the time"; char *pmessage = "now is the time"; 我从第二版中读到,上述两种说法并不相同 我一直认为数组是一种方便的方法来操作指针来存储一些数据,但事实显然并非如此。。。在C语言中,数组和指针之间的“非平凡”区别是什么?没错,但这是一个细微的区别。基本上,前者: char amessage[] = "now is the time"; 定义其成员位于当前作用域堆栈空间中的数组,而: cha
char amessage[] = "now is the time";
char *pmessage = "now is the time";
我从第二版中读到,上述两种说法并不相同
我一直认为数组是一种方便的方法来操作指针来存储一些数据,但事实显然并非如此。。。在C语言中,数组和指针之间的“非平凡”区别是什么?没错,但这是一个细微的区别。基本上,前者:
char amessage[] = "now is the time";
定义其成员位于当前作用域堆栈空间中的数组,而:
char *pmessage = "now is the time";
定义一个指针,该指针位于当前作用域的堆栈空间中,但引用其他位置的内存(在本例中,“now is the time”存储在内存的其他位置,通常是字符串表)
另外,请注意,由于属于第二个定义(显式指针)的数据未存储在当前作用域的堆栈空间中,因此未指定其确切存储位置,因此不应修改
编辑:正如Mark、GMan和Pavel所指出的,当操作符的地址用于这些变量中的任何一个时,也存在差异。例如,&pmessage返回char**类型的指针,或指向chars的指针,而&amessage返回char(*)[16]类型的指针,或指向16个字符的数组的指针(与char**一样,需要在litb指出时取消引用两次) 数组包含元素。一个指针指向它们 第一种是简短的说法
char amessage[16];
amessage[0] = 'n';
amessage[1] = 'o';
...
amessage[15] = '\0';
也就是说,它是一个包含所有字符的数组。特殊初始化为您初始化它,并自动确定其大小。数组元素是可修改的-您可以覆盖其中的字符
第二种形式是指针,它只指向字符。它不直接存储字符。由于数组是字符串文字,因此不能使用指针并写入它所指向的位置
char *pmessage = "now is the time";
*pmessage = 'p'; /* undefined behavior! */
此代码可能会在您的计算机上崩溃。但它可以做任何它喜欢的事情,因为它的行为是不确定的 第二个在ELF的某个只读部分分配字符串。 请尝试以下操作:
#include <stdio.h>
int main(char argc, char** argv) {
char amessage[] = "now is the time";
char *pmessage = "now is the time";
amessage[3] = 'S';
printf("%s\n",amessage);
pmessage[3] = 'S';
printf("%s\n",pmessage);
}
#包括
int main(字符argc,字符**argv){
char amessage[]=“现在是时候了”;
char*pmessage=“现在是时候了”;
a消息[3]=“S”;
printf(“%s\n”,一条消息);
pmessage[3]=“S”;
printf(“%s\n”,pmessage);
}
在第二次赋值(pmessage[3]='S')时,您将得到一个segfault。与分配在两个不同位置的字符串“now is the time”的内存一起,您还应该记住,数组名充当指针值,而pmessage是指针变量。主要区别在于指针变量可以修改为指向其他地方,而数组不能
char arr[] = "now is the time";
char *pchar = "later is the time";
char arr2[] = "Another String";
pchar = arr2; //Ok, pchar now points at "Another String"
arr = arr2; //Compiler Error! The array name can be used as a pointer VALUE
//not a pointer VARIABLE
数组是常量指针。不能更新其值并使其指向其他任何位置。
对于指针,您可以这样做。我不能有效地添加到其他答案中,但我要指出,在中,Peter van der Linden详细介绍了这个示例。如果你问这些问题,我想你会喜欢这本书的
另外,您可以为
pmessage
指定一个新值。不能将新值分配给amessage
;它是不可变的。如果数组的定义使其大小在声明时可用,sizeof(p)/sizeof(数组类型)
将返回数组中的元素数。第一种形式(amessage
)定义了一个包含字符串副本的变量(数组)“现在是时间”
第二种形式(pmessage
)定义了一个变量(指针),该变量位于与字符串的任何副本不同的位置“现在是时间”
请尝试以下程序:
#include <inttypes.h>
#include <stdio.h>
int main (int argc, char *argv [])
{
char amessage [] = "now is the time";
char *pmessage = "now is the time";
printf("&amessage : %#016"PRIxPTR"\n", (uintptr_t)&amessage);
printf("&amessage[0]: %#016"PRIxPTR"\n", (uintptr_t)&amessage[0]);
printf("&pmessage : %#016"PRIxPTR"\n", (uintptr_t)&pmessage);
printf("&pmessage[0]: %#016"PRIxPTR"\n", (uintptr_t)&pmessage[0]);
printf("&\"now is the time\": %#016"PRIxPTR"\n",
(uintptr_t)&"now is the time");
return 0;
}
#包括
#包括
int main(int argc,char*argv[])
{
char amessage[]=“现在是时候了”;
char*pmessage=“现在是时候了”;
printf(“&amessage:%#016”PRIxPTR“\n”,(uintpttr\u t)&amessage);
printf(“&amessage[0]:%#016“PRIxPTR”\n”,(uintptpr\u t)和amessage[0]);
printf(“&pmessage:%#016“PRIxPTR”\n”,(uintpttr\u t)&pmessage);
printf(“&pmessage[0]:%#016“PRIxPTR”\n”,(uintptpr\u t)&pmessage[0]);
printf(&“现在是时间”:%#016“PRIxPTR”\n,
(uintpttr__t)和"现在是时候";;
返回0;
}
您将看到,虽然&amessage
等于&amessage[0]
,但对于&pmessage
和&pmessage[0]
,情况并非如此。事实上,您将看到存储在amessage
中的字符串位于堆栈上,而pmessage
指向的字符串位于其他位置
最后一个printf显示字符串文本的地址。如果您的编译器执行“字符串池”,那么字符串“now is time”(现在是时间)将只有一个副本,您将看到它的地址与amessage
的地址不同。这是因为amessage
在初始化字符串时获取该字符串的副本
最后,要点是amessage
将字符串存储在它自己的内存中(在本例中,在堆栈上),而pmessage
指向存储在别处的字符串。对于此行:
char amessage[]=“现在是时候了”
编译器将评估amessage的使用情况,将其作为指向包含字符“now is time”的数组开始的指针。编译器为“now is The time”分配内存,并使用字符串“now is The time”对其进行初始化。您知道该消息存储在哪里,因为消息总是指该消息的开头。消息可能不会被赋予新值-它不是变量,而是字符串“now is the time”的名称
这一行:
char*pmessage=“现在是时候了”
声明一个变量pmessage,该变量已初始化(给定初始值)字符串“now is the time”的起始地址。与amessage不同,pmessage可以被赋予一个新值。在这种情况下,就像在previ中一样
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07
0x00008000: 'n' 'o' 'w' ' ' 'i' 's' ' ' 't'
0x00008008: 'h' 'e' ' ' 't' 'i' 'm' 'e' '\0'
...
amessage:
0x00500000: 'n' 'o' 'w' ' ' 'i' 's' ' ' 't'
0x00500008: 'h' 'e' ' ' 't' 'i' 'm' 'e' '\0'
pmessage:
0x00500010: 0x00 0x00 0x80 0x00
char amessage[] = "now is the time";
strcpy(amessage, "the time is now");
char *pmessage = "now is the time";
strcpy(amessage, pmessage); /* OKAY */
strcpy(pmessage, amessage); /* NOT OKAY */
strtok(amessage, " "); /* OKAY */
strtok(pmessage, " "); /* NOT OKAY */
scanf("%15s", amessage); /* OKAY */
scanf("%15s", pmessage); /* NOT OKAY */
pmessage = amessage;
//ATTENTION:
//Pointer depth 1
int marr[] = {1,13,25,37,45,56}; // array is implemented as a Pointer TO THE FIRST ARRAY ELEMENT
int* pmarr = marr; // don't use & for assignment, because same pointer depth. Assigning Pointer = Pointer makes them equal. So pmarr points to the first ArrayElement.
int* point = (marr + 1); // ATTENTION: moves the array-pointer in memory, but by sizeof(TYPE) and not by 1 byte. The steps are equal to the type of the array-elements (here sizeof(int))
//Pointer depth 2
int** ppmarr = &pmarr; // use & because going one level deeper. So use the address of the pointer.
//TYPES
//array and pointer are different, which can be seen by checking their types
std::cout << "type of marr is: " << typeid(marr).name() << std::endl; // int* so marr gives a pointer to the first array element
std::cout << "type of &marr is: " << typeid(&marr).name() << std::endl; // int (*)[6] so &marr gives a pointer to the whole array
std::cout << "type of pmarr is: " << typeid(pmarr).name() << std::endl; // int* so pmarr gives a pointer to the first array element
std::cout << "type of &pmarr is: " << typeid(&pmarr).name() << std::endl; // int** so &pmarr gives a pointer to to pointer to the first array elelemt. Because & gets us one level deeper.
#include <stdio.h>
int main ()
{
char amessage[] = "now is the time"; /* Attention you have created a "string literal" */
char *pmessage = "now is the time"; /* You are REUSING the string literal */
/* About arrays and pointers */
pmessage = NULL; /* All right */
amessage = NULL; /* Compilation ERROR!! */
printf ("%d\n", sizeof (amessage)); /* Size of the string literal*/
printf ("%d\n", sizeof (pmessage)); /* Size of pmessage is platform dependent - size of memory bus (1,2,4,8 bytes)*/
printf ("%p, %p\n", pmessage, &pmessage); /* These values are different !! */
printf ("%p, %p\n", amessage, &amessage); /* These values are THE SAME!!. There is no sense in retrieving "&amessage" */
/* About string literals */
if (pmessage == amessage)
{
printf ("A string literal is defined only once. You are sharing space");
/* Demostration */
"now is the time"[0] = 'W';
printf ("You have modified both!! %s == %s \n", amessage, pmessage);
}
/* Hope it was useful*/
return 0;
}
char c[] = "abc";
char c[] = {'a', 'b', 'c', '\0'};
char *c = "abc";
/* __unnamed is magic because modifying it gives UB. */
static char __unnamed[] = "abc";
char *c = __unnamed;
char s[] = "abc", t[3] = "abc";
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
char *p = "abc";
#include <stdio.h>
int main(void) {
char *s = "abc";
printf("%s\n", s);
return 0;
}
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
char s[] = "abc";
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
readelf -l a.out
Section to Segment mapping:
Segment Sections...
02 .text .rodata