我应该如何理解 char * ch="123"
?
'1'
是一个 char
,所以我可以使用:
char x = '1';
char *pt = &x;
那么我如何理解char *pt="123"
?为什么char *pt
可以指向字符串?
pt
的值是"123"
的第一个地址值吗?如果是,如何获取pt
所指向的字符串的长度?
我应该如何理解 char * ch="123"
?
'1'
是一个 char
,所以我可以使用:
char x = '1';
char *pt = &x;
那么我如何理解char *pt="123"
?为什么char *pt
可以指向字符串?
pt
的值是"123"
的第一个地址值吗?如果是,如何获取pt
所指向的字符串的长度?
这实际上是一个非常好的问题,它是C语言中几个奇怪之处的后果:
1:指向字符的指针(char*
)当然也可以指向字符数组中的特定字符。这就是指针算术所依赖的内容:
// create an array of three chars
char arr[3] = { 'a', 'b', 'c'};
// point to the first char in the array
char* ptr = &arr[0]
// point to the third char in the array
char* ptr = &arr[2]
2: 字符串字面值("foo"
)实际上并不是一个字符串,而只是由字符数组和一个空字节组成的数组(所以"foo"
实际上等同于数组{'f', 'o', 'o', '\0'}
)
3: 在C中,数组会“衰变”为指向第一个元素的指针。(这就是为什么许多人错误地说“在C中,数组和指针没有区别”的原因)。也就是说,在将数组赋给指针对象时,它将指针设置为指向数组的第一个元素。因此,给定上面声明的数组arr
,您可以执行char* ptr = arr
,这与char* ptr = &arr[0]
相同。
4: 在任何其他情况下,这样的语法都会使指针指向rvalue(松散地说,这是一个临时对象,您无法获取其地址),这通常是非法的。(你不能做int *ptr = &42
)。但是,当您定义一个字符串字面值(例如"foo"
)时,它不会创建rvalue。相反,它创建了具有静态存储的char数组。您正在创建一个静态对象,该对象在程序加载时创建,当然指针可以安全地指向它。
5: 字符串字面值实际上需要标记为const
(因为它们是静态且只读的),但是由于早期版本的C没有const
关键字,允许您省略const
声明符(至少在C++11之前),以避免破坏旧代码(但是您仍然必须将变量视为只读)。
因此,char* ch = "123"
实际上意味着:
{'1', '2', '3', '\0'}
写入可执行文件的静态部分(因此当程序加载到内存中时,在内存的只读部分创建此变量)作为额外的有趣事实,这与char ch[] = "123";
不同,后者意思是:
{'1', '2', '3', '\0'}
写入可执行文件的静态部分(因此当程序加载到内存中时,在内存的只读部分创建此变量){1,2,3}
可以用来初始化一个数组,但它本身不是一个数组。因此,在第二种情况下,你使用它创建了一个名为 pt
的数组,其中包含这三个数字。但在第一种情况下,你试图创建一个指向非对象的指针。这就是字符串字面值被特别处理的地方(参见上面的第4点),因为它实际上会创建数组,而语法{1,2,3}
则不会这样做。 - jalfconst
。 - Mike Seymourconst
,所以它不再重要,即使它主观上改善了代码。你只需要这样做。而问题不是关于最佳实践,而仅仅是关于语句的意思。(但你当然是对的,你应该使用const
) - jalfchar* ptr = "123";
与char ptr[] = { '1', '2', '3', '\0' };
兼容并且几乎等价(参见http://ideone.com/rFOk3R)。
C语言中,指针可以指向一个值或连续值的数组。C++继承了这一点。
因此,字符串只是以'\0'
结尾的字符数组(char
)。指向char
的指针可以指向char
数组。
长度由起始位置和结束位置'\0'
之间的字符数给出。以下是C strlen
函数给出字符串长度的示例:
size_t strlen(const char * str)
{
const char *s;
for (s = str; *s; ++s) {}
return(s - str);
}
如果没有以'\0'
结尾,它会失败得非常惨。
char* ptr = "123";
不等同于 char* ptr = { '1', '2', '3', '\0' };
。 - BЈовићchar* ptr
和 char ptr[]
不是等价的。在两种情况下,您都可以修改指向的值。猜猜如果您修改指向常量字符数组的非常量字符指针会发生什么 :) 就像这样:http://ideone.com/i37oMP - BЈовићconst char*
。但是我认为如果您不放置const
,那么关闭警告的编译器甚至不会抱怨...而且在我的记忆中,gcc中有一个选项可以将字符串字面量设置为可修改区域,从而允许在没有崩溃的情况下进行修改。 - Johan字符串字面值是由N个const char
组成的数组,其中N是包括隐式的NUL终止符的长度。它具有静态存储期,并且它的实现定义了它存储在哪里。从这里开始,它与普通数组相同-它会衰减为指向其第一个字符的指针-即const char*
。在C++11标准出现后,你所写的代码不合法,在C++中,应该写成const char* ch = "123";
。
你可以使用sizeof
操作符获取字面值的长度。一旦它衰减为一个指针,你需要遍历它并找到终止符(这就是strlen
函数的作用)。
因此,对于const char* ch;
,你得到一个指向常量字符类型的指针,它可以指向单个字符、字符数组的开头或任何起点和终点之间的位置。该数组可以是动态分配、自动分配或静态分配的,并且可以是可变或不可变的。
在char ch[] = "text";
中,你有一个字符数组。这是正常数组初始化器的语法糖(如char ch[] = {'t','e','x','t','\0'};
,但请注意,文字仍将在程序的开始处加载)。这里发生的是:
因此,你有一个可以随意使用的存储区域(与字面值不同,字面值不应写入)。
指针只指向一个内存地址。说指针指向一个数组只是在宽泛意义上使用——指针实际上不能同时存储多个地址。
在你的例子中,char *ch="123"
,指针ch
实际上只指向第一个字节。你可以编写以下代码,并且它完全有意义:
char *ch = new char [1024];
sprintf (ch, "Hello");
delete [] ch;
char x = '1';
ch = &x;
ch
的使用,它既指向new char [1024]
行分配的内存,又指向变量x
的地址,但仍然是相同的指针类型。
C中的字符串以前是以null结尾的,即在字符串末尾添加了一个特殊的'\0'
字符,并假定所有基于char *
的函数(如strlen
和printf
)都会存在这个字符。这样,您可以从第一个字节开始,一直继续到找到包含0x00
的字节,从而确定字符串的长度。
一个冗长的strlen
样式函数的示例实现如下:
int my_strlen (const char *startAddress)
{
int count = 0;
char *ptr = startAddress;
while (*ptr != 0)
{
++count;
++ptr;
}
return count;
}
char *ch
替换为char ch[]
,则后面的赋值ch = &x
将失败。另外,请参见https://dev59.com/1XM_5IYBdhLWcg3wiDuA。 - Jaywalker0xa0 = 0x31
0xa1 = 0x32
0xa2 = 0x33
0xa3 = 0x00
char* otherString = malloc(4)
,假设malloc
返回值为0xb0
,现在otherString
的值为0xb0
,我们想将"pt
"(其值为0xa0
)复制到otherString
中,那么strcpy
函数应该这样调用:strcpy( otherString, pt );
同样如
strcpy( 0xb0, 0x0a );
strcpy
函数会将地址为0xa0
的值复制到地址为0xb0
的位置,并将其指针递增到“pt
”指向的0xa1
位置,检查0xa1
是否为零,如果不是,则将其指针递增到“otherString
”并将0xa1
复制到0xb1
中。以此类推,直到它的“pt
”指针为0xa3
,在这种情况下,它将返回,因为它检测到已经到达了“字符串”的结尾。
当然,这并不是完全正确的实现方式,因为它可以有很多不同的实现方法。
这里有一个链接
char* pt = "123"; does two things:
1. 在ROM中创建字符串字面量"123"(通常在.text部分)
2. 创建一个char*
,该指针被赋值为存储字符串的内存位置的开头。
由于这些操作,像pt[1] = '2';
这样的操作是非法的,因为你试图写入ROM内存。
但是你可以将指针分配给其他内存位置而没有任何问题。