我正在尝试在C语言中创建字符串数组。如果我使用以下代码:
char (*a[2])[14];
a[0]="blah";
a[1]="hmm";
gcc 给我报错 "warning: assignment from incompatible pointer type"。请问正确的做法是什么?
编辑:我很好奇为什么这会引起编译警告,因为如果我执行 printf(a[1]);
,它会正确地打印出"hmm"。
如果您不想更改字符串,那么可以简单地执行以下操作
const char *a[2];
a[0] = "blah";
a[1] = "hmm";
如果您按此方式操作,您将分配一个包含两个指向const char
的指针的数组。然后,这些指针将被设置为静态字符串"blah"
和"hmm"
的地址。
如果您确实想要更改实际的字符串内容,则必须执行类似以下的操作:
char a[2][14];
strcpy(a[0], "blah");
strcpy(a[1], "hmm");
这将分配两个连续的每个包含14个 char
的数组,然后将静态字符串的内容复制到它们中。
C语言中有几种创建字符串数组的方法。如果所有字符串的长度都相同(或者至少具有相同的最大长度),您可以简单地声明一个char类型的二维数组,并根据需要进行赋值:
char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1];
...
strcpy(strs[0], aString); // where aString is either an array or pointer to char
strcpy(strs[1], "foo");
你也可以添加一个初始化器列表:
char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1] = {"foo", "bar", "bletch", ...};
假设在初始化程序中,字符串大小和数量与您的数组维数匹配。在这种情况下,将每个字符串文字(它本身是一个以零结尾的char数组)的内容复制到分配给strs的内存中。此方法的问题是可能存在内部碎片; 如果您有99个字符串长度为5个字符或更少,但有1个字符串长度为20个字符,那么这99个字符串将至少有15个未使用的字符;这是一种浪费空间的方式。
相反,您可以使用一个指向char的1-d数组来存储而非char的2-d数组:
char *strs[NUMBER_OF_STRINGS];
请注意,在这种情况下,你仅仅分配了指向字符串的指针的内存空间;字符串本身的内存空间必须在其他地方分配(可以是静态数组或使用malloc()
或calloc()
函数)。你可以像之前的示例一样使用初始化列表:
char *strs[NUMBER_OF_STRINGS] = {"foo", "bar", "bletch", ...};
你并没有复制字符串常量的内容,而是简单地存储了指向它们的指针。请注意,字符串常量可能是不可写的;你可以像这样重新分配指针:
strs[i] = "bar";
strs[i] = "foo";
但是您可能无法更改字符串的内容;即
strs[i] = "bar";
strcpy(strs[i], "foo");
可能会被禁止。
您可以使用 malloc()
动态为每个字符串分配缓冲区,并将其复制到该缓冲区中:
strs[i] = malloc(strlen("foo") + 1);
strcpy(strs[i], "foo");
顺便说一句,
char (*a[2])[14];
声明a为一个指向14元素字符数组的2个元素指针的数组。
malloc
调用的结果。 - John BodeT [N]
的表达式将被转换为类型为T *
的表达式,并且表达式的值是第一个元素的地址。因此,如果您编写了str = "foo"
,您将尝试将字符串"foo"
的第一个字符的地址分配给数组str
,但这是行不通的。有关详细信息,请参见this answer。 - John Bodechar *strs[NUMBER_OF_STRINGS] = {0};
这可以通过将strs
初始化为NULL
来帮助预防未来的问题。很多人在搜索C语言中的字符串数组时会阅读这篇文章。 - cokedudechar strs[NUMBER_OF_STRINGS][STRING_LENGTH+1] = {"foo", "bar", "bletch", ...};
可以改为 char strs[NUMBER_OF_STRINGS][MAX_STRING_LENGTH+1] = {"foo", "bar", "bletch", ...};
。 - 71GAstrlcpy
,除非您百分之百确定不需要它。 - Thomas Tempelmann以下是您的一些选择:
char a1[][14] = { "blah", "hmm" };
char* a2[] = { "blah", "hmm" };
char (*a3[])[] = { &"blah", &"hmm" }; // only since you brought up the syntax -
printf(a1[0]); // prints blah
printf(a2[0]); // prints blah
printf(*a3[0]); // prints blah
a2
的优势在于您可以对字符串字面量执行以下操作
a2[0] = "hmm";
a2[1] = "blah";
对于a3
,您可以执行以下操作:
a3[0] = &"hmm";
a3[1] = &"blah";
对于变量a1
,即使在赋值字符串常量时,你也必须使用strcpy()
(最好是strncpy()
)。原因是a2
和a3
是指针数组,你可以使它们的元素(即指针)指向任何存储空间,而a1
是一个“字符数组的数组”,所以每个元素都是一个拥有自己存储空间的数组(这意味着当它超出范围时会被销毁),你只能将内容复制到它的存储空间中。a2
和a3
的缺点 - 因为它们指向静态存储区域(即字符串常量存储的位置),其内容不能可靠地更改(即未定义的行为),如果你想要将非字符串常量分配给a2
或a3
的元素,则必须首先动态分配足够的内存,然后将它们的元素指向该内存,并将字符复制到其中-完成后,你必须确保释放内存。
p.s. 如果需要示例,请告诉我。如果您不想跟踪数组中字符串的数量并希望对它们进行迭代,只需在末尾添加NULL字符串:
char *strings[]={ "one", "two", "three", NULL };
int i=0;
while(strings[i]) {
printf("%s\n", strings[i]);
//do something
i++;
};
或者你可以声明一个结构体类型,其中包含一个字符数组(1个字符串),然后创建一个结构体数组,从而创建一个多元素数组。
typedef struct name
{
char name[100]; // 100 character array
}name;
main()
{
name yourString[10]; // 10 strings
printf("Enter something\n:);
scanf("%s",yourString[0].name);
scanf("%s",yourString[1].name);
// maybe put a for loop and a few print ststements to simplify code
// this is just for example
}
相对于其他任何方法,这种方法的优点之一是它允许您直接扫描到字符串中而无需使用 strcpy
;
如果这些字符串是静态的,最好使用:
const char *my_array[] = {"eenie","meenie","miney"};
虽然不是ANSI C的基本部分,但很可能您的环境支持这种语法。 这些字符串是不可变的(只读),因此在许多环境中,它们使用的开销比动态构建字符串数组要少。
例如,在小型微控制器项目中,这种语法使用程序存储器而不是(通常更宝贵的)RAM存储器。 AVR-C是支持此语法的示例环境,但大多数其他环境也是如此。
在 ANSI C 中:
char* strings[3];
strings[0] = "foo";
strings[1] = "bar";
strings[2] = "baz";
char* foo, bar;
中的 bar
是什么类型? - mASOUDconst char *
类型。const char *a[2] = {"blah", "hmm"};
这个代码声明了一个包含两个指向常量字符的指针数组,并将它们初始化为指向两个硬编码的字符串常量。
char (*a[2])[14]
是一个包含两个指向包含14个字符的数组的指针数组。 - avakarchar (*a[2])[14]
- 从a
开始,向右移动:“两个元素的数组”,向左移动:“指向”的,括号结束了,所以继续向右读取:“长度为14的数组”,向左读取:“char”... 把它们组合起来,我们可以得出“a是一个指向两个长度为14的数组的指针数组,这些数组中每个元素都是char类型”。 - Mark K Cowanchar *str
而不是char* str
。我来自Delphi/Pascal背景,习惯于后一种方式,直到我遇到了更复杂的类型。前一种方式仍然看起来很丑陋,但可以使类型标注更加一致(在我看来)。 - Mark K Cowan