我是C的新手,我有一个关于char指针及其打印内容的问题。看一看:
int main()
{
char *p1="ABCD";
p1="EFG";
printf ("%s",p1);
return 0;
}
它将打印EFG
现在:
int main()
{
char *p1="ABCD";
//p1="EFG";
printf ("%s",p1);
return 0;
}
它会给你ABCD
我不知道的是*p1
究竟是什么?
它是一个包含char
值的地址吗?
是char
吗?
现在*p1
中的内容是什么?为什么是const
?
答案 0 :(得分:3)
来自C标准:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
EXAMPLE 8 The declaration
char s[] = "abc", t[3] = "abc";
defines ‘‘plain’’ char array objects s and t whose elements are initialized with character string literals.
This declaration is identical to
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
The contents of the arrays are modifiable. On the other hand, the declaration
char *p = "abc";
defines p with type ‘‘pointer to char’’ and initializes it to point to an object
with type ‘‘array of char’’ with length 4 whose elements are initialized with a
character string literal. If an attempt is made to use p to modify the contents
of the array, the behavior is undefined.
换句话说:
1)char *p1;
声明一个指针变量(p1)。指针p1可以改变。
2)char *p1 = "ABCD";
声明p1(与" 1"相同)并初始化它指向的数据(将其初始化为只读,5元素字符数组" ABCD \ 0"。)
所以你可以改变地址" p1"指向(你可以指向" EFG",将它指定为NULL等等)。但是你不能安全地改变数据本身。您可能会受到访问违规行为"如果你试图覆盖" ABCD"。确切的行为是"未定义" - 任何都可能发生。
3)char p2[] = "ABCD";
声明一个变量(p2),它是一个数组。它分配5个字符,并将数据初始化为" ABCD \ 0"。
数组是读写的(你可以改变数组中的数据),但变量" p2"无法更改(您无法将p2更改为指向其他地址 - 因为它不是指针)。
4)那么"差异"在char *指针和char数组之间[]?这是一个很好的讨论:
答案 1 :(得分:1)
p1
是一个char
指针,用于存放基地址或起始地址或第一个元素的地址使用=
运算符为字符串 分配的em>。
在你的第一个代码中,你已经为它分配了基本地址“ABCD”,后来是“EFG”的基地址[覆盖了以前的值]并最终打印出来。因此,打印出最新值[“EFG”]。
在第二种情况下,您将分配基本地址“ABCD”并进行打印。所以它会打印ABCD
。
也许值得一提的是,%s
中的printf()
格式说明符需要以p1
为指向的空终止字符串的起始地址。
根据const
部分,p1
指向的字符串的值在这种情况下是常量,因为字符串文字通常是存储在只读存储器位置。意味着,不允许更改值p[1] = 's'
。但是,指针p1
不是const
,因此可以正确地将其重新分配给新值p1="EFG";
。
答案 2 :(得分:1)
p1
是一个char指针,它保存编译器在内存中放置“ABCD”的地址。地址是数组中的第一个字符。
当你有char *p1 = "ABCD";
时,你只需要分配p1
中保存的地址的值。
当您重新分配p1 = "EFG";
时,您正在重新分配指针以保存新地址(“EFG”所在的位置)
printf ("%s",p1);
打印位于p1
个位置的内存内容。
答案 3 :(得分:1)
最初p1被声明为char指针并指向" ABCD" (" ABCD"的基地址)。
稍后指针指向" EFG" (包含&#34的基地址; EFG")
+----+ +---+
|ABCD| |EFG|
+----+ +---+
1000 2000
char *p1="ABCD";
+----+
before |1000|
+----+
p1
p1="EFG";
After +----+
|2000|
+----+
p1
答案 4 :(得分:1)
int main()
{
char *p1="ABCD";
p1="EFG";
printf ("%s",p1);
return 0;
}
这缺少必需的#include <stdio.>
。没有它,你的编译器可能会让你忘记调用printf
,但它不能保证工作。还有一个小问题:int main()
应为int main(void)
。
在这个程序中实际上发生了很多事情,其中一些是相当微妙的。
char *p1 = "ABCD";
"ABCD"
是一个字符串文字。它指定类型为char[5]
的匿名数组对象(对于您指定的字符为4,对于标记字符串结尾的'\0'
空字符为1)。该对象具有静态存储持续时间,这意味着它在程序的整个执行期间存在(与局部变量不同,例如,当它超出范围时不再存在(&#39;略微过于简单化))。
在大多数情况下,数组表达式被隐式转换为指向其第一个元素的指针。因此,p1
被初始化为指向该字符'A'
,即该匿名数组对象的第一个字符。
p1
应定义为const char *p1 = ...
。它实际上并不是必需的,但它将帮助编译器捕获任何修改数组对象的尝试。 (该对象不是const
,但修改它有未定义的行为。)
p1 = "EFG";
现在p1
中存储的值被破坏并被新的指针值替换,这次指向字符串文字'E'
的{{1}}。 (所以初始化没有意义,但那没关系。)
"EFG"
这里我们将printf("%s", p1);
(指针值)的值传递给p1
函数。
此时,printf
的值为*p1
,类型为'E'
。但char
能够打印整个字符串printf
。它使用指针算术来执行此操作。在"EFG"
的实现中的某处,有一个循环,它接受传入的指针值,取消引用它以获取要打印的字符值,然后递增指针使其指向下一个字符字符串(printf
)。此循环继续,直到它到达标记字符串结尾的'F'
空字符;该空字符未打印。
由于阵列在某种意义上是“二等公民”#34;在C中,对数组对象的大多数操作都是这样完成的,使用指向数组元素的指针来遍历数组。 (您也可以使用索引符号'\0'
,这基本上是相同的。)arr[i]
无法直接访问数组对象本身;它依赖于指针(到它的第一个元素)来确定数组在内存中的位置,并依赖于printf
终结符来确定字符串的结束位置。
第二个程序是相同的,除了'\0'
未重新分配,因此打印字符串p1
。
C中的数组和指针之间的关系可能令人困惑。 comp.lang.c FAQ的第6节非常好地解释了它。
答案 5 :(得分:1)
我不知道究竟是
*p1
是什么? 它是一个包含char值的地址吗?它是一个炭?
如果您在声明中谈论*p1
char *p1 = "ABCD";
然后*
表示p1
是指向char
的指针(在这种情况下实际上是const char
)。 p1
只指向字符串文字ABCD
的第一个字符。
如果您在
中询问*p1
printf("%c", *p1);
然后*
这里是间接运算符,*p1
表示字符p1
的值指向A
。 *p1
相当于p1[0]
声明
p1 = "DEF";
让p1
指向字符串文字DEF
的第一个字符。
为什么
const
?
字符串文字存储在内存的只读部分中。
char *p1 = "ABCD";
相当于
char const *p1 = "ABCD";
这意味着您无法修改字符串文字。
*p1 = 'a'; // WRONG. Invokes undefined behavior.
答案 6 :(得分:0)
我不知道的是* p1究竟是什么!它是一个包含char值的地址吗?它是一个炭?现在什么是* p1?
*p1
的类型为char
。它的值是字符'A'
。
为什么是
const
?
字符串文字(如"ABCD"
)放在只读内存中。通过p
修改它们是导致未定义的行为的原因。这就是*p1
只读的原因,即它是const
。
答案 7 :(得分:0)
我不知道的是* p1究竟是什么!它是一个包含char值的地址吗?它是一个炭?现在什么是* p1?
*p1
的值为p1[0]
,即您的第一个程序中值char
的{{1}}和第二个程序中的值'E'
。
为什么是常量?
C中不是'A'
(它在C ++中),但在C中指定字符串文字是不可修改的,这意味着修改const
的尝试会调用未定义的行为。