Article View: comp.std.c
Article #5031Re: Q: char **foo, char *foo[], and char foo[][] ?
From: James Carlson
Date: Thu, 18 Apr 1996 12:05
Date: Thu, 18 Apr 1996 12:05
93 lines
4831 bytes
4831 bytes
In article <4l33ok$oo2@Sherlock.lectra.fr>, phil@rd.lectra.fr (Philippe Maurisset) writes: |> Considering the 3 following declarations: |> |> 1 - char **foo; |> 2 - char *foo[]; |> 3 - char foo[][]; |> [...non-working example deleted...] |> Can anybody help me ? Is it system-dependent ? No, it's not system-dependent, but it is context-specific. There are some rules you can follow. When you define a simple array (all []'s, like foo[3][9]), you can refer to it in prototypes either as fully-dimensioned (all []'s with constant values, like foo[3][9]), or with the first dimension elided (all but first [] with constant, like foo[][9]), or even as a simple pointer (like *foo or foo[]), although the dimensioning will be lost and the elements will appear as a single- dimension array (27 elements in this case). You may not refer to it as "*foo[]", either as an argument declarator or in an "extern" in a separate file. When you create an array of pointers (like *foo[3][2]), you can refer to it in prototypes either as the same type of pointer (*foo[3][2] again), or with the first dimension elided (*foo[][2]), or even as a simple pointer (**foo), though, again, you drop back to single-dimension. The pattern here is that the first set of brackets ([]) translate into one level of "*" in the function argument declaration, but additional levels of brackets do NOT add "*"s to the function argument. Thus, char foo[1000] and char foo[10][10][10] are stored in exactly the same internal format, and can be referred to in a function argument list as foo[1000], foo[], foo[][10][10], foo[10][10][10], or *foo. One more wrinkle to consider is that when declaring a variable, *foo is different from (and incompatible with) foo[], but when passed as an argument, they are identical. Thus, when making "extern" statements to refer to a variable (rather than passing as an argument list), do not follow the above rules, but rather declare variable exactly as originally defined. The only exception to that rule is that you may still elide the first dimension in an extern (like foo[][9]), but, again, you cannot choose to call this "*foo" in the extern. Instead of trying to argue about the abstract types, let's instead look at a hypothetical example on an imaginary machine. char **var1; char *var2[5]; char var3[3][2]; These three declarations could produce the following assembly code for our hypothetical machine (assuming 32-bit architecture, nice, simple system): .bss var1,4 # single char** pointer, 4 bytes .bss var2,20 # five char* pointers, 20 bytes .bss var3,6 # array of 3x2 chars, 6 bytes Note that these are extremely different. The first case declares a single simple pointer, which happens to be a pointer-to-a-pointer. The second declares a contiguous array of 5 pointers, each of which happens to be a pointer-to-a-char. The last example just creates a block of 6 bytes, no pointers. Because the types are different, the compiler knows to generate different code for accessing each case. Let's carry this a little further and fetch var3[2][1], var2[2][1], and var1[2][1]. These look like (two register machine, "ptr" and "acc"): move (var3+5),acc # move offset 2*2+1 into accum. move (var2+8),ptr # move offset 2*4 into pointer move 1(ptr),acc # move offset 1 into accumul. move var3,ptr # fetch var3 (pointer) move 8(ptr),ptr # move offset 2*4 into pointer move 1(ptr),acc # move offset 1 into accumul. In the first case, the compiler uses the "var3" symbol as the location of the beginning of the array, and simply needs to offset out to the 6th location to fetch the desired character. In the second case, the compiler can locate var2[2] immediately; it's the second element in the var2 array (pointers of 4 bytes each), but then it must use the value at that location as a pointer to the actual array of characters, and then fetch offset 1 from there. In the last case, the compiler must fetch the contents of var3, treat it as a pointer to fetch the 2nd pointer value (again 4 bytes each), and then fetch offset 1 from that value. -- James Carlson <carlson@xylogics.com> Tel: +1 617 272 8140 Annex Interface Development / Xylogics, Inc. +1 800 225 3317 53 Third Avenue / Burlington MA 01803-4491 Fax: +1 617 272 2618
Technical Details
Message-ID:
<4l5b68$kg2@newhub.xylogics.com>Path:
arc1-ca.pugleaf.net!xylogics.com!not-for-mailReferences:
<4l33ok$oo2@Sherlock.lectra.fr>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!xylogics.com!not-for-mail From: carlson@xylogics.com (James Carlson) Newsgroups: comp.std.c Subject: Re: Q: char **foo, char *foo[], and char foo[][] ? Date: 18 Apr 1996 08:05:28 -0400 Organization: Xylogics Incorporated Distribution: world Message-ID: <4l5b68$kg2@newhub.xylogics.com> References: <4l33ok$oo2@Sherlock.lectra.fr> Reply-To: carlson@xylogics.com NNTP-Posting-Host: newhub.xylogics.com