关于数组:关于C中指针和字符串的问题

Question about pointers and strings in C

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
What is the difference between char s[] and char *s in C?
Difference between char *str ="…" and char str[N] ="…"?

我有一些令我困惑的代码。

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  char* string1 ="this is a test";
  char string2[] ="this is a test";
  printf("%i, %i
"
, sizeof(string1), sizeof(string2));
  system("PAUSE");
  return 0;
}

当它输出string1的大小时,它将打印4,这是预期的,因为指针的大小是4个字节。但当它打印String2时,输出15。我以为数组是指针,所以string2的大小应该和string1相同,对吗?那么,为什么它会为同一类型的数据(指针)打印出两个不同的大小呢?


数组不是指针。数组名在某些情况下会衰减为指向数组第一个元素的指针:当你将它传递给一个函数时,当你将它赋给一个指针时,等等。但其他情况下,数组是数组-它们存在于堆栈中,有编译时大小可以用sizeof和所有其他好东西来确定。


阵列和指针是完全不同的动物。在大多数情况下,指定数组的表达式被视为指针。

首先,一点标准语言(N1256):

6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type"array of type" is converted to an expression with type"pointer to type" that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

字符串文本"this is a test"是char的15元素数组。在声明中

1
    char *string1 ="this is a test";

string1被声明为指向char的指针。根据上述语言,表达式"this is a test"的类型从char [15]转换为char *,结果指针值分配给string1。在声明中

1
    char string2[] ="this is a test";

发生了一些不同的事情。更多标准语言:

6.7.8 Initialization
...
14 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.
...
22 If an array of unknown size is initialized, its size is determined by the largest indexed element with an explicit initializer. At the end of its initializer list, the array no longer has incomplete type.

在这种情况下,string2被声明为char的数组,它的大小是根据初始值设定项的长度计算的,字符串文字的内容被复制到数组中。

下面是一个假设性的记忆地图来说明发生了什么:

1
2
3
4
5
6
7
8
9
10
11
12
Item          Address       0x00  0x01  0x02  0x03
----          -------       ----  ----  ----  ----
no name       0x08001230    't'   'h'   'i'   's'
              0x08001234    ' '   'i'   's'   ' '
              0x08001238    'a'   ' '   't'   'e'
              0x0800123C    's'   't'    0
              ...
string1       0x12340000    0x08  0x00  0x12  0x30
string2       0x12340004    't'   'h'   'i'   's'
              0x12340008    ' '   'i'   's'   ' '
              0x1234000C    'a'   ' '   't'   'e'
              0x1234000F    's'   't'    0

字符串文本具有静态范围;也就是说,在程序启动时为它们留出内存,并一直保留到程序终止。试图修改字符串文字的内容会调用未定义的行为;基础平台可能允许也可能不允许,并且标准对编译器没有任何限制。最好表现得好像文字总是不可写的。

在上面的内存映射中,字符串文字的地址与string1string2的地址有所不同,以说明这一点。

无论如何,您可以看到,具有指针类型的string1包含字符串文本的地址。string2是数组类型,包含字符串文本内容的副本。

由于string2的大小在编译时已知,因此sizeof返回数组中的大小(字节数)。

%i转换说明符不适合用于size_t类型的表达式。如果你在c99工作,使用%zu。在c89中,您将使用%lu并将表达式强制转换为unsigned long

1
2
3
4
C89: printf("%lu, %lu
"
, (unsigned long) sizeof string1, (unsigned long) sizeof string2);
C99: printf("%zu, %zu
"
, sizeof string1, sizeof string2);

注意,sizeof是一个运算符,而不是函数调用;当操作数是一个表示对象的表达式时,无需括号(尽管它们不会造成伤害)。


未知大小的数组等价于用于size of目的的指针。静态大小的数组作为其自身的类型来计算size of用途,size of报告数组所需的存储大小。尽管string2的分配没有显式的大小,但C编译器会神奇地处理它,因为它是由带引号的字符串直接初始化的,并将其转换为具有静态大小的数组。(因为内存没有以任何其他方式分配,所以它也不能做其他任何事情。)静态大小数组与指针(或动态数组)的类型不同。对于sizeof行为,因为这就是c的方式。

这似乎是对sizeof行为的一个很好的参考。


string1是指针,但string2是数组。

第二行类似于int a[] = { 1, 2, 3};,它将a定义为长度为3的数组(通过初始值设定项)。

string2的大小为15,因为初始值设定项是以nul结尾的(所以15是字符串的长度+1)。


编译器知道test2是一个数组,所以它输出分配给它的字节数(14个字母加上空终止符)。记住,sizeof是一个编译器函数,因此它可以知道堆栈变量的大小。


其因为

  • string1保存指针,其中指针具有连续字符&its不变的
  • 字符串2是字符所在的位置。
  • 基本上,C编译器iterprets这2个不同。这里很好地解释了http://c-faq.com/aryptr/aryptr 2.html。


    数组不是指针。指针是指向内存位置的变量,而数组是分配顺序内存的起始点。