2

I recently learn the C language. I read that there is some ways to create an array of string. Is there any difference between char argv[][7] and char *argv[]? If there is not what do you prefer to use?

juanchopanza
  • 210,243
  • 27
  • 363
  • 452
Bryan K.
  • 143
  • 1
  • 4
  • 14

4 Answers4

5

Quoting from the C99 standard §6.2.5 ¶20 (Types)

An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. Array types are characterized by their element type and by the number of elements in the array.

The standard further says in §6.2.5 ¶22

An array type of unknown size is an incomplete type. It is completed, for an identifier of that type, by specifying the size in a later declaration (with internal or external linkage).

The array subscript operator [] has higher precedence than the * operator. Therefore, the statement

char *argv[];

defines argv to be an array of pointer to characters of unknown size since the array size is not specified. The array argv is an incomplete type. This is assuming that the expression in the above statement does not appear as a function parameter. Since the array argv is incomplete type, you must provide its size information before using it. This means you should make the above statement a declaration and provide its definition someplace else so that the linker will resolve it. Read this for the difference between declaration and definition -

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];

You can also initialize the array with an array initializer list and the size of the array will be inferred from the list.

// 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";

Note: the above is just for demonstrating the array initialization without explicitly mentioning its size. String literal are read-only so the statement should better be written as

const char *argv[] = {"Hello", "World"};

If it appears as a function parameter however, then it's equivalent to char **argv as in

int main(int argc; char *argv[]);
// equivalent to
int main(int argc, char **argv);

Same goes for the array in the below statement.

char argv[][7];

The above statement defines argv to be an array of elements of type char[7], i.e., an array of 7 characters. The size of the array argv is, again, not specified. Therefore, argv is an incomplete type. Assuming it does not appear as a function parameter, the statement should be made into a declaration because it's an incomplete type and its definition should be provided elsewhere.

// 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];

The array can be initialized as in the previous case and the size will be determined implicitly from the initializer list.

// 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'}};

However, if the array expression appears as a function parameter, then it is equivalent to
char (*)[7], i.e., a pointer to an array of 7 characters.

void foo(char argv[][7]);
// equivalent to
void foo(char (*)[7])

This is because you cannot pass an array to a function. What actually gets passed is a pointer to the first element of array. Therefore, the array parameter in the function is implicitly converted to pointer to array element type. Please read this for more detail -

Why do C and C++ compilers allow array lengths in function signatures when they're never enforced?

Community
  • 1
  • 1
ajay
  • 8,550
  • 7
  • 34
  • 67
  • 2
    You should add, that this syntax has meaning when initialized at time of declaration. – Grijesh Chauhan Apr 21 '14 at 13:20
  • 1
    To clarify, `char argv[][7];` at file scope is a *tentative definition* and it has incomplete type. At block scope it is a constraint violation. – M.M Apr 21 '14 at 13:46
  • And the *declaration* `char *argv[];` (declarations are not statements in C, although they are in C++) is the same; it's invalid at block scope, and a tentative definition at file scope. – M.M Apr 21 '14 at 13:47
  • @MattMcNabb thanks. Updated my answer. Also, linked to your answer which is very much useful here. :) – ajay Apr 21 '14 at 13:56
  • 2
    @MattMcNabb: the question is tagged both C and C++. tentative definitions are a C feature, not present in C++ (see C++11 appendix §C.1.2, first para). one could perhaps clearly diffentiate between the languages in each case, but as I see it the question is to blame... – Cheers and hth. - Alf Apr 21 '14 at 14:25
2

Try entering these at cdecl.org, and you will see

char argv[][7] :  declare argv as array of array 7 of char
char *argv[]   :  declare argv as array of pointer to char

In other words, they are simply not the same thing. The first is an array of fixed-size character arrays (and other than you might expect, not necessarily nul-terminated!), whereas the other is an array to pointer-to-char, which also works as "C style string", that is a variable-length nul-terminated character sequence (the intent that is commonly behind argv).

Damon
  • 61,669
  • 16
  • 122
  • 172
0

Firstly , it matters where this code appears. Actually these two declarators have different meanings in all 3 of the following scopes: function parameter list, block scope, and file scope.

In the function formal parameter list, char argv[][7] means that argv is a pointer to an array of 7 chars. char *argv[] means that argv is an array of pointers.

If we are taking about main(), then char argv[][7] is non-portable in that context and will probably cause a runtime error.

At block scope (i.e. inside a function), or file scope (global variables), we should not call things argv as that name normally is used for the parameter of main(), it is poor style to use it elsewhere with different semantics. We might have:

char foo[][7] = { "the", "quick", "brown" };
char *bar[] = { "the", "quick", "brown" };

Both of these are legal. In foo, the chars all live inside the storage foo, and are modifidiable. In bar, the chars are not writable. bar is an array of 3 pointers into the string table (in common implementations, anyway).

BTW you should write char const * for type safety in the second one.

foo wastes some memory because the strings are not always 7 chars long, but 7 chars are allocated for each array entry. bar has an added level of indirection, but probably uses up less memory.

Finally, I'm not sure if you are also asking about the declarations:

char foo[][7];
char *bar[];

without initializers. If so, then these are illegal at block scope. In C++ they are illegal at file scope. In C, at file scope they are tentative definitions` which means that they declare an incomplete array type, and you should complete the type later in the file (probably by using one of the initialization examples we already discussed). This is a feature of C that is rarely useful.

M.M
  • 130,300
  • 18
  • 171
  • 314
0

If your doubt is the basic difference between these declarations, then,

char argv[][7];

is a 2-D array of characters with 7 columns. Defining the number of rows in not necessary.

char *argv[];

is an array of pointers to characters.

If you are confusing this with argument count and argument value,

int main(int argc, char *argv[])

Then please refer to

http://www.cprogramming.com/tutorial/c/lesson14.html

What does int argc, char *argv[] mean?

Community
  • 1
  • 1
nishantbhardwaj2002
  • 737
  • 1
  • 5
  • 18