我最近学习了C语言。我读到有一些方法可以创建一个字符串数组。 char argv[][7]
和char *argv[]
之间有什么区别吗?如果没有你喜欢用的东西?
答案 0 :(得分:5)
引用C99标准§6.2.5¶20(类型)
数组类型描述了连续分配的非空集 具有特定成员对象类型的对象,称为元素类型。 数组类型的特征在于元素类型和数字 数组中的元素。
该标准在§6.2.5¶22
中进一步说明未知大小的数组类型是不完整类型。它完成了, 对于该类型的标识符,通过在稍后指定大小 声明(内部或外部联系)。
数组下标运算符[]
的优先级高于*
运算符。因此,声明
char *argv[];
将argv
定义为指向未知大小字符的指针数组,因为未指定数组大小。数组argv
是不完整的类型。这假设上述语句中的表达式不会显示为函数参数。由于数组argv
是不完整类型,因此在使用之前必须提供其大小信息。这意味着您应该将上述语句作为声明并在其他地方提供其定义,以便链接器解析它。阅读本文,了解声明和定义之间的区别 -
What is the difference between a definition and a declaration?
// array declaration.
// this does not allocate space
// but only provides type information
// though of an incomplete type.
// argv must have internal or external linkage.
extern char *argv[];
// definition of the array.
// complete information and
// allocates memory for it.
// in the same translation unit or
// a different one.
char *argv[8];
您还可以使用数组初始值设定项列表初始化数组,并从列表中推断出数组的大小。
// size of the array argv is determined
// explicitly to be 2
char *argv[] = {"Hello", "World"};
// the above is equivalent to
char *argv[2];
argv[0] = "Hello";
argv[1] = "World";
注意:以上内容仅用于演示阵列初始化而不明确提及其大小。字符串文字是只读的,因此最好将该语句写为
const char *argv[] = {"Hello", "World"};
如果它显示为函数参数,则它等同于<{p>}中的char **argv
int main(int argc; char *argv[]);
// equivalent to
int main(int argc, char **argv);
以下声明中的数组也是如此。
char argv[][7];
上述语句将argv
定义为char[7]
类型的元素数组,即7
个字符数组。再次,未指定数组argv
的大小。因此,argv
是不完整的类型。假设它不作为函数参数出现,则应将该语句作为声明,因为它是一个不完整的类型,其定义应该在别处提供。
// array declaration.
// argv must have internal or external linkage
extern char argv[][7];
// definition.
// in the same translation unit
// or a different one
char argv[10][7];
可以像前一种情况一样初始化数组,并且将从初始化列表中隐式确定大小。
// size of the array argv is inferred from
// the initializer list to be 3.
char argv[][7] = {{'a', 'b', 'c', 'd', 'e', 'f', 'g'},
{'a', 'b', 'c', 'd', 'e', 'f', 'g'},
{'a', 'b', 'c', 'd', 'e', 'f', 'g'}};
但是,如果数组表达式显示为函数参数,则它等效于
char (*)[7]
,即指向7
个字符数组的指针。
void foo(char argv[][7]);
// equivalent to
void foo(char (*)[7])
这是因为您无法将数组传递给函数。实际传递的是指向数组第一个元素的指针。因此,函数中的数组参数隐式转换为pointer to array element
类型。请阅读此内容以获取更多详细信息 -
Why do C and C++ compilers allow array lengths in function signatures when they're never enforced?
答案 1 :(得分:2)
尝试在cdecl.org输入这些内容,您会看到
char argv[][7] : declare argv as array of array 7 of char
char *argv[] : declare argv as array of pointer to char
换句话说,它们根本不是一回事。第一个是固定大小的字符数组(除了你可能期望的,不一定是nul-terminated!),而另一个是指向char的数组,它也可以作为“C样式字符串”,这是一个可变长度的nul终止字符序列(通常在argv
后面的意图)。
答案 2 :(得分:0)
首先,这个代码出现的地方很重要。实际上,这两个声明符在以下所有3个作用域中具有不同的含义:函数参数列表,块作用域和文件作用域。
在函数形式参数列表中,char argv[][7]
表示argv
是指向7个字符数组的指针。 char *argv[]
表示argv
是一个指针数组。
如果我们正在考虑main()
,那么char argv[][7]
在该上下文中是不可移植的,并且可能会导致运行时错误。
在块范围(即函数内部)或文件范围(全局变量)中,我们不应该调用argv
这个名称通常是用于main()
的参数,在其他地方使用不同语义的风格很差。我们可能有:
char foo[][7] = { "the", "quick", "brown" };
char *bar[] = { "the", "quick", "brown" };
这两个都是合法的。在foo
中,字符都存储在存储foo
内,并且可以修改。在bar
中,字符不可写。 bar
是一个包含3个字符串表的数组(无论如何,在常见的实现中)。
顺便说一句,你应该在第二个中写出char const *
类型安全。
foo
浪费了一些内存,因为字符串并不总是7个字符长,但是为每个数组条目分配了7个字符。 bar
具有更高级别的间接性,但可能耗尽更少的内存。
最后,我不确定你是否也在询问声明:
char foo[][7];
char *bar[];
没有初始化程序。如果是这样,那么这些在块范围内是非法的。在C ++中,它们在文件范围内是非法的。在C中,在文件范围内它们是暂定定义`这意味着它们声明了一个不完整的数组类型,你应该在文件的后面完成该类型(可能使用我们已经讨论过的一个初始化示例) )。这是C的一个很少有用的功能。
答案 3 :(得分:0)
如果您的疑问是这些声明之间的基本区别,那么,
char argv[][7];
是一个包含7列的2-D字符数组。定义不必要的行数。
char *argv[];
是一个指向字符的指针数组。
如果您将此与参数计数和参数值混淆,
int main(int argc, char *argv[])
然后请参阅