如何理解字符 * ch= "123"?

how to understand char * ch="123"?

本文关键字:ch 何理解 字符      更新时间:2023-10-16

我应该如何理解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', ''}(

3:在 C 中,数组"衰减"为指向第一个元素的指针。(这就是为什么许多人错误地说"C 中的数组和指针之间没有区别"(。也就是说,当您尝试将数组分配给指针对象时,它会将指针设置为指向数组的第一个元素。所以给定上面声明的数组arr,你可以做char* ptr = arr,它的意思与char* ptr = &arr[0]相同。

4:在所有其他情况下,像这样的语法会使指针指向一个右值(松散地说,一个临时对象,你不能接受它的地址(,这通常是非法的。(你不能做int* ptr = &42(。但是当你定义字符串文字(如 "foo" (时,它不会创建右值。相反,它会创建具有静态存储的 char 数组。您正在创建一个静态对象,该对象是在加载程序时创建的,当然指针可以安全地指向对象。

5:字符串文字实际上需要标记为const(因为它们是静态和只读的(,但由于早期版本的 C 没有 const 关键字,因此您可以省略const说明符(至少在 C++11 之前(,以避免破坏旧代码(但您仍然必须将变量视为只读(。

所以char* ch = "123"真正的意思是:

  1. 将 char 数组{'1', '2', '3', ''}写入可执行文件的静态部分(以便在将程序加载到内存中时,在内存的只读部分中创建此变量(
  2. 执行此行代码时,创建一个指向此数组的第一个元素的指针

作为一个额外的有趣的事实,这与 char ch[] = "123"; 不同,而是意味着

  1. 将 char 数组{'1', '2', '3', ''}写入可执行文件的静态部分(以便在将程序加载到内存中时,在内存的只读部分中创建此变量(
  2. 执行此行代码时,在堆栈上创建一个数组,其中包含此静态分配数组的副本。
char* ptr = "123";兼容

并且几乎等同于char ptr[] = { '1', '2', '3', '' };(见 http://ideone.com/rFOk3R(。

在 C 中,指针可以指向一个值或一个连续值数组。C++继承了这一点。所以字符串只是一个以''结尾的字符数组(char(。指向char的指针可以指向char数组。

长度由开头和结尾''之间的字符数给出。C 的示例strlen给出字符串的长度:

size_t strlen(const char * str)
{
    const char *s;
    for (s = str; *s; ++s) {}
    return(s - str);
}

是的,如果最后没有'',它会失败得很惨。

字符串

文本是 N 个const char数组,其中 N 是文本的长度,包括隐式 NUL 终止符。它具有静态存储持续时间,并且它的实现定义了存储位置。从这里开始,它与普通数组相同 - 它衰减到指向其第一个字符的指针 - 这是一个const char*.你在那里拥有的东西在C++中是不合法的(自 C++11 标准开始以来就不再合法了(,它应该是const char* ch = "123";.

您可以使用运算符获取文本sizeof长度。但是,一旦它衰减到指针,您需要遍历它并找到终止符(这就是strlen函数的作用(。

因此,使用const char* ch;,您将获得指向常量字符类型的指针,该指针可以指向单个字符或字符数组的开头(或开头和结尾之间的任何位置(。数组可以是动态的、自动的或静态分配的,可以是可变的,也可以是可变的。

在类似char ch[] = "text";的东西中,你有一个字符数组。这是普通数组初始值设定项的合成糖(如char ch[] = {'t','e','x','t',''};但请注意,文本仍将在程序开始时加载(。这里发生的事情是:

  • 分配具有自动存储持续时间的阵列
  • 它的大小是由编译器从文字的大小推导
  • 出来的
  • 文本的内容被复制到数组中

因此,您有一个可以随意使用的存储区域(与文字不同,文字不得写入(。

C 中没有字符串,但有指向字符的指针。 *pt确实不是指向字符串,而是指向单个字符('1'(。但是,某些函数将char*作为参数,假设其参数指向的地址后面的地址上的字节设置为0,如果他们不对其进行操作。

在您的示例中,如果您尝试对需要"null 终止字符串"的函数使用pt(基本上,它期望它在应该停止处理数据时会遇到值为 0 的字节(,您将遇到分段错误,因为x='1' x提供了1字符的 ascii 值, 但仅此而已,char* pt="123"给出了 pt 1地址的值,但也将包含 ASCII 值的字节放入该内存中 12 3后跟一个值为 0(零(的字节。

因此,内存(在 8 位机器中(可能如下所示:

地址 = 内容(0x31是字符 1(一(的 ASCII 代码(

0xa0 = 0x31
0xa1 = 0x32
0xa2 = 0x33
0xa3 = 0x00

假设你在同一台机器char* otherString = malloc(4),假设malloc返回一个值 0xb0,现在是 otherString 的值,并且我们想将我们的 "pt"(其值为 0xa0(复制到 otherString 中,strcpy调用将如下所示:

strcpy( otherString, pt );

strcpy( 0xb0, 0x0a );

然后strcpy会获取地址0xa0的值并将其复制到0xb0中,它会将指向"pt"的指针递增到0xa1,检查0xa1是否为零,如果不是零,它将递增指向"otherString"的指针,并将0xa1复制到0xb1中,依此类推, 在它被0xa3"pt"指针之前,在这种情况下,它将返回,因为它检测到"字符串"的末尾已经到达。

这是有原因的,而不是100%如何进行,它可以以许多不同的方式实现。

这是一条 http://fossies.org/dox/glibc-2.18/strcpy_8c_source.html

指向数组的指针?

指针仅指向一个内存地址。指针指向数组的短语仅在松散的意义上使用---指针不能真正同时存储多个地址。

在您的示例中,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 样式字符串以空值结尾

C中的字符串曾经以null结尾,即在字符串的末尾添加一个特殊的'',并假定所有基于char *的函数(如strlenprintf(都存在,这样,你可以通过从第一个字节开始来确定字符串的长度,直到找到包含0x00的字节。

strlen样式函数的详细示例实现将是

int my_strlen (const char *startAddress)
{
  int count = 0;
  char *ptr = startAddress;
  while (*ptr != 0)
  {
     ++count;
     ++ptr;
  }
  return count;
}
char* pt = "123"; does two things:

1. 在 ROM 中创建字符串文字"123"(这通常在 .text 部分( 2. 创建一个char*,该被分配字符串所在的内存位置的开头。

由于此操作like pt[1] = '2';是非法的,因为您将尝试写入ROM内存。

但是您可以将指针分配给其他内存位置,而不会出现任何问题。