3

I’m having some problems learning C and I really have no where else to turn for advice. I come from a list of OOP languages such as JavaScript and mainly Python, so C is a major change and I’m hitting quite a few bumps trying to learn the fundamentals. I initially started with Zed Shaw’s “Learn C the Hard Way”, but he doesn’t really teach anything in the book. Yeah, he makes you write a lot of code and change stuff up, but I’m not learning why the code works and it is just leading to more confusion as the examples build in complication.

The main problems I am having are the difference between variables and pointers (I thought it was pretty distinct until I saw some examples that I’ll be posting below, which completely blurred the line between the two).

For example, I understand that declaring and initializing an int called a and a pointer, p would look like this:

int a;
int *p;

a = 12;
p = &a;

What confuses me is when you declare variables that look like pointers, but aren’t really pointers at all (or are they?). For example:

char *string = "This is a string";
printf("%s\n", string);

What is string when it is defined and initialized? Is it a pointer? And if it is, why don’t you dereference it when printing it in the printf function? There are many examples like this that confuse me.

Another example I came across that made no sense whatsoever:

int i;
scanf("%d", &i);

How does this function update the value of integer i, when the ampersand is supposed to reference the location in memory of the variable, not the value? It gets even more complicated with arrays and structs, which is where I stopped and decided I needed to find some advice.


I honestly feel embarrassed posting such a noob question, but everyone starts somewhere. These fundamentals are something I know I need to be able to understand before moving on, but it is so hard for me to make sense of it when I see examples of code that contradict what I had just learned. I know this is a very general question but I am hoping some of you could either explain these basics or point me in a direction where I can better learn/understand this. Most video tutorials I have come across are too general and same with the text tutorials online, where they tell you how to do something, but don’t explain it, which causes some serious problems down the line.

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
samrap
  • 5,595
  • 5
  • 31
  • 56
  • Why not just take a look into what those functions `scanf` and `printf` take in and what they do with them when acting on numbers and `strings` ? – Uchia Itachi Dec 29 '13 at 08:52
  • 4
    Let me tell you this: Everyone! once struggled like this with pointers. Everyone. It's nothing to be embarrassed about. I coded years of Java before I decided to take a look at C and it took me probably weeks until I really knew what the hell is going on with all those & and * characters. – Glandy Dec 29 '13 at 08:55
  • Thanks @Glandy I'm glad to hear I'm not the only one. I guess I'll just have to do a lot of experimenting to figure this out – samrap Dec 29 '13 at 08:58
  • Pointer Fun is here: http://cslibrary.stanford.edu/104/ – alk Dec 29 '13 at 09:08
  • I went straight to QBASIC (no OO for me) to C in high-school and it took me a week to get my head around pointers. Don't even worry about it because you're probably saving others time by asking the question. – nonsensickle Dec 29 '13 at 09:28
  • [Why use pointers?](http://stackoverflow.com/questions/162941/why-use-pointers) – Grijesh Chauhan Dec 29 '13 at 09:56

6 Answers6

6

I will try and explain this slightly differently than everyone else.

Considering that you are coming from JavaScript and Python I will avoid the term "reference" when talking about pointers. This is because though they are similar they are not the same.

What is a pointer?

A pointer is a variable that stores an address. It's that simple. A pointer to an int stores the address where the int is stored.

De-reference

When you dereference a pointer you are telling the compiler to operate, not on the address, but on what is stored at that address.

int *p;
int a = 7;

p = &a;
*p = 5;

The *p = 5 tells the compiler to go to the address that is stored inside p and store the value 5 there (as an integer since the pointer p points to an integer). So wherever we can use variables, we can use dereferenced pointers to variables in their stead.

Should you use:

int *p;

p = 5;

Then you would be assigning an address (a memory location) of 5 (wherever that may be in memory) to the pointer. If you try and use memory locations that are not allowed your program is likely to crash.

Address of

The & operator takes the address of some variable. It really doesn't care what it is, it can even take addresses of pointers.

int a;
int *p;
int **pp;

pp = &p;
p = &a;

That is all it does.

Your examples

String example

char *string = "This is a string";
printf("%s\n", string);

The reason your are confused is that in JavaScript and Python you can treat a string as if it can fit into a variable. It can't! In C a string is a sequence of characters stored sequentially in memory (an array of characters). That is why we can use pointers. What we do is we make the pointer point to the first character and then we know where the whole string is (since it is sequential). Also, in C we don't store the size of the string. Instead, the string starts at the pointer and ends when we encounter a zero byte '\0' or simply 0.

scanf example

int i;
scanf("%d", &i);

The reason scanf is given the address of i is because it will put an integer that is entered through the console into the location where i is. This way scanf can actually have more than one returned value, meaning that:

int i, j;
scanf("%d%d", &i, &j);

is possible. Since scanf knows the addresses of your variables, it can update them with the correct values.

Community
  • 1
  • 1
nonsensickle
  • 4,438
  • 2
  • 34
  • 61
  • @GrijeshChauhan Thanks for the links, they're really good. Wish I could +1 your edit. – nonsensickle Dec 29 '13 at 13:42
  • Thanks everyone for all the great answers. I learned something from all of them, but this was the most detailed and had the most examples. Thanks for all your help :) – samrap Dec 30 '13 at 00:33
