简体   繁体   中英

about the expression “&anArray” in c

First, I read that:

  • array
  • &array
  • &array[0]

will all be the same as long as "array" is really an array. So I tried:

int main(){ 
     char ar[]={'a','b','c','\0'};
     printf("argument: &ar     %s\n",&ar);
     printf("argument: &ar[1]    %s\n",&ar[1]);
}

the output:

argument:&ar   abc  
argument:&ar[1]  bc

It seems that &ar is taken as a pointer to the first element instead of a pointer to "ar", which itself is a pointer to the first element if I am not mistaken.

'cause that shouldn't be how &(pointer to a char) are treated, I tried:

char a='s';
char *pa=&a;
printf("argument: &pa   %c\n",&pa);

The output for %c isn't even a character. Nor shouldn't it be how a pointer to the first element of an array treated. I tried:

char *pa=&ar[0];
char **ppa= &pa;
printf("argument: &pa   %s\n", ppa);

The output for %s is, not surprisingly, nonsense; but why hasn't &ar been nonsense? For if ar were a pointer, shouldn't &ar be as much a pointer to a pointer as is ppa ?

My questions:

  1. Is simply "&" ignored when put before an array name? Are array names simply special on this?
  2. If so, how does the compiler verify that the identifier following "&" is a reference to an array? Does it actually search for it in a list of arrays so for declared?

Seems that &ar is taken as a pointer to the first element instead of a pointer to "ar", which itself is a pointer to the pointer of the first element if I am not mistaken.

