1 Nejin

Assignment Operator C++ Reference Parameter

Pointers, References and Dynamic Memory Allocation are the most powerful features in C/C++ language, which allows programmers to directly manipulate memory to efficiently manage the memory - the most critical and scarce resource in computer - for best performance. However, "pointer" is also the most complex and difficult feature in C/C++ language.

Pointers are extremely powerful because they allows you to access addresses and manipulate their contents. But they are also extremely complex to handle. Using them correctly, they could greatly improve the efficiency and performance. On the other hand, using them incorrectly could lead to many problems, from un-readable and un-maintainable codes, to infamous bugs such as memory leaks and buffer overflow, which may expose your system to hacking. Many new languages (such as Java and C#) remove pointer from their syntax to avoid the pitfalls of pointers, by providing automatic memory management.

Although you can write C/C++ programs without using pointers, however, it is difficult not to mention pointer in teaching C/C++ language. Pointer is probably not meant for novices and dummies.

Pointer Variables

A computer memory location has an address and holds a content. The address is a numerical number (often expressed in hexadecimal), which is hard for programmers to use directly. Typically, each address location holds 8-bit (i.e., 1-byte) of data. It is entirely up to the programmer to interpret the meaning of the data, such as integer, real number, characters or strings.

To ease the burden of programming using numerical address and programmer-interpreted data, early programming languages (such as C) introduce the concept of variables. A variable is a named location that can store a value of a particular type. Instead of numerical addresses, names (or identifiers) are attached to certain addresses. Also, types (such as , , ) are associated with the contents for ease of interpretation of data.

Each address location typically hold 8-bit (i.e., 1-byte) of data. A 4-byte value occupies 4 memory locations. A 32-bit system typically uses 32-bit addresses. To store a 32-bit address, 4 memory locations are required.

The following diagram illustrate the relationship between computers' memory address and content; and variable's name, type and value used by the programmers.

Pointer Variables (or Pointers)

A pointer variable (or pointer in short) is basically the same as the other variables, which can store a piece of data. Unlike normal variable which stores a value (such as an , a , a ), a pointer stores a memory address.

Declaring Pointers

Pointers must be declared before they can be used, just like a normal variable. The syntax of declaring a pointer is to place a in front of the name. A pointer is associated with a type (such as and ) too.

type *ptr; type* ptr; type * ptr;

For example,

int * iPtr; double * dPtr;

Take note that you need to place a in front of each pointer variable, in other words, applies only to the name that followed. The in the declaration statement is not an operator, but indicates that the name followed is a pointer variable. For example,

int *p1, *p2, i; int* p1, p2, i; int * p1, * p2, i;

Naming Convention of Pointers: Include a "" or "" as prefix or suffix, e.g., , , , .

Initializing Pointers via the Address-Of Operator (&)

When you declare a pointer variable, its content is not initialized. In other words, it contains an address of "somewhere", which is of course not a valid location. This is dangerous! You need to initialize a pointer by assigning it a valid address. This is normally done via the address-of operator ().

The address-of operator () operates on a variable, and returns the address of the variable. For example, if is an variable, returns the address of the variable .

You can use the address-of operator to get the address of a variable, and assign the address to a pointer variable. For example,

int number = 88; int * pNumber; pNumber = &number; int * pAnother = &number;

As illustrated, the variable , starting at address , contains an value . The expression returns the address of the variable , which is . This address is then assigned to the pointer variable , as its initial value.

The address-of operator () can only be used on the RHS.

Indirection or Dereferencing Operator ()

The indirection operator (or dereferencing operator) () operates on a pointer, and returns the value stored in the address kept in the pointer variable. For example, if is an pointer, returns the value "pointed to" by .

For example,

int number = 88; int * pNumber = &number; cout << pNumber<< endl; cout << *pNumber << endl; *pNumber = 99; cout << *pNumber << endl; cout << number << endl;

Take note that stores a memory address location, whereas refers to the value stored in the address kept in the pointer variable, or the value pointed to by the pointer.

As illustrated, a variable (such as ) directly references a value, whereas a pointer indirectly references a value through the memory address it stores. Referencing a value indirectly via a pointer is called indirection or dereferencing.

The indirection operator () can be used in both the RHS () and the LHS () of an assignment statement.

Take note that the symbol has different meaning in a declaration statement and in an expression. When it is used in a declaration (e.g., ), it denotes that the name followed is a pointer variable. Whereas when it is used in a expression (e.g., ), it refers to the value pointed to by the pointer variable.

Pointer has a Type Too

A pointer is associated with a type (of the value it points to), which is specified during declaration. A pointer can only hold an address of the declared type; it cannot hold an address of a different type.

int i = 88; double d = 55.66; int * iPtr = &i; double * dPtr = &d; iPtr = &d; // ERROR, cannot hold address of different type dPtr = &i; // ERROR iPtr = i; // ERROR, pointer holds address of an int, NOT int value int j = 99; iPtr = &j;
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> using namespace std; int main() { int number = 88; int * pNumber; pNumber = &number; cout << pNumber << endl; cout << &number << endl; cout << *pNumber << endl; cout << number << endl; *pNumber = 99; cout << pNumber << endl; cout << &number << endl; cout << *pNumber << endl; cout << number << endl; cout << &pNumber << endl; }

Notes: The address values that you get are unlikely to be the same as mine. The OS loads the program in available free memory locations, instead of fixed memory locations.

Uninitialized Pointers

The following code fragment has a serious logical error!

int * iPtr; *iPtr = 55; cout << *iPtr << endl;

The pointer was declared without initialization, i.e., it is pointing to "somewhere" which is of course an invalid memory location. The corrupts the value of "somewhere"! You need to initialize a pointer by assigning it a valid address. Most of the compilers does not signal an error or a warning for uninitialized pointer?!

Null Pointers

You can initialize a pointer to 0 or , i.e., it points to nothing. It is called a null pointer. Dereferencing a null pointer () causes an exception.

int * iPtr = 0; cout << *iPtr << endl; // ERROR! STATUS_ACCESS_VIOLATION exception int * p = NULL;

Initialize a pointer to null during declaration is a good software engineering practice.

C++11 introduces a new keyword called to represent null pointer.

Reference Variables

C++ added the so-called reference variables (or references in short). A reference is an alias, or an alternate name to an existing variable. For example, suppose you make a reference (alias) to , you can refer to the person as either or .

The main use of references is acting as function formal parameters to support pass-by-reference. In an reference variable is passed into a function, the function works on the original copy (instead of a clone copy in pass-by-value). Changes inside the function are reflected outside the function.

A reference is similar to a pointer. In many cases, a reference can be used as an alternative to pointer, in particular, for the function parameter.

References (or Aliases) (&)

Recall that C/C++ use to denote the address-of operator in an expression. C++ assigns an additional meaning to in declaration to declare a reference variable.

The meaning of symbol is different in an expression and in a declaration. When it is used in an expression, denotes the address-of operator, which returns the address of a variable, e.g., if is an variable, returns the address of the variable (this has been described in the above section).

Howeve, when is used in a declaration (including function formal parameters), it is part of the type identifier and is used to declare a reference variable (or reference or alias or alternate name). It is used to provide another name, or another reference, or alias to an existing variable.

The syntax is as follow:

type &newName = existingName; type& newName = existingName; type & newName = existingName;

It shall be read as " is a reference to ", or " is an alias of ". You can now refer to the variable as or .

For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> using namespace std; int main() { int number = 88; int & refNumber = number; cout << number << endl; cout << refNumber << endl; refNumber = 99; cout << refNumber << endl; cout << number << endl; number = 55; cout << number << endl; cout << refNumber << endl; }

How References Work?

A reference works as a pointer. A reference is declared as an alias of a variable. It stores the address of the variable, as illustrated:

References vs. Pointers

Pointers and references are equivalent, except:

  1. A reference is a name constant for an address. You need to initialize the reference during declaration. int & iRef; Once a reference is established to a variable, you cannot change the reference to reference another variable.
  2. To get the value pointed to by a pointer, you need to use the dereferencing operator (e.g., if is a pointer, returns the value pointed to by . It is called dereferencing or indirection). To assign an address of a variable into a pointer, you need to use the address-of operator (e.g., ).
    On the other hand, referencing and dereferencing are done on the references implicitly. For example, if is a reference (alias) to another variable, returns the value of the variable. No explicit dereferencing operator should be used. Furthermore, to assign an address of a variable to a reference variable, no address-of operator is needed.

For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <iostream> using namespace std; int main() { int number1 = 88, number2 = 22; int * pNumber1 = &number1; *pNumber1 = 99; cout << *pNumber1 << endl; cout << &number1 << endl; cout << pNumber1 << endl; cout << &pNumber1 << endl; pNumber1 = &number2; int & refNumber1 = number1; refNumber1 = 11; cout << refNumber1 << endl; cout << &number1 << endl; cout << &refNumber1 << endl; refNumber1 = number2; number2++; cout << refNumber1 << endl; cout << number1 << endl; cout << number2 << endl; }

A reference variable provides a new name to an existing variable. It is dereferenced implicitly and does not need the dereferencing operator to retrieve the value referenced. On the other hand, a pointer variable stores an address. You can change the address value stored in a pointer. To retrieve the value pointed to by a pointer, you need to use the indirection operator , which is known as explicit dereferencing. Reference can be treated as a pointer. It has to be initialized during declaration, and its content cannot be changed.

Reference is closely related to pointer. In many cases, it can be used as an alternative to pointer. A reference allows you to manipulate an object using pointer, but without the pointer syntax of referencing and dereferencing.

The above example illustrates how reference works, but does not show its typical usage, which is used as the function formal parameter for pass-by-reference.

Pass-By-Reference into Functions with Reference Arguments vs. Pointer Arguments

Pass-by-Value

In C/C++, by default, arguments are passed into functions by value (except arrays which is treated as pointers). That is, a clone copy of the argument is made and passed into the function. Changes to the clone copy inside the function has no effect to the original argument in the caller. In other words, the called function has no access to the variables in the caller. For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> using namespace std; int square(int); int main() { int number = 8; cout << "In main(): " << &number << endl; cout << number << endl; cout << square(number) << endl; cout << number << endl; } int square(int n) { cout << "In square(): " << &n << endl; n *= n; return n; }

The output clearly shows that there are two different addresses.

Pass-by-Reference with Pointer Arguments

In many situations, we may wish to modify the original copy directly (especially in passing huge object or array) to avoid the overhead of cloning. This can be done by passing a pointer of the object into the function, known as pass-by-reference. For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> using namespace std; void square(int *); int main() { int number = 8; cout << "In main(): " << &number << endl; cout << number << endl; square(&number); cout << number << endl; } void square(int * pNumber) { cout << "In square(): " << pNumber << endl; *pNumber *= *pNumber; }

The called function operates on the same address, and can thus modify the variable in the caller.

Pass-by-Reference with Reference Arguments

Instead of passing pointers into function, you could also pass references into function, to avoid the clumsy syntax of referencing and dereferencing. For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> using namespace std; void square(int &); int main() { int number = 8; cout << "In main(): " << &number << endl; cout << number << endl; square(number); cout << number << endl; } void square(int & rNumber) { cout << "In square(): " << &rNumber << endl; rNumber *= rNumber; }

Again, the output shows that the called function operates on the same address, and can thus modify the caller's variable.

Take note referencing (in the caller) and dereferencing (in the function) are done implicitly. The only coding difference with pass-by-value is in the function's parameter declaration.

Recall that references are to be initialized during declaration. In the case of function formal parameter, the references are initialized when the function is invoked, to the caller's arguments.

References are primarily used in passing reference in/out of functions to allow the called function accesses variables in the caller directly.

"const" Function Reference/Pointer Parameters

A function formal parameter cannot be modified inside the function. Use whenever possible as it protects you from inadvertently modifying the parameter and protects you against many programming errors.

A function parameter can receive both and non- argument. On the other hand, a non- function reference/pointer parameter can only receive non- argument. For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> using namespace std; int squareConst(const int); int squareNonConst(int); int squareConstRef(const int &); int squareNonConstRef(int &); int main() { int number = 8; const int constNumber = 9; cout << squareConst(number) << endl; cout << squareConst(constNumber) << endl; cout << squareNonConst(number) << endl; cout << squareNonConst(constNumber) << endl; cout << squareConstRef(number) << endl; cout << squareConstRef(constNumber) << endl; cout << squareNonConstRef(number) << endl; cout << squareNonConstRef(constNumber) << endl; } int squareConst(const int number) { number *= number; return number * number; } int squareNonConst(int number) { number *= number; return number; } int squareConstRef(const int & number) { return number * number; } int squareNonConstRef(int & number) { return number * number; }

Passing the Function's Return Value

Passing the Return-value as Reference

You can also pass the return-value as reference or pointer. For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> using namespace std; int & squareRef(int &); int * squarePtr(int *); int main() { int number1 = 8; cout << "In main() &number1: " << &number1 << endl; int & result = squareRef(number1); cout << "In main() &result: " << &result << endl; cout << result << endl; cout << number1 << endl; int number2 = 9; cout << "In main() &number2: " << &number2 << endl; int * pResult = squarePtr(&number2); cout << "In main() pResult: " << pResult << endl; cout << *pResult << endl; cout << number2 << endl; } int & squareRef(int & rNumber) { cout << "In squareRef(): " << &rNumber << endl; rNumber *= rNumber; return rNumber; } int * squarePtr(int * pNumber) { cout << "In squarePtr(): " << pNumber << endl; *pNumber *= *pNumber; return pNumber; }
You should not pass Function's local variable as return value by reference
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> using namespace std; int * squarePtr(int); int & squareRef(int); int main() { int number = 8; cout << number << endl; cout << *squarePtr(number) << endl; cout << squareRef(number) << endl; } int * squarePtr(int number) { int localResult = number * number; return &localResult; } int & squareRef(int number) { int localResult = number * number; return localResult; }

This program has a serious logical error, as local variable of function is passed back as return value by reference. Local variable has local scope within the function, and its value is destroyed after the function exits. The GCC compiler is kind enough to issue a warning (but not error).

It is safe to return a reference that is passed into the function as an argument. See earlier examples.

Passing Dynamically Allocated Memory as Return Value by Reference

Instead, you need to dynamically allocate a variable for the return value, and return it by reference.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> using namespace std; int * squarePtr(int); int & squareRef(int); int main() { int number = 8; cout << number << endl; cout << *squarePtr(number) << endl; cout << squareRef(number) << endl; } int * squarePtr(int number) { int * dynamicAllocatedResult = new int(number * number); return dynamicAllocatedResult; } int & squareRef(int number) { int * dynamicAllocatedResult = new int(number * number); return *dynamicAllocatedResult; }

Summary

Pointers and references are highly complex and difficult to master. But they can greatly improve the efficiency of the programs.

For novices, avoid using pointers in your program. Improper usage can lead to serious logical bugs. However, you need to understand the syntaxes of pass-by-reference with pointers and references, because they are used in many library functions.

  • In pass-by-value, a clone is made and passed into the function. The caller's copy cannot be modified.
  • In pass-by-reference, a pointer is passed into the function. The caller's copy could be modified inside the function.
  • In pass-by-reference with reference arguments, you use the variable name as the argument.
  • In pass-by-reference with pointer arguments, you need to use (an address) as the argument.

Dynamic Memory Allocation

new and delete Operators

Instead of define an variable (), and assign the address of the variable to the pointer (), the storage can be dynamically allocated at runtime, via a operator. In C++, whenever you allocate a piece of memory dynamically via , you need to use to remove the storage (i.e., to return the storage to the heap).

The operation returns a pointer to the memory allocated. The operator takes a pointer (pointing to the memory allocated via ) as its sole argument.

For example,

int number = 88; int * p1 = &number; int * p2; cout << p2 << endl; p2 = new int; *p2 = 99; cout << p2 << endl; cout << *p2 << endl; delete p2;

Observe that and operators work on pointer.

To initialize the allocated memory, you can use an initializer for fundamental types, or invoke a constructor for an object. For example,

int * p1 = new int(88); double * p2 = new double(1.23); int * p1 = new int {88}; double * p2 = new double {1.23}; Date * date1 = new Date(1999, 1, 1); Time * time1 = new Time(12, 34, 56);

You can dynamically allocate storage for global pointers inside a function. Dynamically allocated storage inside the function remains even after the function exits. For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream> using namespace std; int * p1, * p2; void allocate() { p1 = new int; *p1 = 88; p2 = new int(99); } int main() { allocate(); cout << *p1 << endl; cout << *p2 << endl; delete p1; delete p2; return 0; }

The main differences between static allocation and dynamic allocations are:

  1. In static allocation, the compiler allocates and deallocates the storage automatically, and handle memory management. Whereas in dynamic allocation, you, as the programmer, handle the memory allocation and deallocation yourself (via and operators). You have full control on the pointer addresses and their contents, as well as memory management.
  2. Static allocated entities are manipulated through named variables. Dynamic allocated entities are handled through pointers.

new[] and delete[] Operators

Dynamic array is allocated at runtime rather than compile-time, via the operator. To remove the storage, you need to use the operator (instead of simply ). For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> #include <cstdlib> using namespace std; int main() { const int SIZE = 5; int * pArray; pArray = new int[SIZE]; for (int i = 0; i < SIZE; ++i) { *(pArray + i) = rand() % 100; } for (int i = 0; i < SIZE; ++i) { cout << *(pArray + i) << " "; } cout << endl; delete[] pArray; return 0; }

C++03 does not allow your to initialize the dynamically-allocated array. C++11 does with the brace initialization, as follows:

int * p = new int[5] {1, 2, 3, 4, 5};

Pointer, Array and Function

Array is Treated as Pointer

In C/C++, an array's name is a pointer, pointing to the first element (index 0) of the array. For example, suppose that is an array, is a also an pointer, pointing at the first element of the array. That is, is the same as . Consequently, is ; is .

For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> using namespace std; int main() { const int SIZE = 5; int numbers[SIZE] = {11, 22, 44, 21, 41}; cout << &numbers[0] << endl; cout << numbers << endl; cout << *numbers << endl; cout << *(numbers + 1) << endl; cout << *(numbers + 4) << endl; }

Pointer Arithmetic

As seen from the previous section, if is an array, it is treated as an pointer pointing to the first element of the array. points to the next , instead of having the next sequential address. Take note that an typically has 4 bytes. That is increases the address by 4, or . For example,

int numbers[] = {11, 22, 33}; int * iPtr = numbers; cout << iPtr << endl; cout << iPtr + 1 << endl; cout << *iPtr << endl; cout << *(iPtr + 1) << endl; cout << *iPtr + 1 << endl;

sizeof Array

The operation returns the total bytes of the array. You can derive the length (size) of the array by dividing it with the size of an element (e.g. element 0). For example,

int numbers[100]; cout << sizeof(numbers) << endl; cout << sizeof(numbers[0]) << endl; cout << "Array size is " << sizeof(numbers) / sizeof(numbers[0]) << endl;

Passing Array In/Out of a Function

An array is passed into a function as a pointer to the first element of the array. You can use array notation (e.g., ) or pointer notation (e.g., ) in the function declaration. The compiler always treats it as pointer (e.g., ). For example, the following declarations are equivalent:

int max(int numbers[], int size); int max(int *numbers, int size); int max(int number[50], int size);

They will be treated as by the compiler, as follow. The size of the array given in is ignored.

int max(int*, int);

Array is passed by reference into the function, because a pointer is passed instead of a clone copy. If the array is modified inside the function, the modifications are applied to the caller's copy. You could declare the array parameter as to prevent the array from being modified inside the function.

The size of the array is not part of the array parameter, and needs to be passed in another parameter. Compiler is not able to deduce the array size from the array pointer, and does not perform array bound check.

Example: Using the usual array notation.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <iostream> using namespace std; int max(const int arr[], int size); void replaceByMax(int arr[], int size); void print(const int arr[], int size); int main() { const int SIZE = 4; int numbers[SIZE] = {11, 22, 33, 22}; print(numbers, SIZE); cout << max(numbers, SIZE) << endl; replaceByMax(numbers, SIZE); print(numbers, SIZE); } int max(const int arr[], int size) { int max = arr[0]; for (int i = 1; i < size; ++i) { if (max < arr[i]) max = arr[i]; } return max; } void replaceByMax(int arr[], int size) { int maxValue = max(arr, size); for (int i = 0; i < size; ++i) { arr[i] = maxValue; } } void print(const int arr[], int size) { cout << "{"; for (int i = 0; i < size; ++i) { cout << arr[i]; if (i < size - 1) cout << ","; } cout << "}" << endl; }

Take note that you can modify the contents of the caller's array inside the function, as array is passed by reference. To prevent accidental modification, you could apply qualifier to the function's parameter. Recall that inform the compiler that the value should not be changed. For example, suppose that the function prints the contents of the given array and does not modify the array, you could apply to both the array name and its size, as they are not expected to be changed inside the function.

void print(const int arr[], int size);

Compiler flags out an error "assignment of read-only location" if it detected a value would be changed.

Example: Using pointer notation.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> using namespace std; int max(const int *arr, int size); int main() { const int SIZE = 5; int numbers[SIZE] = {10, 20, 90, 76, 22}; cout << max(numbers, SIZE) << endl; } int max(const int *arr, int size) { int max = *arr; for (int i = 1; i < size; ++i) { if (max < *(arr+i)) max = *(arr+i); } return max; }

Pass-by-Reference and sizeof

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> using namespace std; void fun(const int *arr, int size); int main() { const int SIZE = 5; int a[SIZE] = {8, 4, 5, 3, 2}; cout << "sizeof in main() is " << sizeof(a) << endl; cout << "address in main() is " << a << endl; fun(a, SIZE); } void fun(const int *arr, int size) { cout << "sizeof in function is " << sizeof(arr) << endl; cout << "address in function is " << arr << endl; }
sizeof in main() is 20 address in main() is 0x22fefc sizeof in function is 4 address in function is 0x22fefc

The address of arrays in and the function are the same, as expected, as array is passed by reference.

In , the array is 20 (4 bytes per , length of 5). Inside the function, the is 4, which is the pointer (4-byte address). This is why you need to pass the size into the function.

Operating on a Range of an Array

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> using namespace std; int sum(const int *begin, const int *end); int main() { int a[] = {8, 4, 5, 3, 2, 1, 4, 8}; cout << sum(a, a+8) << endl; cout << sum(a+2, a+5) << endl; cout << sum(&a[2], &a[5]) << endl; } int sum(const int *begin, const int *end) { int sum = 0; for (const int *p = begin; p != end; ++p) { sum += *p; } return sum; }

Program Notes:

  • To write a function that operates on a range of the given array, you can pass the begin pointer and the end pointer into the function. By convention, the operation shall start at the begin pointer, up to the end pointer, but excluding the end pointer.
  • In "", (content pointed-to) is constant, but is not constant.

C-String and Pointer

C-string (of the C language) is a character array, terminated with a null character . For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> #include <cstring> using namespace std; int main() { char msg1[] = "Hello"; char *msg2 = "Hello"; cout << strlen(msg1) << endl; cout << strlen(msg2) << endl; cout << strlen("Hello") << endl; int size = sizeof(msg1)/sizeof(char); cout << size << endl; for (int i = 0; msg1[i] != '\0'; ++i) { cout << msg1[i]; } cout << endl; for (char *p = msg1; *p != '\0'; ++p) { cout << *p; } cout << endl; }

Take note that for C-String function such as (in header , ported over from C's ), there is no need to pass the array length into the function. This is because C-Strings are terminated by . The function can iterate thru the characters in the array until . For example,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <iostream> #include <cstring> using namespace std; int count(const char *str, const char c); int main() { char msg1[] = "Hello, world"; char *msg2 = "Hello, world"; cout << count(msg1, 'l') << endl; cout << count(msg2, 'l') << endl; cout << count("Hello, world", 'l') << endl; } int count(const char *str, const char c) { int count = 0; while (*str) { if (*str == c) ++count; ++str; } return count; }

*More On Pointers

Function Pointer

In C/C++, functions, like all data items, have an address. The name of a function is the starting address where the function resides in the memory, and therefore, can be treated as a pointer. We can pass a function pointer into function as well. The syntax for declaring a function pointer is:

return-type (* function-ptr-name) (parameter-list) double (*fp)(int, int) double *dp; double *fun(int, int) double f(int, int); fp = f;
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> using namespace std; int arithmetic(int, int, int (*)(int, int)); int add(int, int); int sub(int, int); int add(int n1, int n2) { return n1 + n2; } int sub(int n1, int n2) { return n1 - n2; } int arithmetic(int n1, int n2, int (*operation) (int, int)) { return (*operation)(n1, n2); } int main() { int number1 = 5, number2 = 6; cout << arithmetic(number1, number2, add) << endl; cout << arithmetic(number1, number2, sub) << endl; }

Generic Pointer or void Pointer (void *)

A pointer can hold address of any data type (except function pointer). We cannot operate on the object pointed to by pointer, as the type is unknown. We can use a pointer to compare with another address.

[TODO] Example

Constant Pointer vs. Constant Pointed-to Data

  1. Non-constant pointer to constant data: Data pointed to CANNOT be changed; but pointer CAN be changed to point to another data. For example, int i1 = 8, i2 = 9; const int * iptr = &i1; // *iptr = 9; // error: assignment of read-only location iptr = &i2;
  2. Constant pointer to non-constant data: Data pointed to CAN be changed; but pointer CANNOT be changed to point to another data. For example, int i1 = 8, i2 = 9; int * const iptr = &i1; *iptr = 9; // iptr = &i2; // error: assignment of read-only variable
  3. Constant pointer to constant data: Data pointed to CANNOT be changed; and pointer CANNOT be changed to point to another data. For example, int i1 = 8, i2 = 9; const int * const iptr = &i1; // *iptr = 9; // error: assignment of read-only variable // iptr = &i2; // error: assignment of read-only variable
  4. Non-constant pointer to non-constant data: Data pointed to CAN be changed; and pointer CAN be changed to point to another data. For example, int i1 = 8, i2 = 9; int * iptr = &i1; *iptr = 9; iptr = &i2;
Link to "C++ References & Resources"

Ok I had this problem and I couldn't find a good answer so I'm going to share what I learned.

You could pass by value there is nothing wrong with that. (as you showed in your question.)

But the reason we pass the parameter by const reference is so the function doesn't make an actual copy of the value being called in. Its referenced, so its just pointing at that value wherever it is in the memory.

This saves processing time especially if its something big that has thousands of names... In this case the time saved would be almost nothing.

And for the const, that ensures the user of the function that the referenced value is not going to be changed because it could be changed since you have access to the original location in the memory because its passed by reference.. If your function definition actually changes the value of the parameter being called in by const reference , it will be a compiler error, it wont let you do that. because when you put const, you are telling the compiler this value cannot be changed.

answered Feb 28 '14 at 22:38

Leave a Comment

(0 Comments)

Your email address will not be published. Required fields are marked *