3

I'll answer your specific questions about why printf does not get passed a dereferenced pointer, and why scanf gets passed an address, and hopefully that will make some things clearer.


First of all, a C-style string is, by convention, an array of characters in memory, terminated by a \0 character (called a NUL terminator). The way C-style strings are kept track of is by keeping a pointer to the first character of a string.

When printf("%s\n", some_str) is called, where some_str is of type char*, what printf does is print the character pointed to by some_str, then the character after it (in memory, located by simply incrementing the pointer some_str), then the character after that, until it finds a \0, and then it stops printing.

The same procedure is used by other C-style string manipulating functions, like strcpy, strlen, etc.

One reason this is done is that strings tend to be of different lengths, thus to require different amounts of memory, but it is inconvenient to have a data type that is of variable size. So, we have a convention for the format of a string in memory (essentially the \0 terminator convention), and we just point to the string with a char*.

If you dereference a char *, you will get a single character, not a string as you may expect, so be careful about this.


Why does scanf("%d", &i) get passed an address to an int, rather than the int itself?

The reason is that in C, function arguments get passed by value, meaning that when you pass something to a function, a copy of it is made and handed to the function, and whatever the function does to the copy, the original stays as it was. What are the implications of this?

For one thing, if you have a function like:

void add_two(int i) {
  i = i + 2;
}

If you call: int i = 3; add_two(i);, the value of i will not change, because add_two just got a copy of i. On the other hand, if you have a function like:

void really_add_two(int *i) {
  *i = *i + 2;
}

Now int i = 3; really_add_two(&i); will result in i having the value 5. This is because a pointer to i is given to really_add_two, and the function modifies the data at that memory location (via the dereferences).

The reason scanf needs to be given an address is the same as the above.


If you are serious about learning C, you should pick up Kernighan and Richie, "The C Programming Language." It will be much better than going through tutorials online, and well worth it in the long run.

Andrey Mishchenko
  • 3,986
  • 2
  • 19
  • 37
2

a pointer is a variable that holds a memory address instead of the value.

when you write

int a;

you are specifying that a can hold an integer

+-----+
| int | 
+-----+

a is somewhere in the memory and the address of a is written &a

      +-----+
&a -> | int | 
      +-----+

when you write

int *p;

you are specifying that p can hold a pointer to an integer i.e. a memory address.

     +---------+
p -> |   int   |
     +---------+

for example it can point to a

p = &a;

int i;
scanf( "%d", &i );

in order to understand the above you need to understand how function arguments are passed to a function and what actually functions are.

a function is also a pointer so when you call a function you are just telling the compiler that you want to execute code at a certain memory address. a stack is used to pass arguments and results between the function and the caller.

so

int foo(int n) { return 11; }

int j = 10;
int i = foo(j);

tells the compiler that it should execute the assembly instructions at address foo but first push 10 (j) on the stack, a copy of j is pushed on the stack and foo is executed, when foo is about to return it pushes 11 on the stack and returns, the return value 11 is then popped by the caller and assigned to i. notice it is a copy of value 11 that is pushed on the stack.

if you want the function to change the value of the argument you cannot push a copy of the variable on the stack. instead you need to pass a copy of the memory address of the variable

int foo(int *p) { *p = 12; return 11; }

int j = 10;
int i = foo(&j);

now j will be 12 and i 11

scanf is a bit more complicated than my example, it can take variable number of arguments and uses the first argument ("%d") as a format specifier to determine the data type and size of the following arguments. the %d tells it to expect an int pointer.

AndersK
  • 35,813
  • 6
  • 60
  • 86
1

What is string when it is defined and initialized? Is it a pointer? And if it is, why don’t you dereference it when printing it in the printf function?

