2

I am very new at C programing language. Now i am learning memory and pointer. I am reading and reading but i am not understanding when to use the pointer and when not to. Just see the following to code. In my first code why we dont use "&" operator on this line "scanf("%2s", card_name)" ? and on my second code why i had to use "&" operator on my "scanf("%i", &decks);" I have passed whole night to understand it. Now i am here to get some good hints to understand the difference..

Code 1

#include <stdio.h>
#include <stdlib.h>
int main(){
    char card_name[3];
    int count=0;
    while (card_name[0]!='X') {
        int val=0;
        puts("Enter The Card Name:");
        scanf("%2s", card_name);
        switch (card_name[0]) {
            case 'K':
            case 'Q':
            case 'J':
                val=10;
                break;
            case 'A':
                val=11;
                break;
            case 'X':
                continue;
            default:
                val= atoi(card_name);
                break;
        }

        if (val<2||val>11) {
            puts("sorry this is not valid");
                break;
            }
            if (val>=2&&val<=10) {
                count++;
                printf("Then value is %i And The Count is %i\n", val, count);
                break;
            }
        }
    }
}

Code 2

#include <stdio.h>
int main() {
    int decks;
    puts("Enter a number of decks"); 
    scanf("%i", &decks);
    if (decks < 1) {
        puts("That is not a valid number of decks");
        return 1; }
    printf("There are %i cards\n", (decks * 52));
    return 0; 
}
Uyghur Lives Matter
  • 18,820
  • 42
  • 108
  • 144
  • A `char[]` is the address of a chunk of bytes. An `int` is just a number. – SLaks Nov 04 '13 at 22:26
  • 2
    `card_name` is an array so the name card_name already points to a memory location and not a "value". `scanf` takes input and places it in the memory address passed. With `int` you need to specify the address but an array is already an address in a sense. – Benjamin Gruenbaum Nov 04 '13 at 22:27
  • 2
    `card_name` is an array. When an array expression is evaluated, in most but not all contexts, it's implicitly converted to a pointer to its first element. See [this answer](http://stackoverflow.com/a/18626459/827263) and section 6 of the [comp.lang.c FAQ](http://www.c-faq.com). – Keith Thompson Nov 04 '13 at 22:27
  • Since arrays automatically decay into pointers to their first element when passed to a function, there's no need for the `&`. In fact, it's an error to use it. `card_name` has type `char [3]`, and it is implicitly converted to `char *`, and that's what `scanf()` expects. If you used the `&` operator, you would pass to `scanf()` a pointer of type `char (*)[3]`, which would cause undefined behavior. –  Nov 04 '13 at 22:28
  • 2
    @BenjaminGruenbaum Please don't teach beginners that an array is an address, because it is harmful. It should not take much to correctly state that "it is **implicitly converted** to a pointer". –  Nov 04 '13 at 22:29
  • @H2CO3 I used "in a sense" to note it's not _really_ a memory address. That said +1 you are correct and that's noted. Decaying is the correct term. The mix up can be harmful if you don't know the difference and it can confuse newbies with things like `sizeof` a lot. I use "loose" terminology all the time when working because that's not harmful when people understand the jargon but I guess it can be problematic in places like SO answer comments. Won't happen again ;) – Benjamin Gruenbaum Nov 04 '13 at 22:35
  • @BenjaminGruenbaum Yes, I understand that, and it was definitely not my intention to split hair. I know that **you** know that. Just OP doesn't :) –  Nov 04 '13 at 22:36
  • 1
    @BenjaminGruenbaum nitpicking about the correct term, the correct term is not *decay*, absent in the Standard, but *convert*. – ouah Nov 04 '13 at 22:39
  • @ouah Today I learned, never knew arrays being converted to pointers is called "convert" in the spec. "Decay" is just the term people use to describe the process of it happening - I see it in books and articles all the time. Thanks – Benjamin Gruenbaum Nov 04 '13 at 22:42
  • 1
    @BenjaminGruenbaum what the Standard says (emphasis mine): (C99, 6.3.2.1p3) "Except when [...], an expression that has type "array of type" is **converted** to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue." – ouah Nov 04 '13 at 22:46

2 Answers2

6

What you stumbled upon is probably the most subtle thing about the C language: the fact that array identifiers decay into pointers. When you write

char card_name[3];

you define an array of three characters. That is the easy thing, the type of card_name is written as char ()[3].

The difficult part to grasp, is that card_name will decay to a pointer to the first element of that array almost in all contexts, so the type of almost any mention of card_name will be char*. It is almost as if the address taking operator was implicitely applied to any mention of an array name. So, when you pass card_name to scanf(), you are actually passing a pointer to its first element, and scanf() uses this pointer to store the name.

On the other hand, when you do the same thing with the integer, there is no such thing like pointer decay. The code behaves exactly how you would expect it to, whenever you use the name decks you get the value of the integer, not just its address. This is why you have to explicitely take an address from decks.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
1

Try to understand pointer levels of indirection. Scanf always takes a level 1 pointer as an arguement.

An array is declared this:

char name[4];

and name is a level 1 object. The array decays to a pointer when passed as a argement:

void f(char* name) { ... }

Other level 1 objects:

int* p1;
double* d1;

and these are level 2 objects:

int** p2;
double** d2;

and level 0:

int p0;
double d0;

Thus the level is the number of stars (or []) in the declaration. This gets modified in the code by the use of * and &. The * dereferences a pointer and reduces the level by 1. But the & does the opposite - it increases the level by 1.

So using scanf with the above, we would have:

scanf("%i%i%i", &p0, p1, *p2);
scanf("%lf%lf%lf", &d0, d1, *d2);

Where &p0 is level 0 + 1 for & for a total of 1 p1 is already 1 *p2 is level 2 - 1 for * for a total of 1

resigned
  • 1,044
  • 1
  • 10
  • 11