#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[]) {
char str[] = "hello";
printf("%s, %p", str, str);
return 0;
}
上面的代码给出了输出
你好,0x7fff5fbff7aa
令我困惑的是,为什么str
可以同时成为字符串和指针?
我知道字符串是指向char
的指针。所以我认为str
只是pointer
。
但编译器如何知道%s
给出了str
指向的字符串?
编译器是如何工作的?
P.S
我认为,当我们将%c
和%i
用于char
变量并获得不同的输出时,情况会发生同样的情况。
答案 0 :(得分:4)
在此代码中,str
是一个数组。数组和指针是不同的。您可以创建一个指向数组元素的指针。
在代码printf("%s, %p", str, str);
中,str
的两个用法实际上都请求指向数组第一个元素的指针。你可以写&str[0]
来表示同样的事情,但是从C开始就是一个设计决定,在大多数情况下写一个数组的名称实际上会请求这样的指针。
定义printf
函数,如果它看到%s
,则它跟随(取消引用)相应的指针并打印出字符,直到它到达空终止符。如果它看到%p
,那么它会打印出指针本身的某种表示(而不是指针指向的那种)。
答案 1 :(得分:2)
在
char str[] = "hello";
如果您将str
视为标识符,则它是一个字符数组
数组和指针的行为不同,例如,
sizeof(array);
// would give you the sizeof(type of array)*total elements in array
sizeof(pointer);
// would give you just the size of the pointer in your system , say 8 bytes
但是当将函数 decays to pointer
传递给数组的第一个元素时的数组,如
printf("%s, %p", str, str);
// same as printf("%s, %p", &str[0], &str[0]);
这里str
,虽然它是一个数组被认为是指向数组第一个元素的指针,即&str[0]
。
你得到的结果不同只是因为你使用了不同的格式说明符,即%s
&amp; %p
分别决定如何打印内容。
答案 2 :(得分:0)
printf
函数用于使用相同的标识符str
打印不同的值。
%s
和%p
是格式说明符。对于%s
,printf
将从str
给出的地址开始打印字符串。对于%p
,printf
将打印str
给出的内存地址。
编译器在这里没有特殊的作用。但是,如果格式说明符和相应的参数之间存在类型不匹配,它可以警告您。例如,如果您执行printf("%s",10);
,则可以警告这种不匹配,表示printf
期待char *
- %s
- 但您传递的是int
。
PS :注意str
是char数组 - 它与char指针不同 - 但是当你将它传递给函数时,它变成了一个char指针({{1指向其第一个元素的地址。
答案 3 :(得分:0)
这是一个新的C程序员很难与什么是数组交朋友?和什么是指针?以及如何数组<当数组作为函数调用中的参数传递时,/ em>转换为指针。通过了解适用的一些简单的fules,可以很容易地理解所有这些。
首先,任何变量(例如a = 5;
)其中a
包含 int
的地址以及构成该地址的字节数内存保存5
的立即值。因此,当您分配a = 5;
时,您将变量标签a
指向的内存设置为5
的值。这里的关键是,从这个意义上讲,所有变量都可能指向内存中的某个位置。这里的区别在于正常变量指向的内存,正常变量指向的内存包含一些立即值(5
这里),而在这种情况下指针,指针变量指向memmory中的地址,其中可以找到其他地址。 (例如,指针只是一个指向某些类型的地址的变量,而不是某些值)。
经典的例子值得再看一遍
int a = 5; /* 'a' stores a memory address holding the value '5' as its value */
int *pa = &a; /* pointer-to-a 'pa' stores the memory address of 'a' as its value */
应用于任何数组,数组的第一个元素是整个数组的起始地址。你可以这样想:
int a[] = { 1, 2, 3, 4 }; /* where &a[0] is the memory address for the array,
a[i] = *(a + i), thus &(*(a + 0)), is simply 'a' */
所以在char str[] = "hello";
的情况下,字符数组的第一个元素(根据它的初始化方式(例如char array[] = "stuff";
)将包含地址在初始化程序的开始和结束"
之间给出的 nul-termminated 字符数组的第一个字符。
数组的第一个元素位于整个数组的地址,可由array[0]
引用,正如我们上面所见,它等同于{{1} }或仅*(array + 0)
,然后使用 urnary *array
运算符获取 &
array
的地址只是&(*array)
。
这就是为什么您可以将array
作为str
本身(第一个元素的地址)传递给格式说明符的原因array
打印为字符串,以及为什么您可以将%s
传递给格式说明符 str
来打印指针地址。
希望你在这一点上回答你的问题,但是......
这也是理解当字符数组 %p
作为函数参数列表str
(或任何数组类型)中的参数传递时的原因作为指针传递(即,当作为参数传递给函数时,任何数组中的第一级间接是转换到指针)数组作为指针传递的历史原因只需要做节省记忆。
答案 4 :(得分:0)
在C中,字符串是一系列字符值,后跟一个0值终结符。例如,字符序列{'H', 'e', 'l', 'l', 'o', 0}
是一个字符串,但{'H', 'e', 'l', 'l', 'o'}
不是 - 0终结符有所不同。
字符串(包括字符串文字)存储作为char
的数组。鉴于声明
char str[] = "Hello";
你会得到像
这样的东西 +---+
str: |'H'| str[0]
+---+
|'e'| str[1]
+---+
|'l'| str[2]
+---+
|'l'| str[3]
+---+
|'o'| str[4]
+---+
| 0 | str[5]
+---+
在记忆中。请注意,没有为指向数组第一个元素的指针留出存储空间。
在大多数情况下,表达式类型为&#34; N元素数组T
&#34;将被转换(&#34;衰变&#34;)到类型为#34的表达式;指向T
&#34;并且表达式的值将是数组的第一个元素的地址。此规则的例外情况是,数组表达式是sizeof
或一元&
运算符的操作数,或者表达式是用于在声明中初始化数组的字符串文字。
所以,让我们采取以下代码:
char str[] = "Hello";
char *ptr = "World";
printf( "%s, %s\n", str, ptr );
字符串文字 "Hello"
,"World"
和"%s, %s\n"
存储为char
的数组,以便在程序启动时分配它们在程序的整个生命周期内可用。
"Hello"
,"World"
,"%s, %s\n"
和str
都是数组表达式(它们都有类型&#34; N元素数组char
&#34;)。在ptr
的声明中,"World"
数组表达式不是sizeof
或一元&
运算符的操作数,也不用于初始化{{1}的数组1}},所以表达式被转换(&#34;衰减&#34;)到#34;指向char
&#34;的指针,表达式的值是第一个元素的地址对于数组,所以char
最终会指向ptr
的第一个字符。
同样,在"World"
调用中,数组表达式printf
和"%s, %s\n"
不是str
或一元sizeof
运算符的操作数,因此它们是也转换为指针表达式,这些指针值实际上是传递给&
的。
但是,在printf
的声明中,str
字符串文字 用于初始化"Hello"
数组,因此它是不转换为指针表达式;相反,char
使用字符串文字的内容进行初始化,其大小也由文字的大小决定。
这是我在系统上生成的代码的具体内存映射:
str
字符串文字 Item Address 00 01 02 03
---- ------- -- -- -- --
"Hello" 0x400b91 48 65 6c 6c Hell
0x400b95 6f 00 30 30 o.00
"World" 0x400b60 57 6f 72 6c Worl
0x400b64 64 00 25 73 d.%s
"%s, %s\n" 0x400b66 25 73 2c 20 %s,.
0x400b6a 25 73 0a 00 %s..
str 0x7fff7cec1a50 48 65 6c 6c Hell
0x7fff7cec1a54 6f 00 00 00 o...
ptr 0x7fff7cec1a48 60 0b 40 00 `.@.
0x7fff7cec1a4c 00 00 00 00 ....
从地址"Hello"
开始存储,0x400b91
从地址"World"
开始存储,格式字符串0x400b60
开始存储在地址"%s, %s\n"
(无论出于何种原因,编译器将0x400b66
和"World"
放在彼此旁边。
数组"%s, %s\n"
从地址str
开始存储,它包含字符串文字0x7fff7cec1a50
内容的副本。指针"Hello"
从地址ptr
开始存储,并包含字符串文字0x7fff7cec1a48
的地址(x86存储多字节值,如little-endian中的指针订购)。
"World"
来电将收到指针值printf
,0x400b66
和0x7fff7cec1a50
。格式字符串中的0x7fff7cec1a48
转换说明符表示&#34;打印从地址开始的字符序列并继续,直到我看到0终结符&#34;。