It is a pointer. It points directly to the very first character in your string. You don't have to dereference it because printf will accept a char pointer.. and print everything until it finds a null terminator (which is automatically added for you).

How does this function update the value of integer i, when the ampersand is supposed to reference the location in memory of the variable, not the value?

Because &i makes the argument be passed in as an int*. So the function would dereference and add.. somewhat like this:

// NOTE: not actual scanf implementation
void scanf(char* format, int * input) {
    *input = get_number(); // store value in variable
}
Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
1

How about reading some basics about pointers?

http://cslibrary.stanford.edu/106/

I will answer your questions below:

What confuses me is when you declare variables that look like pointers, but aren’t really pointers at all (or are they?). For example:

char *string = “This is a string”;
printf(“%s\n”, string);

Yes they are! Its a char pointer.

How does this function update the value of integer i, when the ampersand is supposed to reference the location in memory of the variable, not the value?

It depends on the function signature. If you look at what scanf accepts as argument list, it will be clear to you.

Digital_Reality
  • 4,488
  • 1
  • 29
  • 31
  • "*It is a char pointer and assigned to*" this read as if to the literal the char pointer had been assigned. – alk Dec 29 '13 at 08:59
  • The sentence *is not address of some 'variable' but it is literal string* isn't very well explained... It doesn't make sense to me. – nonsensickle Dec 29 '13 at 09:04
  • @nonsensickle, adding link for more explanation http://stackoverflow.com/questions/2245664/string-literals-in-c – Digital_Reality Dec 29 '13 at 09:30
1
    char *string = "This is a string";
    printf("%s\n", string);
What is string when it is defined and initialized? Is it a pointer? And if it is, why don’t you dereference it when printing it in the printf function? There are many examples like this that confuse me.

First, a little background:

Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to intialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.

The string literal "This is a string" is an expression of type "17-element array of char" (16 characters plus the 0 terminator). Since it is not the operand of either the sizeof or unary & operators, and since it isn't being used to initialize an array of char, the type of the expression is converted to "pointer to char", and the value of the expression is the address of the first element.

The variable string is declared as type "pointer to char" (char *), and it is initialized with the address of the string literal.

In the printf call, the conversion specifier %s expects its corresponding argument to have type char *; it will print characters starting at the specified address until it sees a 0 terminator. This is why the variable isn't dereferenced in the printf call.

    int i;
    scanf("%d", &i);
How does this function update the value of integer i, when the ampersand is supposed to reference the location in memory of the variable, not the value? It gets even more complicated with arrays and structs, which is where I stopped and decided I needed to find some advice.

C passes all function arguments by value, meaning the formal parameter in the function definition and the actual parameter in the function call are two different objects in memory. Updating the formal parameter has no effect on the actual parameter. For example, imagine a simple swap function, like so:

void swap( int a, int b ) { int tmp = a; a = b; b = tmp; return; }
...
int x = 0, y = 1;
printf( "before swap: x = %d, y = %d\n", x, y );
swap( x, y );
printf( " after swap: x = %d, y = %d\n", x, y );

If you compiled and ran this code, you'd see the same values for x and y in both output statements; a and b are different objects in memory from x and y, and changing the values of a and b has no effect on the values of x and y.

Since we want to modify the values of x and y, we must pass pointers to these variables to the function, like so:

void swap( int *a, int *b ) { int tmp = *a; *a = *b; *b = tmp; return; }
...
int x = 0, y = 1;
printf( "before swap: x = %d, y = %d\n", x, y );
swap( &x, &y );
printf( " after swap: x = %d, y = %d\n", x, y );

Instead of swapping the values of a and b, we swap the values of what a and b point to; the expressions *a and *b refer to the same objects in memory as x and y.

This is why you must pass a pointer to i in the scanf call (the scanf conversion specifier %d expects the corresponding argument to have type int *); you would not be able to update the value of i otherwise.

I haven't been too impressed with "Learn C The Hard Way"; then again, almost all C references have some fatal flaws. Kernighan and Ritchie's The C Programming Language, while pretty long in the tooth (it doesn't cover anything introduced in the last two standard revisions), is still probably the best overall introduction to the language. I've also heard good things about King's C Programming: A Modern Approach. My go-to desktop reference is Harbison & Steele's C: A Reference Manual, although it's exactly what it says it is - a reference manual, and as such not that big on explaining basic concepts.

John Bode
  • 119,563
  • 19
  • 122
  • 198