我试图理解C中的指针,但我目前对以下内容感到困惑:
Char *p = "hello" 这是一个指向字符数组的char指针,从h开始。 Char p[] = "hello" 这是一个存储hello的数组。
当我把这两个变量都传递给这个函数时,有什么不同?
void printSomething(char *p)
{
printf("p: %s",p);
}
我试图理解C中的指针,但我目前对以下内容感到困惑:
Char *p = "hello" 这是一个指向字符数组的char指针,从h开始。 Char p[] = "hello" 这是一个存储hello的数组。
当我把这两个变量都传递给这个函数时,有什么不同?
void printSomething(char *p)
{
printf("p: %s",p);
}
Char *和Char[]是不同的类型,但并不是在所有情况下都能立即显示出来。这是因为数组衰减为指针,这意味着如果在需要char*类型的表达式的地方提供了char[]类型的表达式,编译器会自动将数组转换为指向其第一个元素的指针。
你的例子函数printSomething需要一个指针,所以如果你试图像这样传递一个数组给它:
char s[10] = "hello";
printSomething(s);
编译器会假装是你写的:
char s[10] = "hello";
printSomething(&s[0]);
Char p[3] = "hello" ?应该是char p[6] = "hello"记住在C语言的"string"结尾有一个'\0'字符。
不管怎样,数组在C语言中只是指向内存中调整对象的第一个对象的指针。唯一不同的是语义。虽然可以将指针的值更改为指向内存中的不同位置,但创建后的数组将始终指向相同的位置。 此外,当使用数组时,“新建”和“删除”会自动为你完成。
对于这样的情况,效果是相同的:您最终传递字符串中第一个字符的地址。
声明显然是不一样的。
下面的代码为字符串和字符指针预留内存,然后将指针初始化为指向字符串中的第一个字符。
char *p = "hello";
而下面的方法只为字符串预留内存。所以它实际上可以使用更少的内存。
char p[10] = "hello";
让我们来看看:
#include <stdio.h>
#include <string.h>
int main()
{
char *p = "hello";
char q[] = "hello"; // no need to count this
printf("%zu\n", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64
printf("%zu\n", sizeof(q)); // => size of char array in memory -- 6 on both
// size_t strlen(const char *s) and we don't get any warnings here:
printf("%zu\n", strlen(p)); // => 5
printf("%zu\n", strlen(q)); // => 5
return 0;
}
Foo *和Foo[]是不同的类型,编译器对它们的处理也不同(pointer =地址+指针类型的表示,array =指针+数组的可选长度,如果已知,例如,如果数组是静态分配的),详细信息可以在标准中找到。在运行时级别上,它们之间没有区别(在汇编程序中,好吧,几乎没有区别,请参阅下面)。
此外,在C常见问题解答中有一个相关的问题:
Q: What is the difference between these initializations? char a[] = "string literal"; char *p = "string literal"; My program crashes if I try to assign a new value to p[i]. A: A string literal (the formal term for a double-quoted string in C source) can be used in two slightly different ways: As the initializer for an array of char, as in the declaration of char a[] , it specifies the initial values of the characters in that array (and, if necessary, its size). Anywhere else, it turns into an unnamed, static array of characters, and this unnamed array may be stored in read-only memory, and which therefore cannot necessarily be modified. In an expression context, the array is converted at once to a pointer, as usual (see section 6), so the second declaration initializes p to point to the unnamed array's first element. Some compilers have a switch controlling whether string literals are writable or not (for compiling old code), and some may have options to cause string literals to be formally treated as arrays of const char (for better error catching). See also questions 1.31, 6.1, 6.2, 6.8, and 11.8b. References: K&R2 Sec. 5.5 p. 104 ISO Sec. 6.1.4, Sec. 6.5.7 Rationale Sec. 3.1.4 H&S Sec. 2.7.4 pp. 31-2
在C语言中,char数组和char指针的区别是什么?
c99n1256草案
字符串字面量有两种不同的用法:
Initialize char[]: char c[] = "abc"; This is "more magic", and described at 6.7.8/14 "Initialization": An array of character type may be initialized by a character string literal, optionally enclosed in braces. Successive characters of the character string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array. So this is just a shortcut for: char c[] = {'a', 'b', 'c', '\0'}; Like any other regular array, c can be modified. Everywhere else: it generates an: unnamed array of char What is the type of string literals in C and C++? with static storage that gives UB (undefined behavior) if modified So when you write: char *c = "abc"; This is similar to: /* __unnamed is magic because modifying it gives UB. */ static char __unnamed[] = "abc"; char *c = __unnamed; Note the implicit cast from char[] to char *, which is always legal. Then if you modify c[0], you also modify __unnamed, which is UB. This is documented at 6.4.5 "String literals": 5 In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals. The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence. For character string literals, the array elements have type char, and are initialized with the individual bytes of the multibyte character sequence [...] 6 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.
6.7.8/32“初始化”给出了一个直接的例子:
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.
GCC 4.8 x86-64 ELF实现
计划:
#include <stdio.h>
int main(void) {
char *s = "abc";
printf("%s\n", s);
return 0;
}
编译和反编译:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
输出包含:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
结论:GCC将char* it存储在.rodata部分,而不是在.text中。
如果我们对char[]做同样的操作:
char s[] = "abc";
我们获得:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
因此它被存储在堆栈中(相对于%rbp)。
但是请注意,默认的链接器脚本将.rodata和.text放在同一个段中,该段有执行权限,但没有写权限。这可以观察到:
readelf -l a.out
它包含:
Section to Segment mapping:
Segment Sections...
02 .text .rodata
APUE第5.14节:
char good_template[] = "/tmp/dirXXXXXX"; /* right way */
char *bad_template = "/tmp/dirXXXXXX"; /* wrong way*/
... 对于第一个模板,名称分配在堆栈上,因为我们使用 数组变量。然而,对于第二个名称,我们使用指针。在这种情况下,只有 指针本身的内存位于堆栈上;编译器将字符串安排为 存储在可执行文件的只读段中。当mkstemp函数尝试时 若要修改字符串,则会发生分段错误。
引用的文字与@Ciro Santilli的解释相符。