请记住一个让我烦恼的问题。
我知道pointers
和arrays
在C中有所不同,因为pointers
存储address
而arrays
存储'real' values
。
但是我对string
感到困惑。
char *string = "String";
我读到这一行做了几件事:
编译器创建字符数组,其值为String
。
然后,该数组被视为pointer
,并且程序为指针字符串分配一个指针,该指针指向编译器创建的数组的first element
。
这意味着,arrays
被视为pointers
。
那么,这个结论是真还是假?为什么?
如果为false,那么pointers
和arrays
之间的区别是什么?
感谢。
答案 0 :(得分:7)
指针包含对象的地址(或者是不指向任何对象的空指针)。指针具有特定类型,指示它可以指向的对象类型。
数组是一个连续的有序元素序列;每个元素都是一个对象,并且数组的所有元素都是相同的类型。
string 定义为"由第一个空字符"终止并包含的连续字符序列。 C没有字符串类型。字符串是数据布局,而不是数据类型。
数组和指针之间的关系可能令人困惑。我所知道的最好的解释是由comp.lang.c FAQ的第6节给出的。要记住的最重要的事情是数组不是指针。
阵列在某种意义上是第二类公民"在C和C ++中。它们不能被赋值,作为函数参数传递,或者进行相等性比较。操作数组的代码通常使用指向数组各个元素的指针,并使用一些显式机制来指定数组的长度。
混淆的一个主要原因是数组类型的表达式(例如数组对象的名称)在大多数上下文中被隐式转换为指针值。转换后的指针指向数组的初始(第零个)元素。如果数组是:
,则不会发生此转换sizeof
的操作数(sizeof array_object
产生数组的大小,而不是指针的大小); &
的操作数(&array_object
生成整个数组对象的地址);或初始化程序中用于初始化数组对象的字符串文字。
char * string =" String&#34 ;;
为避免混淆,我将在您的示例中进行一些更改:
const char *ptr = "hello";
字符串文字"hello"
创建一个类型为char[6]
(在C中)或const char[6]
(在C ++中)的匿名对象,其中包含字符{ 'h', 'e', 'l', 'l', 'o', '\0' }
。
在该上下文中,对该表达式的求值产生指向该数组的初始字符的指针。这是指针值;没有隐式创建的指针对象。该指针值用于初始化指针对象ptr
。
在任何时候,阵列都不会被视为"一个指针。数组表达式转换为指针类型。
混淆的另一个原因是看似数组类型的函数参数实际上是指针类型;在编译时类型是调整。例如,这个:
void func(char param[10]);
真的意味着:
void func(char *param);
10
会被默默忽略。所以你可以这样写:
void print_string(char s[]) {
printf("The string is \"%s\"\n", s);
}
// ...
print_string("hello");
这个看起来就像操纵数组一样,但事实上数组"hello"
被转换为指针,而指针是传递给print_string
的指针功能
答案 1 :(得分:2)
那么,这个结论是真是假,为什么?
你的结论是错误的。
数组和指针是不同的。 comp.lang.c FAQ list · Question 6.8解释了数组和指针之间的区别:
数组是一个预先分配的单个连续元素(所有相同类型),大小和位置都是固定的。指针是对任何数据元素(特定类型)的引用。必须指定一个指针指向其他地方分配的空间,但是可以随时重新分配它(并且可以调整空间,如果从malloc派生的话,可以调整大小)。指针可以指向一个数组,并且可以模拟(与malloc一起)一个动态分配的数组,但指针是一个更通用的数据结构。
当你这样做时
char *string = "String";
当C编译器遇到这种情况时,它为字符串文字7
预留了String
个字节的内存。然后将指针string
设置为指向已分配内存的起始位置。
宣布
时char string[] = "String";
当C编译器遇到这种情况时,它为字符串文字7
预留了String
个字节的内存。然后给出该存储器位置的名称,即第一个字节string
。
所以,
string
是一个指针变量,在第二种情况下,它是一个数组名称。 这意味着数组不被视为C中的指针,但它们在指针算术和数组索引在C 中是等效的意义上密切相关,指针和数组是不同的。
答案 2 :(得分:0)
我们可以创建一个名为' string'
的数组char string[] = "Hello";
我们可以分配一个指向该字符串的指针
char* stringPtr = string;
因此,数组名称与指针类似。但是,它们并不相同,因为数组是连续的内存块,而指针只引用内存中的单个位置(地址)。
char *string = "String";
此声明创建数组并设置指向用于存储数组的内存块的指针的地址。
这意味着,数组被视为指针。那么,这个结论是真还是假
错误,数组不是指针。但是,由于解引用运算符[]
,只是为了混淆(!),指针可能看起来像是数组。char *string = "String";
char letter = string[2];
在本例中 string [2] ,首先将字符串转换为指向数组第一个字符的指针,并使用指针算法,返回相关项。
答案 3 :(得分:0)
你必须了解内存中发生的事情。
字符串是一个连续的内存单元块,以特殊值(空终止符)终止。如果你知道这个内存块的开始,并且你知道它的结束位置(通过被告知存储单元的数量或者通过读取它们直到你到达null)那么你就可以了。
指针只不过是内存块的开头,它是第一个存储单元的地址,或者是指向第一个元素的指针。所有这些术语都意味着同样的事情。它就像电子表格中的单元格引用一样,如果你有一个巨大的网格,你可以通过它的X-Y坐标告诉一个特定的单元格,所以单元格B5告诉你一个特定的单元格。在计算机术语(而不是电子表格)中,内存实际上是一个非常非常长的单元格列表,如果您愿意,它可以是一维电子表格,单元格引用看起来像0x12345678而不是B5。
最后一点是理解计算机程序是一个由OS加载到内存中的数据块,编译器会找出相对于程序开头的字符串的位置,所以你自动知道哪个它所在的内存块。
这与在堆上分配内存块(它只是巨大内存空间的另一部分)或堆栈(同样是为本地分配保留的一块内存)完全相同。您拥有字符串所在的第一个内存位置的地址。
所以
char* mystring = "string";
char mystring[7];
copy_some_memory(mystring, "string", 7);
和
char* mystring = new char(7);
copy_some_memory(mystring, "string", 7);
都是一样的。 mystring
是第一个字节的内存位置,包含值's'。语言可能会使它们看起来不同,但这只是语法。所以数组是一个指针,只是语言使它看起来不同,你可以使用稍微不同的语法对其进行操作,以使其操作更安全。
(注意:第一个和其他示例之间的最大区别在于编译器集数据集是只读的。如果您可以更改该字符串数据,您可以更改您的程序代码,因为它也只是一个存储在为程序数据保留的内存段中的CPU指令块。出于安全考虑,这些特殊的内存块仅限于您。)
答案 4 :(得分:0)
以下是另一种看待它们的方式:
首先,memory
是您可以存储数据的地方。
其次,address
是一些记忆的位置。 address
引用的内存可能存在也可能不存在。您无法将置于 address
,只有 address
- 您只能将数据存储在memory
中} address
指的是。
array
是内存中的连续位置 - 它是特定类型的一系列内存位置。它存在,并且可以将实际数据放入其中。与memory
中的任何实际位置一样, 一个address
。
pointer
包含address
。该地址可以来自任何地方。
string
是NUL终止的array
字符。
这样看:
memory
- 一所房子。你可以把东西放进去。房子有 address
。
array
- 一排房子,一个在另一个旁边,都是一样的。
pointer
- 一张纸,您可以写一个address
。你不能在纸张本身存储任何东西(地址除外),但是你可以把东西放在你在纸上写的地址上。
答案 5 :(得分:0)
然后,这个数组被认为是一个指针,程序为指针字符串分配一个指向编译器创建的数组的第一个元素的指针。
这里措辞不是很好。数组仍然是一个数组,并被视为这样。程序将指向第一元素的指针值(rvalue)分配给指向char的变量(通常为lvalue)。这是这里唯一的中间/非存储指针值,因为编译器和链接器在编译链接时知道数组的地址。但是,您无法直接访问该数组,因为它是匿名文字。如果你用文字初始化一个数组,那么文字将会消失(想象为优化 - 作为单独的实体),并且数组可以通过其预先计算的地址直接访问。
char s[] = "String"; // char[7]
char *p = s;
char *p = &s[0];