You are mistaken. &ar is a pointer to the array ar , but the array ar is not a pointer of any sort (it's an array), so &ar is not a pointer to a pointer.

An array is a contiguous sequence of objects - in the case of ar , it's a contiguous set of 4 char s. &ar is a pointer to this set of 4 chars , which necessarily means it points at the same place as &ar[0] , a pointer to the first char in that set. It has a different type, though: &ar has type char (*)[4] which means "pointer to array of 4 chars" and &ar[0] has type char * , which means "pointer to char".

The confusion arises because in almost all expressions, ar evaluates to a pointer to the first element of the array (the exceptions to this are when it's the operand of the unary & operator or the sizeof operator). This doesn't mean that ar is a pointer though - it's not - just that in most cases it evaluates to a pointer value.

The printf format %s means “The corresponding argument is a pointer to char. Print the string at that location.” And a string, for this purpose, is a sequence of characters ending in a null character.

When you passed &ar to printf, you passed the address of the 'a' (although the type is wrong; printf expects a pointer-to-char, and you passed a pointer-to-array-of-char, but they have the same address), and printf saw the string 'a', 'b', 'c', '\\0', so it printed "abc". The same would have happened if you passed ar or &ar[0] ; those evaluate to the same address.

When you passed &ar[1] to printf, you passed a pointer to where the 'b' is, and printf saw the string 'b', 'c', '\\0', so it printed "bc".

If you want to pass just the single character at a location, use the %c format and pass a character (instead of a pointer to character). For example, if you use the %c format with *ar , 'a' will be printed, and, if you use %c with *&ar[1] , 'b' will be printed.

Seems that &ar is taken as a pointer to the first element instead of a pointer to "ar", which itself is a pointer to the first element if I am not mistaken.

When used in an expression, ar acts as a pointer to the first element of the array, the same as &ar[0] . &ar and ar are the same address (the first character in the array is at the same address as the start of the array) although they have different types (pointer to array of char and pointer to char).

the output for %c isn't even a character

It is a character, just not what you were expecting and perhaps not a normal character or a printable character. %c expect to be passed a character argument, but you passed it an address argument.

If so, how does the compiler verify that the identifier following "&" is in reference to an array, does it actually search for it in a list of arrays so for declared?

The parsing is more complicated than that (essentially, the identifier is identified as a known array before the & is considered, then the combined expression of the & and the identifier is evaluated). However, the effect is that &ar evaluates to the same address as the first element.

If your code compiled without warnings, you are not making full use of your compiler. It will help you if you ask it to do so. Learn how to make it compile with warnings, and learn how to fix the problems it diagnoses. I compiled the code below with one or the other of these commands:

gcc -O3 -g -std=c99 -Wall -Wextra -m64 array-stuff.c -o array-stuff
gcc -O3 -g -std=c99 -Wall -Wextra -m32 array-stuff.c -o array-stuff

That's a good starting point for clean code with GCC. Indeed, -Wall without -Wextra is pretty good too.

Here's an adaptation of your code (in a file array-stuff.c ) — although most of it is different:

#include <stdio.h>
#include <inttypes.h>

int main(void)
{ 
    // DC4M = Doesn't compile for me, because I compile with stringent warnings
    char ar[16] = { 'a', 'b', 'c', '\0' };  // Note explicit size
    printf("%-18s  %s\n", "Code:", "char ar[16] = { 'a', 'b', 'c', '\0' };");
    //printf("argument: &ar     %s\n", &ar);    // DC4M
    printf("argument: &ar[1]  %s\n", &ar[1]);

    printf("%-18s  0x%" PRIXPTR "\n", "ar:",       (uintptr_t)ar);
    printf("%-18s  0x%" PRIXPTR "\n", "&ar:",      (uintptr_t)&ar);
    printf("%-18s  0x%" PRIXPTR "\n", "(ar+1):",   (uintptr_t)(ar+1));
    printf("%-18s  0x%" PRIXPTR "\n", "(&ar+1):",  (uintptr_t)(&ar+1));
    printf("%-18s  0x%" PRIXPTR "\n", "&ar[1]:",   (uintptr_t)(&ar[1]));
    printf("%-18s  0x%" PRIXPTR "\n", "&(ar[1]):", (uintptr_t)(&(ar[1])));
    printf("%-18s  0x%" PRIXPTR "\n", "(&ar)[1]:", (uintptr_t)((&ar)[1]));

    printf("%-18s  %zu\n", "sizeof(ar):",       sizeof(ar));
    printf("%-18s  %zu\n", "sizeof(&ar):",      sizeof(&ar));
    printf("%-18s  %zu\n", "sizeof(void*):",    sizeof(void*));
    printf("%-18s  %zu\n", "sizeof(ar[1]):",    sizeof(ar[1]));
    printf("%-18s  %zu\n", "sizeof(&ar[1]):",   sizeof(&ar[1]));
    printf("%-18s  %zu\n", "sizeof(&(ar[1])):", sizeof(&(ar[1])));
    printf("%-18s  %zu\n", "sizeof((&ar)[1]):", sizeof((&ar)[1]));

    {
    char  a = 's';
    char *pa = &a;
    printf("%-18s  %s\n", "Code:", "char  a = 's';");
    printf("%-18s  %s\n", "Code:", "char *pa = &a;");
    //printf("argument: &pa   %c\n", &pa);    // DC4M
    printf("%-18s  0x%" PRIXPTR "\n", "&pa:",  (uintptr_t)&pa);
    printf("%-18s  0x%" PRIXPTR "\n", "&a:",   (uintptr_t)&a);
    printf("%-18s  0x%" PRIXPTR "\n", "pa:",   (uintptr_t)pa);
    }

    {
    char  *pa = &ar[0];
    char **ppa = &pa;
    //printf("argument: &pa   %s\n", ppa);  // DC4M
    printf("%-18s  %s\n", "Code:", "char  *pa = &ar[0];");
    printf("%-18s  %s\n", "Code:", "char **ppa = &pa;");

    printf("%-18s  0x%" PRIXPTR "\n", "&pa:",  (uintptr_t)&pa);
    printf("%-18s  0x%" PRIXPTR "\n", "ppa:",  (uintptr_t)ppa);
    printf("%-18s  0x%" PRIXPTR "\n", "*ppa:", (uintptr_t)*ppa);
    printf("%-18s  0x%" PRIXPTR "\n", "&ppa:", (uintptr_t)&ppa);
    }

}

This is the output from a Mac OS X 10.7.4 machine with a 64-bit compilation:

Code:               char ar[16] = { 'a', 'b', 'c', '
argument: &ar[1]  bc
ar:                 0x7FFF6C9DE570
&ar:                0x7FFF6C9DE570
(ar+1):             0x7FFF6C9DE571
(&ar+1):            0x7FFF6C9DE580
&ar[1]:             0x7FFF6C9DE571
&(ar[1]):           0x7FFF6C9DE571
(&ar)[1]:           0x7FFF6C9DE580
sizeof(ar):         16
sizeof(&ar):        8
sizeof(void*):      8
sizeof(ar[1]):      1
sizeof(&ar[1]):     8
sizeof(&(ar[1])):   8
sizeof((&ar)[1]):   16
Code:               char  a = 's';
Code:               char *pa = &a;
&pa:                0x7FFF6C9DE560
&a:                 0x7FFF6C9DE56F
pa:                 0x7FFF6C9DE56F
Code:               char  *pa = &ar[0];
Code:               char **ppa = &pa;
&pa:                0x7FFF6C9DE558
ppa:                0x7FFF6C9DE558
*ppa:               0x7FFF6C9DE570
&ppa:               0x7FFF6C9DE550

And this is the output from a 32-bit compilation:

Code:               char ar[16] = { 'a', 'b', 'c', '
argument: &ar[1]  bc
ar:                 0xC008A670
&ar:                0xC008A670
(ar+1):             0xC008A671
(&ar+1):            0xC008A680
&ar[1]:             0xC008A671
&(ar[1]):           0xC008A671
(&ar)[1]:           0xC008A680
sizeof(ar):         16
sizeof(&ar):        4
sizeof(void*):      4
sizeof(ar[1]):      1
sizeof(&ar[1]):     4
sizeof(&(ar[1])):   4
sizeof((&ar)[1]):   16
Code:               char  a = 's';
Code:               char *pa = &a;
&pa:                0xC008A668
&a:                 0xC008A66F
pa:                 0xC008A66F
Code:               char  *pa = &ar[0];
Code:               char **ppa = &pa;
&pa:                0xC008A664
ppa:                0xC008A664
*ppa:               0xC008A670
&ppa:               0xC008A660

When you understand how the various numbers were arrived at, you will be well on your way to understanding things.

Note that &array[1] is interpreted as &(array[1]) ; it is different from (&array)[1] in type and size. The postfix operators such as array subscripting bind more tightly than the unary operators such as the address ( & ) and indirection ( * ) operators.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM