Never confuse education with intelligence, you can have a PhD and still be an idiot.
- Richard Feynman -



Chapter:C Pointers

From Juneday education
Revision as of 10:16, 14 March 2018 by Henrik Sandklef (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Meta information about this chapter

Expand using link to the right to see the full content.

Introduction

Pointers are used a lot in C programs. They are need to use dynamic memory. Without knowing how to work with pointers a programmer will be left without a very useful and important tool.

Purpose

With pointers we open up great possibilities as well as mistakes. Pointers are a building block for the coming lecture on dynamic memory.

Goal

The student shall:

  • be able to alter values of variables using pointers
  • get the address of a variable
  • dereference an address
  • use structs and points
  • write and understand functions where pointers to variables are passed

Instructions to the teacher

Common problems

Chapter videos

All videos in this chapter:

See below for individual links to the videos.

Introducing pointers

Description

"In computer science, a pointer is a programming language object, whose value refers to (or "points to") another value stored elsewhere in the computer memory using its memory address." - Pointer

Explaining pointers

Comparing to things in our mundane life

If we do an analogy with real life and more specifically a house. It is not hard to imagine there is a house somewhere. It is neither hard to imagine someone has a note with the address to that house.

If we go back to programming again and think of a variable, let's say of type int, then this variable "is" the house.

What about the address? And the note? The address of the house would correspond to the location in memory where the value (of the variable) is stored.

Before we start discussing the note with the adress we need to think about one thing. How many notes with the house's address can exist? As many as we want, e g 0, 12 or 34or why not 456. The note corresponds to a variable storing an address. We can have many notes with the same address and we can have many (or none) variables containing the same memory location. Variables containing addresses are called pointers. Pointers refer to memory locations.

Well, it's all nice and so. But come on - why complicate things? We will compare with life in general. Imagine if I ask Rikard to clean my apartment. It would be rather inconvenient for me to bring him my whole apartment. It would be way easier to ask him: Rikard, can you please clean my apartment which is located at Idiot Street 666 floor 13, Stupidburg, Zombieland?

Same thing with computing. Imagine we have a a chunk of video data stored in memory. Why copy all that data invoke a function to display it? It would be simpler to invoke the function and point to where the data is located.

Note: we can store a pointer in a long variable, but this will not be a pointer ... even if the value points to some memory

Ok, let's move on to the syntax when dealing with pointers.


Videos

  1. Introducing pointers (eng) (download presentation)

Address of a variable &

Let's go back to th apartment. Let's say we want to know the address of the apartment. We don't know of an easy way to do this in an actual apartment, but one way would be to check the street sign etc. In C this is way easier. Let's say we have a variable length of type int and that we want the address of that variable, then we simply do &x. The operator & is called address of operator.

Let's check an example:

1   int  length = 123;
2   printf ("length, value       %d\n", length);
3   printf ("length, address     %p\n", &length); // not perfect code - see the exercises below!

Line by line

  1. declare an int variable length and assign it the value 123
  2. print (using "%d") the value of the length variable
  3. print (using "%p") the address of the length variable

We will wrap up how to put all the pointer pieces together in an example, see below.

Exercises

Before we proceed we'd like to say that we recommend using "%p" when printing addresses/pointers with printf.

1. The address of an (undefined) int variable

Write a small program (in one file will do fine) that declares an int variable without assigning it any value. Print out the value and the address of the variable. Before you campile and execute try to figure out what will happen.

2. Getting more help from the compiler

Compilation probably succeeded in the previous exercise. But we (the authors) know, since we wrote the exercise, that there are some issues with the code we asked you to write. Add the following options to gcc: -g -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -fstack-protector-strong.

Compile again. Try to explain the warnings.

Hint: example compilation command: gcc -g -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -fstack-protector-strong addr-exercise-01.c

3. Getting errors instead of warnings

Since we want to get rid of all possible bugs as early as possible we can instruct gcc to treat all warnings as errors. For us this means that instead of simply getting a warning and a program produced we get errors and no program produced. This is good - since we want to make sure our program is as bug free as possible. Add this options as well as the previos ones to gcc: -Werror .

Compile again. What happened?

Hint: example compilation command: gcc -g -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -fstack-protector-strong addr-exercise-01.c

4. Fixing the void* problem

One of the warning (or rather errors) was that the "%p" format expects a void* instead of an *int as we supply. You need to do an explicit typecast to void* of the &x (assuming your variable is called x).

Hint: To typecast you write (wanted-type)expression and in our case the wanted type is void* and the expression is &x to the typecast should be something like (void*) &x

5. Fixing the unassigned variable

Change the program above so that you assign the variable a value. Print out the value and the address of the variable as before.

Compile using all the flags above.

Solutions

Expand using link to the right to see the full content.


1. The address of an (undefined) int variable

Depending on how the compiler assigns or does not assign a default value the value printout may differ. The printout of "&x" simply print a big number - actually the address of the variable x.

Suggested solution:

#include <stdio.h>
int main()
{
  /* declare an int variable - no assignment */
  int x;
  /* print the value */
  printf ("x value:     %d\n", x);
  /* print the address   */
  printf ("x address:   %p\n", &x);


  return 0;
}

Source code at gihtbub: addr-exercise-01.c.

Compilation and execution worked fine. The printout looks like this (on Henrik's computer... the address will surely "look" different on your computer)

x value:     0
x address:   0x7fff351d3e7c


2. Getting more help from the compiler We now get two warnings when compiling:

addr-exercise-01.c: In function ‘main’:
addr-exercise-01.c:15:26: warning: format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘int *’ [-Wformat=]
   printf ("x address:   %p\n", &x);
                          ^
addr-exercise-01.c:12:3: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
   printf ("x value:     %d\n", x);
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

These are just warnings so the program is created and we can run the program. The warning tells us that:

  • x has not been initialised to a value when printed
  • %p require a void* while we pass int*


Source code at gihtbub: addr-exercise-01.c.


3. Getting errors instead of warnings We now get two errors:

addr-exercise-01.c: In function ‘main’:
addr-exercise-01.c:15:26: error: format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘int *’ [-Werror=format=]
   printf ("x address:   %p\n", &x);
                          ^
addr-exercise-01.c:12:3: error: ‘x’ is used uninitialized in this function [-Werror=uninitialized]
   printf ("x value:     %d\n", x);
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors

The two warnings we got in the earlier exercise are now errors so the program is NOT created.

Source code at gihtbub: addr-exercise-01.c.

4. Fixing the void* problem

#include <stdio.h>
/*                                                                                                       
 *                                                                                                       
 * Small program to show how to get the address of a variable                                            
 *                                                                                                       
 */
int main()
{
  /* declare an int variable - no assignment */
  int x;
  /* print the value */
  printf ("x value:     %d\n", x);
  /* print the address                                                                                   
     we cast since "%p"expects a void* instead of the int* we supply                                     
  */
  printf ("x address:   %p\n", (void*)&x);


  return 0;
}

Source code at gihtbub: addr-exercise-04.c.

Compiling this with the -Werror options still gives an error during compilation.

addr-exercise-04.c: In function main:
addr-exercise-04.c:12:3: error: x is used uninitialized in this function [-Werror=uninitialized]
   printf ("x value:     %d\n", x);
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors

5. Fixing the unassigned variable

#include <stdio.h>
/*                                                                                                       
 *                                                                                                       
 * Small program to show how to get the address of a variable                                            
 *                                                                                                       
 */
int main()
{
  /* declare an int variable - no assignment */
  int x = 12;
  /* print the value */
  printf ("x value:     %d\n", x);
  /* print the address                                                                                   
     we cast since "%p"expects a void* instead of the int* we supply                                     
  */
  printf ("x address:   %p\n", (void*)&x);


  return 0;
}

Source code at gihtbub: addr-exercise-05.c.

Compiling (using -g -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -fstack-protector-strong) and execution of this works fine.

Note: if we use the gcc flags -g -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -fstack-protector-strong we can't be sure our code is bug free but it gives us a better chance of finding bugs. So let's use the flags from now on!</code>

Videos

Address of a variable (eng)

Links

Pointer variables *

Description

We use the * sign when declaring a pointer. Let's say we want to declare a pointer to int, a variable which points to memory that can store an integer. If we compare this to our house it will be like saying that we want a note that can contain addresses. Going back to computing again, to declare a pointer to int we simply do like this:

int *int_ptr;

The variable int_ptr:

  • is a variable of type pointer to in
  • is usually simply called pointer or int pointer
  • can be used to store addresses (to variables of type int)

Videos

Pointer variables (eng)

Exercises

A bit of recap first:

int x; - declares an int variable, x.

x=13; - assigns 13 to x.

int *xp; - declares a pointer to int, xp

xp = &x; - assigns the address of x to xp

*xp = 12; - assigns 12 to the memory xp points to (same memory as x). x now has the value 12.


...and now to the actual exercises

1. Write a program that, in the main function:

  • declares an integer variable, called students .
  • assigns the variable 12.

Compile the program. What do you think will happen when you execute it. Execute the program to verify.

2. Add a printout of the variable students .

3. To the program above:

  • add a variable, an integer pointer, called studentsp .
  • assign the variable the address of students.

Compile the program. What do you think will happen when you execute it. Execute the program to verify.

4. Add a printout of the variable studentsp . Try printing it as a decimal using %d .

Compile the program. The compiler (hopefully) warns you. Read the warning and think about it.

5. What happens if you use %p to format instead?

Read the manual of printf .

studentsp points to students so studentsp is an indirect way of accessing students .

6. You should now Assign a new value of students using studentsp . Hint: use the dereference * operator

7. Look at the following code.

1   long salary;
2   long *salary_ptr;

What does the code do?

7. Look at the following code.

1   long salary;
2   long *salary_ptr;
3   
4   salary = 50000; /*dream on*/
5   salary_ptr = &salary;

The value of salary_ptr is related to salary. In what way?

8. Look at the following code.

1   long salary;
2   long *salary_ptr;
3 
4   salary = 50000; /*dream on*/
5   salary_ptr = &salary;

What is the value of salary after all the lines above have been executed?

9. Look at the following code.

1   *salary_ptr = 60000;

If we add this code to the code in the previous exercise, what is the value of salary after all the lines have been executed? What we're doing is something we will look into in the next section. This exercise is a sort of cliff hanger.

10. What is the size of a char *?

11. What is the size of a int *?

12. What is the size of a long *?

13. What is the size of a long long *?

Solutions

Expand using link to the right to see the full content.

1. Without the recommended gcc flags we can compile and execute. You'll see nothing happening when executing the program, but can be pretty sure that an int has been declared and assigned 12.

However if we use the recommended flags -g -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -fstack-protector-strong the will get an error saying

variable-pointer-1.c:5:7: error: variable ‘students’ set but not used [-Werror=unused-but-set-variable]
   int students;
       ^~~~~~~~
cc1: all warnings being treated as errors

Suggested solution source code variable-pointer-1.c

2. Same as above but now the value of students is printed to stdout. Solution source code pointer-3-2.c

3. Same as for the previous. A pointer has also been created.

Solution source code pointer-3-3.c

If we use the recommended flags -g -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -fstack-protector-strong the will get an error saying

variable-pointer-3.c: In function ‘main’:
variable-pointer-3.c:6:8: error: variable ‘studentsp’ set but not used [-Werror=unused-but-set-variable]
   int *studentsp;
        ^~~~~~~~~
cc1: all warnings being treated as errors

This is not the end of the world. The program will not malfunction. But since the variable is not used we can remove the parts of the code the deals with this variable. If we remove lines of code from our program, we also remove the possibility of bugs. So this warning (unused variable) is very useful. However, since this is an exercise where we want you to see the warning/error occur we ask you to keep the code as it is.

4. A pointer (in this case an address) is most likely bigger than the max value of an int so the compiler warns about this.

Solution source code pointer-3-4.c

5. Yes, it works.

Solution source code pointer-3-5.c

6. We can assign students by dereferencing the studentsp variable. Since studentsp has been assigned the address of students we thereby assign student the value.

  *studentsp = 17;

Solution source code pointer-3-6.c

Think of it like we're getting the address of students, stores the address on a piece of paper (studentsp) and then go t that address and assigns a value

7. Creates a long variable and a pointer to long variable.

If we use no flags to gcc compilation goes fine. If we use the recommended flags -g -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -fstack-protector-strong we will get errors:

variable-pointer-7.c:6:9: error: unused variable salary_ptr [-Werror=unused-variable]
   long *salary_ptr;
         ^~~~~~~~~~
variable-pointer-7.c:5:8: error: unused variable salary [-Werror=unused-variable]
   long salary;
        ^~~~~~
cc1: all warnings being treated as errors

Using these flags we get warning, converted to errors with the -Werror flag:

variable-pointer-7.c:6:9: error: unused variable ‘salary_ptr’ [-Werror=unused-variable]
   long *salary_ptr;
         ^~~~~~~~~~
variable-pointer-7.c:5:8: error: unused variable ‘salary’ [-Werror=unused-variable]
   long salary;
        ^~~~~~
cc1: all warnings being treated as errors

Again, it is good to get warned (or an error) if we have variables that are not used.

Solution source code: pointer-3-7.c

8. salary is assigned the value 5000. salary_ptr is assigned the address of salary, so salary_ptr points to the memory where the value of salary is stored.

salary_ptr is unused, so if using the recommended flags we will get a warning or error.

Suggested solution source code pointer-3-8.c

9. salary has the value 60000 after the new line has been executed.

Solution source code pointer-3-9.c

10. 8 (on the author’s computer/OS).

You can check what size it is on your computer by compiling and executing the code below:
1 include <stdio.h>
2 
3 int main() {
4 
5   printf ("Size of char:  %lu\n", sizeof(char));
6   printf ("Size of char*: %lu\n", sizeof(char*));
7 
8   return 0;
9 }

To compile and execute (a bit of recap): Save the above source code in a file called check_size.c

Compile the file and store the resulting (executable) file in check_size, like this: <code>gcc check_size.c -o check_size

Execute the file like this: ./check_size

We (the authors) normally do the simple trick of "compile and if it succeeds, then execute": gcc check_size.c -o check_size && ./check_size

11. 8 (on the author’s computer/OS).

Add the following lines to the code above:
1   printf ("Size of int:   %lu\n", sizeof(int*));
2   printf ("Size of int*:  %lu\n", sizeof(int*));

Recompile and execute: gcc check_size.c -o check_size && ./check_size

12. 8 (on the author’s computer/OS).

Add the following lines to the code above:
1   printf ("Size of long:  %lu\n", sizeof(long*));
2   printf ("Size of long*: %lu\n", sizeof(long*));
Recompile and execute: gcc check_size.c -o check_size && ./check_size

13. 8 (on the author’s computer/OS).

Add the following lines to the code above:
1   printf ("Size of long long:  %lu\n", sizeof(long long*));
2   printf ("Size of long long*: %lu\n", sizeof(long long*));
Recompile and execute: gcc check_size.c -o check_size && ./check_size
Check out the source code here: [1] 

Videos

Pointer variables (eng)

Links

Dereferencing

Description

Let's go back the house analogy. When wanting Rikard to clean our apartment he needs to be able "go there". Same thing with pointers. We need to go to the location which the pointer refers (or points) to. We do this with the * sign. Yes, it's true, the same sign as when declaring a pointer. Tough, but this is something we have to [accept].

Let's say we want to write an integer value to the memory referred to by the pointer int_ptr above:

*int_ptr = 12;

This will write the value 12 to the memory pointed to by int_ptr.

Exercises

1. Printing an address of a variable

Write a program that:

  • creates an integer variable (x) and assigns it a value (e g 1234);
  • creates a pointer to int variable (xp) and assigns it the address of the integer variable
  • prints the value of the integer variable (x)
  • prints the address of the integer variable (&x)
  • prints the value of the pointer to int variable (xp)

2. Changing the value of a variable using a pointer

Let's continue with the code above by adding code after the code from the previous exercise. With the dereference operator (*) we can "go to" the memory of a variable and update it. Use this to update the memory where the pointer points to. Update the value to 4321. Print the interger variable (x) one more time.

Hint: If ptr is a pointer, we can update the memory it points to by writing *ptr=... where ... should be replaced by an expression.

Solutions

Expand using link to the right to see the full content.

1. Printing an address of a variable Suggested solution:

#include <stdio.h>

/*
 *
 * Small program to show how to dereference a pointer
 *
 */

int main()
{
  int  x  = 12;
  int *xp = &x;
  
  printf ("x value:     %d\n", x);
  printf ("x address:   %p\n", (void*)&x);
  printf ("xp:          %p\n", (void*)xp);


  return 0;
}

Source code: deref-pointers-01.c

2. Changing the value of a variable using a pointer Suggested solution:

#include <stdio.h>

/*
 *
 * Small program to show how to dereference a pointer
 *
 */

int main()
{
  int  x  = 1234;
  int *xp = &x;
  
  printf ("x value:     %d\n", x);
  printf ("x address:   %p\n", (void*)&x);
  printf ("xp:          %p\n", (void*)xp);

  /* Update the mem where xp points to*/
  *xp = 4321;
  printf ("x value:     %d\n", x);

  return 0;
}

Source code: deref-pointers-02.c


Videos

Dereferencing pointers (eng)

Links

Pointers and functions

Description

First some recap:

If we invoke a function, passing an int as argument, like this set_temperature(19); the value (19) is copied to the function.

Same thing happens if we do:

   int wanted_temp = 19;
   set_temperature(wanted_temp);

Before invoking the function the expression wanted_temp is evaluated (to 19). Then the value 19 is copied in to the function. It is important, but quite easy, to understand that the value is copied to the function. If we want to use fancy words we call this "call by value". Imagine you have a badge on your room. The badge has your name on it. You pass your name (not the badge) to a person who writes your name down on a note and ask that person to write his/her name instead. Would you expect the name on the badge to be updated? No, thought so. "Call by value" has its limits.

And now to the fun, or at least new, stuff. What happens when passing pointers to functions. Same thing with pointers as with values (above). If we pass a pointer to a function, then the value of the pointer (i e the address) is copied to the function. Let's bring back the apartment (or house) analogy. When Henrik asks Rikard to clean his apartment he gives Rikard a copy of the address. As long as Rikard is cleaning up the apartment there are two variables (pointers) with the same address but only one apartment. When Rikard is done he throws away the address (after all, who would want to go back to Henrik's apartment?). The value of the pointer is copied, so in a way passing a pointer is "call by value" but... since the important thing is not the pointer but what it points to (i e what it references) we call this "call by reference".

Now, let's go back to the badge on your room. Ask the same person to update the name on the badge but instead give her/him the address to your room. Would you expect the name on the badge to be updated? Yes, cool isn't it?. "Call by reference" is kind of useful.

Exercises

Recap:

When calling/invoking a function, the supplied arguments are copied to the function - regardless os whether they initially were variables or literals, e g:

1   int x=2;
2   int y=4;
3   int res = add(x,y); /* the values of x and y are copied to the function add */
4   res = add(12, 34);  /* the values 12 and 34 are copied to the function add */

...and now to the actual exercises

  1. Look at the following function.
    1   void swap (int x, int y) {
    2     int tmp = x;
    3     x = y;
    4     y = tmp;
    5   }
    
    What does it do?
  2. Call the function swap with 12 and 34 as arguments. What are the values of 12 and 34 after the function has been executed?
    *Hint: it really is a trick question. Stop wasting energy!*
  3. Declare two integer variables, girls and boys, and assign them values. Call the function swap. What are the values of boys and girls?
    *Hint: don't ask the supervisors what will happen.... write the code, compile and test it!!. Have fun!*
  4. Look at the following function.
    1   void swap (int *x, int *y) {
    2     int tmp = *x;
    3     *x = *y;
    4     *y = tmp;
    5   }
    
    What does it do?
  5. Declare two integer variables, girls and boys, and assign them
    values. Call the function swap with girls and boys as arguments.
    Does it compile without warning?
  6. Declare two integer variables, girls and boys, and assign them
    values. Call the function swap with the addresses of girls and boys as arguments.
    What are the values of boys and girls after the code has been executed?
  7. Look at the following macro.
     #define swap_int(a,b) int tmp=a; a=b; b=tmp;
    
    What does it do?
  8. Declare two integer variables, girls and boys, and assign them values. Call the macro swap_int with the addresses of girls and boys as arguments.
    What are the values of boys and girls after the code has been executed?
  9. Write a function, doubler, that takes a pointer to in as parameter. The value pointed to by the parameter shall be doubled . The function shall return void.
    Also make sure to test the function.
  10. Try the function above by supplying NULL as argument.
    What do you think will happen? Will it compile? Will it execute nicely?
    Make the function safer to use.
  11. Add a pointer to int as a second arg to doubler. The function shall now return an integer value (0 if sucessful doubling, 1 otherwise). The memory pointed to by the second argument should be assigned the doubled value. The prototype of the function should look something like:
    int doubler (int *x, int *result);
    Make sure to test the function.

Solutions

Expand using link to the right to see the full content.

  1. It swaps the values of the local variables x and y. Since a and y are local variables (with values copied in to them when the function is invoked) it really doesn't do anything useful. Solution source code functions-pointer-1.c
  2. 12 is 12. 34 is 34. Solution source code functions-pointer-2.c
    I'ts math :)... Nobel award music is playing in the background now!*
  3. See the first exercise on why nothing is outputted. Output the values of girls and boys and you'll see that non of the arguments have been changed. Solution source code functions-pointer-3.c
  4. The swap function goes to the memory pointed to by the arguments and reads and changes the values in that memory. Note that the variables, created inside the function when the function is invoked, do not exist anymore after the function has finished executing. Solution source code functions-pointer-4.c
  5. No. Tons of warnings. When running the program it crashes since we're reading (when dereferencing the pointers x and y which points to memory outside of our program). Solution source code functions-pointer-5.c
  6. By using the addresses the swap function has swapped the values of the variables girls and boys. Solution source code functions-pointer-6.c
  7. see next answer
  8. It substitutes the call to swap_int with the ... ahh, let's look at the code after the preprocessor has been invoked instead. Let's simply do gcc -E functions-pointer-8.c Solution source code functions-pointer-8.c
    This will give us the following code:
     1 ...
     2 snip
     3 ...
     4   int main() {
     5   
     6     int girls = 12;
     7     int boys = 11;
     8   
     9     printf ("girls: %d  boys: %d\n", girls, boys);
    10 
    11     int tmp=x; x=y; y=x;;
    12   
    13     printf ("girls: %d  boys: %d\n",
    14     girls, boys);
    15 
    16     return 0;
    17   }
    
    Note: this is a very short version of what gcc -E produces. Try for yourself!
  9. See Solution source code functions-pointer-9.c
  10. The program compiles but crashes when executed. It crashes, with a segmentation fault, since we're accessing memory outside of our program. Solution source code functions-pointer-10.c
  11. See Solution source code functions-pointer-11.c It is a bit awkward to write tests like this isn't it? In C (Java and other languages) there is a thing called assert. Here's a solution to this exercise using asserts functions-pointer-11-assert.c

Videos

Pointers and functions (eng)

Links

Pointers and User arguments

This section uses concepts we have not yet been introduced to. We believe the exercises are a good way to get used to user input via the command line which is going to be used in the hand ins and the exercises are providing code or hints for you dealing with the concepts you do not yet know about.

Description

When starting a program you can pass information to it. This is what you do when you invoke the compiler (gcc file.c). In the coming hand ins it is good if you have a strategy for managing the input your program gets from its user.

We recommed reading: Interacting with programs and Command line parser before you continue with this section.

Exercises

1. Empty parameter list Write a program that has an empty parameter list in its main method. Fact is, this is enough.

#include <stdio.h>

int main()
{
  return 0;
}

Nothing problematic with this program given what we've learned. But there is one problem. We cannot manage any information passed to us by the user.

Source code: user-pointer-01.c

2. Count the number of arguments from the user Instead write our main function like this:

int main(int argc, char **argv)
{
  return 0;
}

If we do this the operating system and C will make sure that the information passed to the program via the command line can be reached by the program using the variables argc and argv. argc is an integer containing the number of arguments the user passed to the program, including the program itself. If we pretend we're gcc for a while and we're invoked like this: gcc -pedantic file.c then argc will have the value 3 since the user passed the arguments:

  1. gcc
  2. -pedantic
  3. file.c

Add to your program a statement the prints the number of arguments.

Test your program, assumed to be named user-pointer-02, (at least) the following ways:

  • ./user-pointer-02
  • ./user-pointer-02 -pedantic
  • ./user-pointer-02 -pedantic -Werror something else

What do you expect to be printed?

3. Listing the user arguments

The arguments, passed to the program by the user, are stored in char **argv which is a pointer to pointer to char. Until we've read about arrays we don't know how this works so we will ask you to copy our code and believe that it might prove useful to have seen this:

#include <stdio.h>

/*
 *
 * Small program to show how to manage the information passed to it
 * via the command line options
 *
 */

int main(int argc, char **argv)
{
  int i;
  printf ("Number of arguments from the user: %d\n", argc);

  for (i=0; i<argc; i++)
    {
      printf ("* Argument nr %d is '%s'\n", i, argv[i]);
    }
  
  return 0;
}

Test your program, assumed to be named user-pointer-03, (at least) the following ways:

  • ./user-pointer-03
  • ./user-pointer-03 -pedantic
  • ./user-pointer-03 -pedantic -Werror something else

4. Find a valid option

Given that the user passes the option --terminate we shall stop printing immediately and exit the program. This is hardly any useful. For now we shall be happy with detecting the --terminate. So let's write code that does that.

This means you have to test every user argument if it is the same as --terminate. If it is we shall only print out something like "really should quit here". In the coming exercise we will actually do the rest but one thin at a time.

Hint: you can compare the text in argv[i], like this strncmp(argv[i], "--terminate", strlen("--terminate"). If the expression returns 0 we have found the string.

5. Handle a valid option

If we find the option --terminate we should now exit the for-loop, using break, and let the program return 0.

6. Fixing a small bug

Try passing --terminate2 to the program. It stops since it only compares the text "--terminate" with "--terminate2" up to the lenght of the former. To do a better comparison we recommend also comparing the length of the texts - if they're different, the texts can't be the same.

Solutions

Expand using link to the right to see the full content.

1. Empty parameter list

Not really a question, so we can't provide an answer.

Source code: user-pointer-01.c

2. Count the number of arguments from the user

Suggested solution:

#include <stdio.h>

/*
 *
 * Small program to show how to manage the information passed to it
 * via the command line options
 *
 */

int main(int argc, char **argv)
{
  printf ("Number of arguments from the user: %d\n", argc);
  
  return 0;
}

If we test the program like above we will get a printout like this (of course the printout will change if you have a print statement different from ours):

  • 1
  • 2
  • 5

Source code: user-pointer-02.c

Note: If we want to be "crazy" we can use the program seq to create arguments for us. If you type (in the terminal running bash) seq 1 10 you will see that seq prints out all the number between 1 and 10. We can pass them as command line options to our program like this: ./user-pointer-02 $( seq 1 10). Our program will now print out that it got 11 arguments (1 being the program name and the 10 it got from seq). Using this trick we can pass 100001 options to our program, like this: ./user-pointer-02 $( seq 1 10000). Nice isn't it :)


3. Listing the user arguments

You will see something like:

#./user-pointer-03 
Number of arguments from the user: 1
* Argument nr 0 is './user-pointer-03'
# ./user-pointer-03 -pedantic
Number of arguments from the user: 2
* Argument nr 0 is './user-pointer-03'
* Argument nr 1 is '-pedantic'
# ./user-pointer-03 -pedantic -Werror something else
Number of arguments from the user: 5
* Argument nr 0 is './user-pointer-03'
* Argument nr 1 is '-pedantic'
* Argument nr 2 is '-Werror'
* Argument nr 3 is 'something'
* Argument nr 4 is 'else'

Source code: user-pointer-03.c

4. Find a valid option

Suggested solution:

#include <stdio.h>
#include <string.h>

/*
 *
 * Small program to show how to manage the information passed to it
 * via the command line options
 *
 */

int main(int argc, char **argv)
{
  int i;
  printf ("Number of arguments from the user: %d\n", argc);

  for (i=0; i<argc; i++)
    {
      printf ("* Argument nr %d is '%s'\n", i, argv[i]);
      if (strncmp(argv[i], "--terminate", strlen("--terminate"))==0)
        {
          printf ("Really should quit here\n");
        }
    }
  
  return 0;
}

Source code: user-pointer-04.c

5. Handle a valid option Suggested solution:

#include <stdio.h>
#include <string.h>

/*
 *
 * Small program to show how to manage the information passed to it
 * via the command line options
 *
 */

int main(int argc, char **argv)
{
  int i;
  printf ("Number of arguments from the user: %d\n", argc);

  for (i=0; i<argc; i++)
    {
      printf ("* Argument nr %d is '%s'\n", i, argv[i]);
      if (strncmp(argv[i], "--terminate", strlen("--terminate"))==0)
        {
          break;
        }
    }
  
  return 0;
}

Source code: user-pointer-05.c

6. Fixing a small bug Suggested solution:

#include <stdio.h>
#include <string.h>

/*
 *
 * Small program to show how to manage the information passed to it
 * via the command line options
 *
 */

int main(int argc, char **argv)
{
  int i;
  printf ("Number of arguments from the user: %d\n", argc);

  for (i=0; i<argc; i++)
    {
      printf ("* Argument nr %d is '%s'\n", i, argv[i]);
      if (strlen(argv[i])==strlen("--terminate") &&
          strncmp(argv[i], "--terminate", strlen("--terminate"))==0)
        {
          break;
        }
    }
  
  return 0;
}

Source code: user-pointer-06.c


Videos

Pointers and user arguments (eng)

Links

Our C FAQ