This document discusses pointers in C++. It begins by introducing pointers as a data type that stores the memory addresses of other variables. It then explains how pointer variables are defined using the asterisk (*) symbol and how they can be used to manipulate the data of other variables through dereferencing. The document provides an example C++ program to demonstrate how pointers work, showing how a pointer variable can be initialized with the address of another variable and then used to read from and write to that variable's memory location. It concludes by discussing pointer arithmetic operations like incrementing, decrementing, adding/subtracting integers to pointers, and subtracting one pointer from another.
This slide begins your formal investigation of the C# programming language by presenting a number
of bite-sized, stand-alone topics you must be comfortable with as you explore the .NET Framework.
This slide begins your formal investigation of the C# programming language by presenting a number
of bite-sized, stand-alone topics you must be comfortable with as you explore the .NET Framework.
● Introduction to components of a Computer System
● Introduction to Algorithm and Flowchart
● Keywords, Identifiers, Constants and Variables
● Data types in C
● Operators in C
● Basic Input and Output Operations
● Expressions and Precedence of Operators
● In-built Functions
C++ / CPP / C PLUS PLUS notes, object oriented programming using C++ / CPP / C PLUS PLUS, C++ / CPP / C PLUS PLUS tutorial, lecture notes, C++ / CPP / C PLUS PLUS programming notes, C++ / CPP / C PLUS PLUS example programs, C++ / CPP / C PLUS PLUS programs with explanation, C++ / CPP / C PLUS PLUS source code with output, C++ / CPP / C PLUS PLUS programs, C++ / CPP / C PLUS PLUS coding, C++ / CPP / C PLUS PLUS codes, C++ / CPP / C PLUS PLUS slides, C++ / CPP / C PLUS PLUS notes
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. A pointer references a location in memory, and obtaining the value stored at that location is known as dereferencing the pointer.
Pointers in C language is a variable that stores/points the address of another variable. A Pointer in C is used to allocate memory dynamically i.e. at run time.
general use of pointer
what is pointer in c language
uses is pointer in c language
representation of pointer in c language
syantax of pointer in c language
program of pointer in c language
● Introduction to components of a Computer System
● Introduction to Algorithm and Flowchart
● Keywords, Identifiers, Constants and Variables
● Data types in C
● Operators in C
● Basic Input and Output Operations
● Expressions and Precedence of Operators
● In-built Functions
C++ / CPP / C PLUS PLUS notes, object oriented programming using C++ / CPP / C PLUS PLUS, C++ / CPP / C PLUS PLUS tutorial, lecture notes, C++ / CPP / C PLUS PLUS programming notes, C++ / CPP / C PLUS PLUS example programs, C++ / CPP / C PLUS PLUS programs with explanation, C++ / CPP / C PLUS PLUS source code with output, C++ / CPP / C PLUS PLUS programs, C++ / CPP / C PLUS PLUS coding, C++ / CPP / C PLUS PLUS codes, C++ / CPP / C PLUS PLUS slides, C++ / CPP / C PLUS PLUS notes
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. A pointer references a location in memory, and obtaining the value stored at that location is known as dereferencing the pointer.
Pointers in C language is a variable that stores/points the address of another variable. A Pointer in C is used to allocate memory dynamically i.e. at run time.
general use of pointer
what is pointer in c language
uses is pointer in c language
representation of pointer in c language
syantax of pointer in c language
program of pointer in c language
Computers use their memory for storing instructions of the programs and the values of the variables. Memory is a sequential collection of storage cells. Each cell has an address associated with it. Whenever we declare a variable, the system allocates, somewhere in the memory, a memory location and a unique address is assigned to this location.c pointers lecture
Cyaniclab : Software Development Agency Portfolio.pdfCyanic lab
CyanicLab, an offshore custom software development company based in Sweden,India, Finland, is your go-to partner for startup development and innovative web design solutions. Our expert team specializes in crafting cutting-edge software tailored to meet the unique needs of startups and established enterprises alike. From conceptualization to execution, we offer comprehensive services including web and mobile app development, UI/UX design, and ongoing software maintenance. Ready to elevate your business? Contact CyanicLab today and let us propel your vision to success with our top-notch IT solutions.
Enhancing Research Orchestration Capabilities at ORNL.pdfGlobus
Cross-facility research orchestration comes with ever-changing constraints regarding the availability and suitability of various compute and data resources. In short, a flexible data and processing fabric is needed to enable the dynamic redirection of data and compute tasks throughout the lifecycle of an experiment. In this talk, we illustrate how we easily leveraged Globus services to instrument the ACE research testbed at the Oak Ridge Leadership Computing Facility with flexible data and task orchestration capabilities.
First Steps with Globus Compute Multi-User EndpointsGlobus
In this presentation we will share our experiences around getting started with the Globus Compute multi-user endpoint. Working with the Pharmacology group at the University of Auckland, we have previously written an application using Globus Compute that can offload computationally expensive steps in the researcher's workflows, which they wish to manage from their familiar Windows environments, onto the NeSI (New Zealand eScience Infrastructure) cluster. Some of the challenges we have encountered were that each researcher had to set up and manage their own single-user globus compute endpoint and that the workloads had varying resource requirements (CPUs, memory and wall time) between different runs. We hope that the multi-user endpoint will help to address these challenges and share an update on our progress here.
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTier1 app
Even though at surface level ‘java.lang.OutOfMemoryError’ appears as one single error; underlyingly there are 9 types of OutOfMemoryError. Each type of OutOfMemoryError has different causes, diagnosis approaches and solutions. This session equips you with the knowledge, tools, and techniques needed to troubleshoot and conquer OutOfMemoryError in all its forms, ensuring smoother, more efficient Java applications.
Listen to the keynote address and hear about the latest developments from Rachana Ananthakrishnan and Ian Foster who review the updates to the Globus Platform and Service, and the relevance of Globus to the scientific community as an automation platform to accelerate scientific discovery.
Quarkus Hidden and Forbidden ExtensionsMax Andersen
Quarkus has a vast extension ecosystem and is known for its subsonic and subatomic feature set. Some of these features are not as well known, and some extensions are less talked about, but that does not make them less interesting - quite the opposite.
Come join this talk to see some tips and tricks for using Quarkus and some of the lesser known features, extensions and development techniques.
How to Position Your Globus Data Portal for Success Ten Good PracticesGlobus
Science gateways allow science and engineering communities to access shared data, software, computing services, and instruments. Science gateways have gained a lot of traction in the last twenty years, as evidenced by projects such as the Science Gateways Community Institute (SGCI) and the Center of Excellence on Science Gateways (SGX3) in the US, The Australian Research Data Commons (ARDC) and its platforms in Australia, and the projects around Virtual Research Environments in Europe. A few mature frameworks have evolved with their different strengths and foci and have been taken up by a larger community such as the Globus Data Portal, Hubzero, Tapis, and Galaxy. However, even when gateways are built on successful frameworks, they continue to face the challenges of ongoing maintenance costs and how to meet the ever-expanding needs of the community they serve with enhanced features. It is not uncommon that gateways with compelling use cases are nonetheless unable to get past the prototype phase and become a full production service, or if they do, they don't survive more than a couple of years. While there is no guaranteed pathway to success, it seems likely that for any gateway there is a need for a strong community and/or solid funding streams to create and sustain its success. With over twenty years of examples to draw from, this presentation goes into detail for ten factors common to successful and enduring gateways that effectively serve as best practices for any new or developing gateway.
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptxrickgrimesss22
Discover the essential features to incorporate in your Winzo clone app to boost business growth, enhance user engagement, and drive revenue. Learn how to create a compelling gaming experience that stands out in the competitive market.
Enterprise Resource Planning System includes various modules that reduce any business's workload. Additionally, it organizes the workflows, which drives towards enhancing productivity. Here are a detailed explanation of the ERP modules. Going through the points will help you understand how the software is changing the work dynamics.
To know more details here: https://blogs.nyggs.com/nyggs/enterprise-resource-planning-erp-system-modules/
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Anthony Dahanne
Les Buildpacks existent depuis plus de 10 ans ! D’abord, ils étaient utilisés pour détecter et construire une application avant de la déployer sur certains PaaS. Ensuite, nous avons pu créer des images Docker (OCI) avec leur dernière génération, les Cloud Native Buildpacks (CNCF en incubation). Sont-ils une bonne alternative au Dockerfile ? Que sont les buildpacks Paketo ? Quelles communautés les soutiennent et comment ?
Venez le découvrir lors de cette session ignite
Accelerate Enterprise Software Engineering with PlatformlessWSO2
Key takeaways:
Challenges of building platforms and the benefits of platformless.
Key principles of platformless, including API-first, cloud-native middleware, platform engineering, and developer experience.
How Choreo enables the platformless experience.
How key concepts like application architecture, domain-driven design, zero trust, and cell-based architecture are inherently a part of Choreo.
Demo of an end-to-end app built and deployed on Choreo.
Code reviews are vital for ensuring good code quality. They serve as one of our last lines of defense against bugs and subpar code reaching production.
Yet, they often turn into annoying tasks riddled with frustration, hostility, unclear feedback and lack of standards. How can we improve this crucial process?
In this session we will cover:
- The Art of Effective Code Reviews
- Streamlining the Review Process
- Elevating Reviews with Automated Tools
By the end of this presentation, you'll have the knowledge on how to organize and improve your code review proces
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Globus
The Earth System Grid Federation (ESGF) is a global network of data servers that archives and distributes the planet’s largest collection of Earth system model output for thousands of climate and environmental scientists worldwide. Many of these petabyte-scale data archives are located in proximity to large high-performance computing (HPC) or cloud computing resources, but the primary workflow for data users consists of transferring data, and applying computations on a different system. As a part of the ESGF 2.0 US project (funded by the United States Department of Energy Office of Science), we developed pre-defined data workflows, which can be run on-demand, capable of applying many data reduction and data analysis to the large ESGF data archives, transferring only the resultant analysis (ex. visualizations, smaller data files). In this talk, we will showcase a few of these workflows, highlighting how Globus Flows can be used for petabyte-scale climate analysis.
Unleash Unlimited Potential with One-Time Purchase
BoxLang is more than just a language; it's a community. By choosing a Visionary License, you're not just investing in your success, you're actively contributing to the ongoing development and support of BoxLang.
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisGlobus
JASMIN is the UK’s high-performance data analysis platform for environmental science, operated by STFC on behalf of the UK Natural Environment Research Council (NERC). In addition to its role in hosting the CEDA Archive (NERC’s long-term repository for climate, atmospheric science & Earth observation data in the UK), JASMIN provides a collaborative platform to a community of around 2,000 scientists in the UK and beyond, providing nearly 400 environmental science projects with working space, compute resources and tools to facilitate their work. High-performance data transfer into and out of JASMIN has always been a key feature, with many scientists bringing model outputs from supercomputers elsewhere in the UK, to analyse against observational or other model data in the CEDA Archive. A growing number of JASMIN users are now realising the benefits of using the Globus service to provide reliable and efficient data movement and other tasks in this and other contexts. Further use cases involve long-distance (intercontinental) transfers to and from JASMIN, and collecting results from a mobile atmospheric radar system, pushing data to JASMIN via a lightweight Globus deployment. We provide details of how Globus fits into our current infrastructure, our experience of the recent migration to GCSv5.4, and of our interest in developing use of the wider ecosystem of Globus services for the benefit of our user community.
Globus Compute wth IRI Workflows - GlobusWorld 2024Globus
As part of the DOE Integrated Research Infrastructure (IRI) program, NERSC at Lawrence Berkeley National Lab and ALCF at Argonne National Lab are working closely with General Atomics on accelerating the computing requirements of the DIII-D experiment. As part of the work the team is investigating ways to speedup the time to solution for many different parts of the DIII-D workflow including how they run jobs on HPC systems. One of these routes is looking at Globus Compute as a way to replace the current method for managing tasks and we describe a brief proof of concept showing how Globus Compute could help to schedule jobs and be a tool to connect compute at different facilities.
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Globus
The U.S. Geological Survey (USGS) has made substantial investments in meeting evolving scientific, technical, and policy driven demands on storing, managing, and delivering data. As these demands continue to grow in complexity and scale, the USGS must continue to explore innovative solutions to improve its management, curation, sharing, delivering, and preservation approaches for large-scale research data. Supporting these needs, the USGS has partnered with the University of Chicago-Globus to research and develop advanced repository components and workflows leveraging its current investment in Globus. The primary outcome of this partnership includes the development of a prototype enterprise repository, driven by USGS Data Release requirements, through exploration and implementation of the entire suite of the Globus platform offerings, including Globus Flow, Globus Auth, Globus Transfer, and Globus Search. This presentation will provide insights into this research partnership, introduce the unique requirements and challenges being addressed and provide relevant project progress.
How Recreation Management Software Can Streamline Your Operations.pptxwottaspaceseo
Recreation management software streamlines operations by automating key tasks such as scheduling, registration, and payment processing, reducing manual workload and errors. It provides centralized management of facilities, classes, and events, ensuring efficient resource allocation and facility usage. The software offers user-friendly online portals for easy access to bookings and program information, enhancing customer experience. Real-time reporting and data analytics deliver insights into attendance and preferences, aiding in strategic decision-making. Additionally, effective communication tools keep participants and staff informed with timely updates. Overall, recreation management software enhances efficiency, improves service delivery, and boosts customer satisfaction.
In software engineering, the right architecture is essential for robust, scalable platforms. Wix has undergone a pivotal shift from event sourcing to a CRUD-based model for its microservices. This talk will chart the course of this pivotal journey.
Event sourcing, which records state changes as immutable events, provided robust auditing and "time travel" debugging for Wix Stores' microservices. Despite its benefits, the complexity it introduced in state management slowed development. Wix responded by adopting a simpler, unified CRUD model. This talk will explore the challenges of event sourcing and the advantages of Wix's new "CRUD on steroids" approach, which streamlines API integration and domain event management while preserving data integrity and system resilience.
Participants will gain valuable insights into Wix's strategies for ensuring atomicity in database updates and event production, as well as caching, materialization, and performance optimization techniques within a distributed system.
Join us to discover how Wix has mastered the art of balancing simplicity and extensibility, and learn how the re-adoption of the modest CRUD has turbocharged their development velocity, resilience, and scalability in a high-growth environment.
1. By: Asaye Chemeda Email: asayechemeda@yahoo.com 26
CHAPTER THREE
POINTERS
Introduction
The data types in C++ are broadly categorized into simple
data types, structured data types and pointers. We have
already seen the first two. In this chapter, we will deal
about the third categories of data types in C++, i.e.,
pointers. Their name is coined from the fact that these
data types point to another data. Pointer variables, or
shortly pointers, are data types which are derived based
on other data types. There is also no name associated with
the data type of pointers. However, they are considered
to be a separate data types in C++.
Pointer variables are used to store memory addresses of
the variables they point to. The variables which are
pointed to by pointers have a name for their data types,
unless they are pointers themselves. Even if the data type
of pointers has no name in C++, pointers are usually
considered to have the same data type as the variable they
point to and are defined as such. However, there are also
pointers called generic pointers with void data type.
These types of pointers can point to any variable with any
data type. They can also be assigned with other pointers
which point to any data type.
Since pointers refer to the memory addresses of other
variables, they are very powerful means for manipulation
of data in C++. Pointers will provide you the way by
which you can manipulate data which other conventional
approaches fail to do. Thus, they are very useful tools in
C++. However, they may also have undesirable
consequences unless they are handled carefully in a
program.
Defining Pointers
Pointers take the same data type but different content
than the variables which they point to. If so, how are these
pointer variables are defined? What makes them be
distinguished from non-pointer variables?
Pointer variables are distinguished from other non-
pointer variables by using the asterisk symbol, ‘*’, during
their definition. If the asterisk symbol is put in front of a
variable during its declaration, the compiler will consider
it to be a pointer. The asterisk symbol can appear
anywhere between the data type and the pointer variable.
However, it is a good practice to pre-attach it with the
pointer variable. The syntax used to define a pointer
variable is:
In the above declaration, the dataType , which can also
be void, is usually the same as the data type of the
variable that the pointerVariable points to.
Once a pointer variable is defined, how it is then used for
data manipulation? Before answering this question, we
will discuss about two operators which are often used
with pointers. They are the address of operator denoted
by the ampersand symbol, ‘&’ and the dereferencing or
at address or indirection operator denoted by the
asterisk symbol,’*’. The address of operator returns the
memory address of its operand while the dereferencing
operator returns the value stored at a given memory
location. To understand this a bit more, let us see the
following piece of code.
In the above code, an integer variable x is defined and
initialized with a value of 100 on line 1. As you know,
when values are declared, a memory space is allocated for
the variables during compile time. Those memory
locations have addresses. Let us say that the memory
address where x is stored, amongst others, is 1000. Let us
depict this diagrammatically as follows.
The above diagram shows that at memory location 1000,
an integer value of 100 is stored.
On line 2, a pointer variable p is declared by using the *
symbol. Which means that the variable p will store
memory address of another variable, i.e., p is declared to
point to another variable. The int data type that the
pointer variable is declared by indicates that the pointer
will point to a variable with int data type. Again, a separate
memory location will be allocated for this variable. Let us
say the memory location allocated for p has an address of
1003. When we depict this graphically, as in the above
diagram, we will have the following.
The above diagram shows that a memory address of 1003
is allocated for the additional variable p. Note that, the
pointer variable is not yet initialized and at this stage it can
point to any memory location. If your program tries to
2. By: Asaye Chemeda Email: asayechemeda@yahoo.com 27
access memory address 1003 which is not yet initialized
and use the address which it points to now, your program
may be directed to any memory location even those
having sensitive system data and your computer system
may crash. Line 3 shows how a pointer variable is
initialized.
On line 3, p is assigned with the address of variable x by
using the address of operator &. The part on the right
hand side of the assignment operator ( = ), ‘&x’, returns
the memory address of variable x. The assignment
operator will assign this address to the pointer variable p
on the left hand side. Which means that the memory
address of variable x, which is 1000, will be stored in the
memory address of p, which is 1003. In short, p is now
pointing to variable x. After this, the memory locations
will appear to be:
In the above diagram, the memory address 1003 now
contains another memory address 1000, which is the
memory address of variable x. After the initialization on
line 3, the variable p points to a specific known address
and you can use it without any harm to your computer.
On line 4, the value at address contained by variable p is
assigned with value of 25 by using the dereferencing
operator *. The part on the left hand size of assignment
operator of the statement, *p, refers to the value stored
at memory address contained by pointer variable p. The
memory address contained by p is 1000 and the value at
address 1000 was 100. Therefore, the statement on line 4
changes this value to 25. Diagrammatically, what happens
after line 4 is shown as follows:
In the above diagram, the value at address 1000 is now
changed to 25. Note that, memory address 1003 still
contains the memory location of variable x and further
changes can be made through the same procedure.
However, the pointer variable p can also be made to point
to another variable.
The way how pointers manipulate data in C++ is as
simple as what we discussed above. But that is not all
about pointers. Based on the above concept, we can
utilize pointers to access and manipulate data in different
ways. Before proceeding to those details, don’t you think
it is good if we can incorporate the above piece of code
into a full program and see the results? If that is also your
thought, let us see the following program.
Program 3.1[Correct].cpp
When the above program is run, the resulting output is
shown below. Note that, the address results are machine
dependent and you may obtain different addresses than
those shown below.
What we confirmed from the above output is that (1) The
address of x can be obtained by either the address of
operator & or by the pointer variable p which points to
the variable x (Line 9 of the program and line 1 of the output).
(2) A memory location is also allocated for the pointer
variable p which can be obtained by the & operator (Line
11 of the program and line 2 of the output). (3) The value of the
variable x can be obtained from the variable itself or from
its pointer variable p by using the dereferencing operator
*. (Line 13 of the program and line 3 of the output). (4) The
value of the variable x can be changed by using the *
operator and its pointer variable p. (Line 15 of the program
and line 4 of the output).
Have you noticed that pointers are variables themselves?
Their difference from other non-pointer variables are due
to the type of data they store being different from other
simple or structured data types. You may ask the
following string of questions. If pointers are variables, can
we perform arithmetic operations on them? If it is
possible, what do the results of arithmetic operations on
pointers refer to? Do the results even have any real
purpose in programming? All these questions will be
answered in the following topic.
Pointer Arithmetic
Besides being assigned with and compared to another
pointer variable, only the following arithmetic operations
can be performed on pointers.
1. Pointer variables can be incremented or
decremented.
3. By: Asaye Chemeda Email: asayechemeda@yahoo.com 28
2. Integer values can be added or subtracted from
pointers.
3. One pointer variable can be deducted from
another pointer variable.
Note that any other arithmetic operation is prohibited on
pointers. Understanding the above arithmetic operations
to a modest detail is helpful and they will be discussed
below.
Assigning pointer with another pointer or a value. A
pointer is not only assigned or initialized with the help of
the & operator. A pointer can also be assigned or
initialized with another pointer. After the assignment,
both the assigned and the assigning pointers refer to the
same memory location. Let us have a look at the following
piece of code.
On line 1, a character variable c is declared and initialized
with a character literal ‘A’. On line 2, pointers p1 and p2
are declared. On line 3, p1 is initialized with the address
of c. On line 4, p2 is assigned with p1. After line 4, both
p1 and p2 will point to c. What each line of the above
piece of code will do is graphically depicted as below.
Note that the memory addresses are arbitrary.
Pointers of any data type can also be assigned with only
one value, i.e. ‘0’. Pointers which are assigned with this
value are called null pointers and they point to nothing.
Null pointers can also be created by assigning pointers of
any data type with the word ‘NULL’. Null pointers are
conventionally used to avoid problems caused by
uninitialized pointers.
Comparison of pointer variables. A comparison can be
made between two pointers. In general, pointers can be
used in logical comparison statements. These
comparisons, however, are based on the equality or
otherwise of the addresses contained by pointers. For
instance, if p and q are two pointers,
• p == q returns true if the memory address in p
is equal to that in q or false otherwise.
• p < q returns true if the memory address in p is
less than that in q or false otherwise.
• p > q returns true if the memory address in p is
greater than that in q or false otherwise.
Such pointer comparisons might be useful if we are
interested to know whether two pointers refer to the same
memory address or not. However, the purpose of pointer
comparison is not limited to this and you can use the
concept whenever appropriate.
Incrementing or decrementing of pointers. The values
of pointers can be incremented by the ++ operator or
decremented by the -- operator. What the incrementing
or decrementing operations will do the pointer variable is
interesting. When a pointer is incremented or
decremented, the value by which the pointer will do as
such depends on the data type of the pointer. For
character pointers which point to characters which always
have 1byte size, an increment adds one to the pointer
where as a decrement subtracts one from the pointer. For
integer pointers which point to int variables which
normally have 4byte size, an increment adds four to the
pointer where as a decrement subtracts four from the
pointer. In short, the increment or decrement of pointers
is done by an amount equal to the memory size allocated
for a single base data type.
Usually, memory addresses correspond to hexadecimal
numbers. However, for simplicity purposes, we will use
memory addresses with integer values and we will see how
incrementing and decrementing is done for pointers
which point to different data types. Since the procedure is
similar for different data types, we will only deal with char
and int data types.
Increment and decrement in char pointers
Let us refer to the following piece of code.
On line 1 of the above of code, character variables c1,
c2 and c3 are declared and initialized with character
literals ‘A’, ‘B’ and ‘C’ respectively. On line 2, a pointer
pChar with char data type is declared and initialized with
address of c1. At this point, pChar points to c1. Let us
assume that c1, c2 and c3 are stored in contiguous
4. By: Asaye Chemeda Email: asayechemeda@yahoo.com 29
memory locations with addresses of 2000, 2001 and 2002
respectively and pChar is stored at memory address
2004. At this point, pChar and *pChar are equal to
2000 and ‘A’ respectively. This is depicted in a diagram
as follows.
When pChar is incremented on line 3, the memory
address in pChar will be incremented by the amount of
the memory size of a single character variable, which is 1.
After the increment, the memory address in pChar
becomes 2001. Which means that pChar now points to
c2. Thus, pChar and *pChar are equal to 2001
and ‘B’ respectively. Diagrammatically, this is represented
as:
When pChar is incremented on line 4 one more time,
the memory address in pChar will be incremented by 1.
After the increment, the memory address in pChar
becomes 2002. Which means that pChar now points to
c3. Thus, pChar and *pChar are equal to 2002
and ‘C’ respectively. When this is put in diagram, we will
have:
When pChar is decremented on line 5, the memory
address in pChar will be decremented by 1 and becomes
2001. Which means that pChar now points to c2
again. Thus, pChar and *pChar are equal to 2001
and ‘B’ respectively. When this is put in diagram, we will
have:
Increment and decrement in int pointers
Let us refer to the following piece of code.
On line 1 of the above of code, int variables i1, i2 and
i3 are declared and initialized with integer values ‘75’,
‘83’ and ‘49’ respectively. On line 2, a pointer pInt with
int data type is declared and initialized with address of i1.
At this point, pInt points to i1. Since the memory size
of int data types is normally 4bytes, each integer variable
requires four memory cells. Let us assume that i1, i2
and i3 are stored in contiguous memory locations with
the first cell addresses of 10, 14 and 18 respectively and
pInt is stored at memory address 26. At this point,
pInt and *pInt are equal to 10 and 75 respectively.
This is depicted in a diagram as follows. The memory
addresses are written for alternate cells to avoid
congestion of characters.
When pInt is incremented on line 3, the memory
address in pInt will be incremented by the amount of
the memory size of a single int variable, which is 4. After
the increment, the memory address in pInt becomes 14
not 11. Which means that pInt now points to the next
int variable which is i2. Thus, pInt and *pInt
are equal to 14 and 83 respectively. Diagrammatically,
this is represented as:
When pInt is incremented on line 4 one more time, the
memory address in pInt will be incremented by 4. After
the increment, the memory address in pInt becomes 18.
Which means that pInt now points to i3. Thus,
pInt and *pInt are equal to 18 and 49
respectively. When this is put in diagram, we will have:
When pInt is decremented on line 5, the memory
address in pInt will be decremented by 4 and becomes
14. Which means that pInt now points to i2 again.
Thus, pInt and *pInt are equal to 14 and 83
respectively. When this is put in diagram, we will have:
Isn’t the way that the compiler handles incrementing and
decrementing operations on pointers fascinating? Indeed
5. By: Asaye Chemeda Email: asayechemeda@yahoo.com 30
it is. But, have you noted this? In the above examples we
assumed that the variables are stored in contiguous
(interconnected) memory locations. But, is that always the
case? Not necessarily. Simple data type values may not be
stored as such. As a result, it may be difficult to keep track
of the memory locations for variables belonging to simple
data types and trying to use the incrementing and
decrementing operators to access values of such data
types may result in wrong calculation results or data
corruption. If so, what is the importance of incrementing
and decrementing pointers?
Here is the answer for the above question. Some data
types are stored in the main memory in a structured
manner. Structured data types such as arrays are good
example for this. When elements of arrays are stored in
the main memory, the compiler will allocate contiguous
memory locations for them. Therefore, if the address of
the first element is known, the address of the other
elements can easily be tracked based on the memory size
requirement for each element.
As you may recall, the identifier used for defining arrays
refers to the address of the first element. In other words,
the identifier of an array is a pointer to the first array element. The
identifier can be assigned to a pointer. Based on a pointer
which is assigned with identifier of an array, the memory
addresses of all the other elements can easily be obtained
through incrementing and decrementing. You see, that is
where the incrementing and decrementing operations on
pointers become very useful.
In chapter two, we have seen how arrays are passed to
functions. Do you remember how it was done? It was by
reference, right? After our recent discussion, do you see
another way by which arrays can be passed to functions?
Let me give you a hint. Just remember how the following
four things are done. How value is transferred from the
argument passed to the formal parameter list of the
function when a function is called. How pointers are
declared. What the identifier of an array refers to. And
how pointer increment and decrement is done.
When we revise how the above four things are done, we
will get the following. When a function is called by passing
arguments, the called function defines and assigns the
variables in its formal parameter list with the passed
arguments according to their order. Pointer variables are
declared by pre-attaching * to the name of the pointer
variable. The identifier of an array is a pointer to the first
element of the array. Pointer incrementing and
decrementing is normally done to access and manipulate
data in contiguous memory locations by increment and
decrement operators.
Now, let us see how we can take the above knowledge to
our advantage to pass arrays to functions and manipulate
the elements of the array. When a function call is made to
pass an array, let the calling function pass the identifier of
the array. Let us make the corresponding argument on the
formal parameter list of the called function to be a
pointer. Which means that, during function call, the array
identifier, which is a pointer, will be assigned to the
corresponding pointer variable in the called function.
Inside the body of the called function, all the array
elements can now be accessed and manipulated by
incrementing and decrementing the pointer which is
assigned by the array identifier. Don’t you think it is a
good strategy? Of course it is. This way of passing arrays
to functions is termed as passing arrays by pointers and
if the program is well-written, it is the neatest way to pass
arrays. Note that, the changes made on the pointers of
array elements inside the body of the function is also
reflected in the calling environment. The above way of
passing arrays is applicable for one-dimensional arrays.
Passing two-dimensional arrays can also be done through
pointers. But, let us keep discussing this concept aside
until we start dealing with the ‘pointers and arrays’ topic.
I feel that we have discussed lots of concepts and we need
to take a break. Do you also feel the same? However, in
the meantime, let us write a program through which we
will be able to understand what we discussed so far about
arithmetic operations on pointers as well as about passing
arrays by pointers. Here is the program.
Program 3.2 [Correct].cpp
6. By: Asaye Chemeda Email: asayechemeda@yahoo.com 31
The above program calculates the cube roots of the first
five positive perfect cubes by the cubeRoot function
and prints the cube roots by the print function. We will
only discuss the important points in the program.
On line 11, the cubeRoot function is called and the
identifier of the cube array is passed as an argument.
Technically, what is passed as an argument is a pointer to
the first element of the cube array. When the
cubeRoot function is invoked by this call, the first thing
the function is going to do is assigning its formal
parameter accordingly with the passed argument. In C++
statement, this assignment is represented as:
Isn’t this how a pointer is declared initialized with another
pointer? Of course it is. In the above declaration, we need
to understand that the data type of c is double and it can
only be initialized with a pointer of only double data type.
After the above declaration the pointer c will contain the
memory address of the first element of the cube array.
On line 17, the cube root of the value at address contained
by c is calculated and stored in the same address
contained by c. On line 18, the pointer is incremented.
After each increment, the pointer c will point to the next
element in the contiguous memory location, i.e., the next
element of cube array. The for-loop controls for how
many times that the increment will be done. When the
loop is complete, all the elements of the cube array will
now contain the cube roots of the corresponding original
values. This change is also reflected in the main function
where the cubeRoot function is called. Therefore,
when the array identifier cube is now passed to the print
function on line 12, it refers to the memory address of the
first element of the changed cube array elements not those
elements given on line 10.
Note that the data type of the formal parameter of the
print function is void. This means that, the cubeRt
pointer is a void pointer which can be assigned with
another pointer which points to a variable of any data
type. However, to access and manipulate the data
contained by the cubeRt pointer, explicit type casting
should be made. Type casting is conversion of one data
type into another. One way to do this in C++ is by putting
the data type to be converted to within brackets in front
of the variable which we want to convert its data type as
it was done on line 22. However, type casting may not be
applicable for conversion between some data types. Note
that the casting on line 22 could also be done by using the
static_cast key word and the data type to be casted
to within angled brackets as in the following statement:
On line 22 of the program, the void cubeRt pointer is
casted or converted to double pointer and the casted
pointer is assigned to a double pointer named x. Through
pointer x, the elements of the array x can now be
accessed and printed by using the increment operator.
The part of the statement on line 24, *x++, means access
the value stored at address contained by x and increment
the pointer x to the next value.
I hope the above example clears any doubts you might
have regarding pointer incrementing and passing one-
dimensional arrays to functions as pointers. Remember,
we haven’t yet covered all the possible arithmetic
operations on pointers; two more are remaining.
Adding or subtracting integers from pointer
variables. If you understand how incrementing and
decrementing of pointers is done, understanding how
adding or subtracting of integers is done is pretty easy. As
you know, when incrementing is done by the ++ operator,
the value of the variable on which the incrementing is
done will be increased by sizeof(base data type of the
variable). The keyword sizeof is a reserved C++ word
used to return the size of the memory space allocated for
objects. The value sizeof(base data type) of may not
merely mean one. It may mean one for char pointers,
four for int pointers or eight for double pointers.
Adding an integer i to a pointer, therefore, means adding
i times size of the pointer variable. The same is true for
subtracting integers. Let us see this through the following
piece of code.
In the above piece of code, a double variable x is defined
and initialized on line 1. On line 2, a double pointer p is
defined and initialized with the address of x. Say the
address of x is 100. After line 2, the value of p will be 100.
Double data types normally require 8bytes. A single
increment on pointer p by ++ will result in a value of p
to be 108. However, on line 3, the value of p is increased
by 10. What do you think will the value of p become after
line 3 when the value of p after line 2 is 100? Are you
saying 110? If you say so, you might have forgotten how
incrementing of pointers is done.
When 10 is added to a double data type pointer variable,
the value of the pointer will be incremented by 10 times
sizeof(double) (i.e., 8). In other words, when 10 is
added to pointer p, the value of the pointer will be
incremented by 80. Therefore after line 3, the value of p
becomes 180.
Subtracting integers from pointers is also done in a similar
procedure.
7. By: Asaye Chemeda Email: asayechemeda@yahoo.com 32
Deducting two pointer variables. Two pointers
pointing to the same data type may also be deducted from
each other. The difference between two pointers results
in an integer value. The absolute value of the resulting
integer represents the number of objects of the base data
type between the pointers. The difference between two
pointers, however, cannot be assigned to another pointer
even if the difference is equal to zero.
We have now seen the only possible arithmetic operations
on pointers. The following operations, amongst others,
are prohibited.
• Adding two pointers
• Assigning non-zero values to pointers
• Adding or subtracting floating-point data type
values from pointers.
• Multiplying or dividing two pointers.
At this point, we have discussed the basics of pointers and
pointer operations. Let us proceed further and discuss
other details. Have you ever imagined that there could
also be array of pointers? Indeed, there are arrays of
pointers too.
The concept of arrays of pointers integrates the concepts
of both arrays and pointers. Arrays of pointers are arrays
which contain pointers as elements. They are declared and
used in the same way as other arrays. The only difference
is the asterisk symbol should be used to tell the compiler
that the array is an array of pointers. For instance, an array
of int pointers x having 10 elements can be declared as
follows.
What would x store if it was just an int array declared
without the asterisk? Ten integer values, right? Because of
the asterisk, however, x is declared as a pointer array and
it stores ten memory addresses with each memory address
pointing to an int variable. After this declaration, x can
be initialized with memory addresses of other variables. If
we want to initialize the fourth element of x with the
address of an int variable y, we can write the following:
Now, if we want to initialize the value of y by 76, we can
use the following statement:
We have discussed that pointers point to other variables
which have their own data types. The interesting question
here is: can pointers point to another pointers? Of course,
they can. Simply, ask yourself what the name of array of
pointers represents. If you understand what kind of
pointer that a name of array of pointers is, reading the
next topic might not be necessary. Otherwise, go through
the next topic for a better understanding.
Multiple Indirection
Pointers are variables themselves and a memory location
is allocated for them. If a pointer stores the memory
location allocated for another pointer variable, the pointer
is pointing to another pointer variable. In C++, the
process of pointing at another pointer is known as
multiple indirection. The pointer with multiple
indirection is known as pointer to a pointer. The process
of multiple indirection can be repeated as many as we
want although more than three multiple indirection is
rarely used in programming.
Pointers to pointers can be defined in the same way as
other pointers with only one indirection. The only
difference is, as many asterisk symbols as the number of
indirections will be put between the pointer and data type
of the pointer. The data type of pointers with multiple
indirection will be the same as the data type of the target
value.
A pointer with two indirections is declared with the
following syntax.
In the above declaration, the two asterisk symbols
between the pointerVariable and dataType
indicate that the pointerVariable will point to
another pointer.
Pointers to pointers can only be assigned with address of
other pointers or ‘0’. The way how we use the address of
operator (&) and the indirection operator (*) while using
pointers to pointers is the same as simple pointers. The
target value can also be accessed and manipulated by
pointers to pointer by using as many indirection operators
as they are used to define the pointer to pointer.
It is easy, right? If not, the following simple program will
make it so.
Program 3.3[Correct].cpp
8. By: Asaye Chemeda Email: asayechemeda@yahoo.com 33
In the above program, a float variable x is defined and
initialized on line 6. A pointer p and a pointer to pointer
pTp are defined on line 7. Note that, if p is supposed to
point to x and if pTp is supposed to point to p, the data
type of pTp will also be the same as the data type of x.
that is the reason why p and pTp were defined as such.
On line 8, the address of variable x is assigned to p and
on line 9, the address of p is assigned to pTp. Note that,
trying to assign pTp with address of a non-pointer
variable such as x rather than with address of a pointer
would have resulted in compilation error.
From the above program, the main expected outcome is
that the output of line 10 and line 13 to be the same. If
you understand why this should be, it means that you have
understood multiple indirection. If not, there should be
no worries as we will go through the output of the five
cout statements.
The output of the statement on line 10 is the address of
the variable x contained in p. Since p is assigned with the
address of p on line 8, displaying the value of p by cout
statement will result in displaying the memory address of
x.
The value of pTp will be displayed by the cout
statement on line 11. The output of this line is the
memory address of p contained in pTp. This is because
pTp is assigned with the address of p on line 9.
Through the indirection operator, the value at address
contained in p will be displayed as an output of line 12.
What is address contained in p? It is the address of
variable x, right? Therefore, *p access the value of x.
When we come to the statement on line 13, a value
referred by *pTp will be printed. This means the value at
address contained by pTp will be displayed. On line 9,
pTp is assigned with the address of p. Therefore, pTp
contains the address of p and *pTp accesses the value at
the address contained by pTp which is the value of p
which is also the address of x. Therefore, *pTp and p
refer to the same thing and the output of line 10 and line
13 will be the same.
The value of x can also be accessed through pTp by
applying the same number of indirection operators as it
was used to define pTp. The cout statement on line 14,
which prints the value of **pTp, actually prints the value
of x. We have seen above that when the * is pre-attached
once on pTp, it accesses p, when another one is pre-
attached, the value of x will be accessed.
Although the memory addresses are machine dependent,
the output of the above program is shown below. As can
be observed, the memory addresses printed on the first
and forth lines of the output are the same which asserts
our explanation above.
I guess you have mixed feelings about multiple
indirections. On one hand, you may wonder how pointers
to pointers work to access and manipulate values. One the
other hand, you may be curious about what the purpose
of defining pointers to pointers is. Am I right? If so, my
suggestion is to keep on your wondering about multiple
indirections. And the following topic will answer your
curiosity.
Pointers and Arrays
In C++, pointers and arrays are interrelated. The second
chapter on arrays and our discussion so far in this chapter
about pointers have provided us the subtle differences
between them. In C++ programming, pointers are often
used to access and manipulate array elements. Arrays can
also be used as pointers. In our discussion about arrays in
chapter two, we have said that array names refer to the
memory locations of the first array elements. Therefore,
array names are pointers themselves. Even if array names
are pointers, you can’t perform pointer arithmetic on
them which attempts to change their values. In general,
array names are constant pointers.
You may ask, in what way are arrays and pointers are
interrelated? We have already seen how one-dimensional
arrays and pointers can be interrelated during our
discussion on incrementing and decrementing pointers.
The concepts which were not discussed then, will be
covered in this topic.
Let us start discussing more about the relationship
between arrays and pointers for one dimensional arrays.
Consider the following one-dimensional array
declaration:
In the above array the name numbers is a pointer to the
first array element, i.e. it contains the memory address of
the value 10. Through indexing, the first element is
accessed by numbers[0]. Therefore, the following
logical statement returns true.
9. By: Asaye Chemeda Email: asayechemeda@yahoo.com 34
The above logical statement compares the equality
between numbers and the address of numbers[0].
Since the returned value is true, it means that
numbers contains the address of numbers[0]. Thus,
numbers is a pointer. If numbers is dereferenced, it
gives the value of the first array element. The following
logical statement will, therefore, return true.
The first array element can also be changed to another int
value, say 15, by using the array name through the
following statement.
So far so good. The array name, numbers, is behaving
exactly the same as other regular pointers. Can we
conclude that array name can be used as like any other
pointer? Before rushing to the conclusion, let us apply
some arithmetic operations on numbers and let us see
how it behaves.
First, let us apply the incrementing operation on
numbers and let us point to the next element of the
array. Recall that incrementing pointers will make them
point to the next element in contiguous memory
locations. Trying to point to the next element of the
numbers array through the following statement,
however, results in compilation error.
Array names are not like other pointers, after all. Can you
guess what the reason for the compilation error could be?
It is the ++ operator. Since the data type of the array is
int, the above statement attempted to increment the value
of numbers by sizeof(int). But, array names are
constant pointers and their values cannot be changed and
they only point to the first array element. This rule is not
limited for only incrementing, any other arithmetic
operation which attempts to change the value of an array
name is illegal. We can now conclude that array names are
pointers with restrictions.
If incrementing and decrementing is illegal on array
names, we may think that, they are useless to point to
other array elements than the first one. However, that is
not true at all. Array names can also be used to point to
all array elements. The reason why the above statement
failed to point to the next array element is because the ++
operator attempted to change the value of numbers,
which is a constant pointer. Any valid arithmetic
operation which doesn’t attempt to change the value of
numbers can point to other elements of the array as
well. Let us modify the above increment statement by this
one:
Both the statements on Line 1 and Line 2 add
sizeof(int) to the value of numbers. The only
difference between them is, the statement on Line 2
doesn’t attempt to change the value of numbers while the
statement on Line 1 does. Therefore, the statement on
Line 2 is legal and it points to the second element of
numbers array. If we dereference it, the second element
of numbers array will be returned. For instance, the
following statement prints the value of the second
element of the array:
And the following statement changes the value of the
fourth element of the array to 32.
By using the name of the array, we accessed and
manipulated the array elements which we wanted. It was
fascinating, right? The above example shows how one-
dimensional arrays can be used as pointers and the
associated restrictions while doing so. Can we say that
one-dimensional arrays and pointers are the same except
the arithmetic restrictions on array names? Not really.
There are subtle differences between them. We can pick
two, for instance. The first one is the difference in the
memory address of pointers themselves and the memory
address referred by array names. The other is
initialization.
We have seen that normal pointers have their own
memory space allocated for them. However, a separate
memory space is not allocated for array names, although
they are considered as pointers. The memory address
associated with array names is that of the first array
element. In general, the memory returned by the address of
operator on one-dimensional array names is the same the memory
address of the first element.
We have also seen that arrays can be initialized during or
after declaration (except for character arrays) using braces.
However, pointers cannot be initialized with braces even
if they can be used as arrays.
Having the above differences between arrays and
pointers, let us now consider character arrays in
accordance with the above concepts. Do you remember
what we said about character arrays in chapter two? We
said that they are given special consideration in C++. One
of the ways in which they are treated differently is, the
whole character array can be printed by printing the array
name. With the concept in mind, let us now investigate
what made character arrays so special in C++. For this,
we will consider the following character array.
10. By: Asaye Chemeda Email: asayechemeda@yahoo.com 35
To check that the array name program only refers to
the address of the first element, we can see the truth
values of the following logical statements.
From the above, only the statement on Line 1 returns
true. Hence, the name program only points to the
first elements of the array. The following statements will
print characters ‘C’, ‘+’ and ‘+’ respectively.
Any of the elements of the character array can also be
manipulated by using the dereferencing operator.
So far, we are getting what we expected and everything
seems normal. The character array name program is
behaving just like any other array name. If so, what do you
expect the output of the following statement will be?
If the character array name program behaves like other
array names, what is printed by the above statement
would be the memory address of the first program
array element. But, that is not what will be printed. All the
array elements, and probably additional characters, will
be printed. Even if we expect memory address of the
character ‘C’ to be printed, all the characters in the array
will printed as a string. Mysterious, isn’t it?
The reason for the above mystery to happen will be
solved by analyzing property of the ‘<<’ operator. The
way how the ‘<<’ operator will respond when it is called
into action is implemented in the iostream.h file. In
the iostream.h, there are a number of overloaded
functions to handle the responses when this operator is
called in a program.
Recalling our discussion on functions in chapter one,
overloaded functions have the same name but different
signatures. The overloaded function to handle the ‘<<’
operator is overloaded with several data types including
pointers.
Do you remember rule 1 of overloading which we
discussed? The rule can be summarized as: when an
overloaded function is called, the compiler tries to invoke
the function with the best match formal parameter as the
passed argument. Based on this rule, when pointers are
passed to the function responding to calls made to ‘<<’
operator, there are two alternative overloaded functions
with pointer formal parameters to be invoked. One of
them has a data type for its formal parameter as const
void* while the other one has const char*. When
names of non-character arrays are passed as an argument,
the overloaded function with the const void* as a
formal parameter data type will be invoked. As a result,
the memory address contained in the passed pointer,
which is the array name, will be printed by the ‘<<’
operator.
However, when the name of character arrays is passed,
the overloaded function with const char* as a data
type for the formal parameter will be invoked. This
function doesn’t print the memory address contained in
the name of the passed character array. Rather, all the
characters in the array until the null character is
encountered will be printed. If the character array is not
null-terminated, additional characters may also be printed.
Mystery solved!
After all, character arrays behave in the same way as other
arrays. The separate functions which handle exclusively
character arrays, however, treat them differently and
create the illusion that character arrays are different from
others.
We have seen how one-dimensional arrays can be used as
pointers. Now, let us see the reverse- how pointers can be
used as one-dimensional arrays. Again, we will consider a
fragment of a code. Consider the following string array
and string pointer.
In the above code fragment, the name of the string array,
str, which is actually a pointer to the first element of the
array, “C++” is assigned to a string pointer, p. If p is
dereferenced after Line 3, the return will be the string
“C++”. If you answer what the following statement prints
correctly, it means that you have fresh memory of
arithmetic operations on pointers.
In the above statement, the value of p pointer is pre-
incremented by sizeof(string) and then
dereferenced. The pre-increment will make p point to the
second element of str array before being dereferenced.
The dereferencing returns the value of the second
element of the str array. Therefore, the above statement
prints the string “is”.
Is that how a pointer is used as an array? If so, where is
the array subscripting operator ([]) used? These are good
11. By: Asaye Chemeda Email: asayechemeda@yahoo.com 36
questions. Indeed, accessing array elements without
pointers is characterized by the usage of indexing through
the [] operator. The way the pointer p was used above
to access the elements of str array doesn’t show that
pointers can be used as arrays.
Do you remember how name of array was used as a
pointer to access array elements? The basic idea at that
time was array name is a pointer and by dereferencing the
array name with integer addition or subtraction, the array
elements can be accessed and manipulated. Now, let us
see how array elements are accessed by indexing using the
array name. Since indexing starts from zero, str[0]
refers to the value of the first element of str array,
str[1] refers to the second and so on.
By initializing the p pointer with array name str, we
have synchronized them to refer to the memory address
of the first element of str array. Now, let us try to use
the pointer p as str the way that str could be used as
p to access the array elements. If this is possible, i.e. if we
can access and manipulate elements of str array by
indexing p pointer without using the dereferencing
operator, we can say that pointers can indeed be used as
arrays.
Of course, it is possible to access and manipulate elements
of one-dimensional arrays by indexing pointers. For
instance, p[0] refers to the value of the first element of
str array. The other elements can also be accessed and
manipulated in a similar manner. The following statement
changes the third element of str array from “fun” to
“awesome”.
Note that, in the above statement, the array subscripting
not the dereferencing operator was used on the pointer p
to change the third str element. Note that using index of 2
with pointers may not necessarily refer to the third array element. It
only means the third element from which the pointer is initialized
with the address of. For instance, if a pointer is initialized
with the address of the second element of the array, using
index of 2 with the pointer refers the fourth element of
the array not the third.
Don’t you feel convinced that pointers can be used as
arrays? Hopefully, you do. Now, think about details.
Consider the following program and guess what will be
printed by the cout statements. Note that p is initialized
with the address of the second element of str array. If
you answered both of them correctly, you have
completely understood what we discussed so far about
pointers and arrays.
Program 3.4[Correct].cpp
In the above program, p is initialized with the address of
the second element of str array. Therefore, the indexing
for p starts from the second element of str array. Based
on this, p[0] refers the string “is” and p[1] refers to
the third element of str array. Thus, the output of the
cout statement on line 9 is the string “awesome”.
The array name, however, always refers to the memory
address of the first element. When 1 is added to an array
name and dereferenced, the second element of the array
is always returned. The output of the cout statement on
line 10 is, therefore, the string “is”.
One-dimensional arrays and pointers can be
interchangeably used to access and manipulate array
elements without much difference between them. Is this
also the case for two-dimensional arrays? Can we apply
what we discussed so far about pointers and arrays for
two-dimensional arrays too? The answer for this is a
mixed yes and no. before looking into how two-
dimensional arrays can be used as pointers or vice-versa,
let us see how elements of two-dimensional arrays are
stored in the memory.
When we consider two-dimensional arrays, what we
normally visualize is simple data type variables arranged
in two dimensions. If the array has (m+1) rows and (n+1)
columns, we may expect it to be arranged as below in the
memory.
Figure 1: Two-dimensional array representation
The question is: is this how two-dimensional arrays are
actually stored in the memory? In two dimensions with
rows and columns? Not really. Two-dimensional arrays
are stored in the memory as one-dimensional arrays. The
above two-dimensional array will be stored in the memory
as:
12. By: Asaye Chemeda Email: asayechemeda@yahoo.com 37
Figure 2: Equivalent one-dimensional array representation
If two-dimensional arrays are stored in the memory as if
they are one-dimensional arrays, don’t you think the
basics of how they could be used interchangeably with
pointers is already explained? We can say so but it may
not be as straight forward as it was for one-dimensional
arrays. The details are therefore important.
Let us start discussing from what the name of a two-
dimensional array represents. In one-dimensional array,
the name of the array just represents a scalar pointer
containing the memory address of the first element. Is
that so for two-dimensional arrays too? Not really. The
name of a two-dimensional array is a pointer to the first
element of a one-dimensional array of pointers. The
elements in this array of pointers are the memory
addresses of the first elements in each row. When this is
represented in diagram, we will have the following:
Figure 3: Two-dimensional array direction
The name of two dimensional array is, therefore, a pointer
to a pointer. Even if the name of two-dimensional array
is a pointer to a pointer, we can’t assign it to a scalar
pointer to pointer.
Are you getting lost? Or is it getting interesting? May be,
if we use examples, it will be easier to understand and
more exciting. Let us consider the following array
declaration:
In the above statement, marks array is declared to have
three columns and is initialized with six elements. The
array will have two rows but the compiler is not interested
in the number of rows. Between the array name and the
array elements, there is an array of pointers. The array
name marks is a pointer to a pointer and doesn’t directly
point to any of the array elements. If marks is
dereferenced once, what is returned is a memory address
not the first array element. To understand what marks
points to, we can think of marks to be an array name for
one-dimensional array of pointers with the following two
elements.
We can also consider the above two elements to be the
array names for the first and second rows of the two-
dimensional marks array respectively. They are memory
addresses themselves but they can be considered as names
of the two rows of marks array. If they are one-
dimensional array names, they refer to the same memory
address as the elements they point to without having
memory addresses of their own.
If the array name marks can be considered to have the
above two elements, the identifier marks is supposed to
take the memory address of &marks[0][0]. As an
array name itself, &marks[0][0], refers to the
memory address of the first element of the two-
dimensional marks array. In other words, the array name
marks also refers to the memory address of element
marks[0][0]. Despite this, dereferencing marks
only once, will not return the first element of the array.
What a single dereferencing on marks returns is the
address of marks[0][0]. However, if marks is
dereferenced twice, the first element will be returned.
Which means that the array name marks is a pointer to
a pointer. The following logical statement will return
true.
Even if marks is a pointer to a pointer, it can’t be
assigned to a scalar pointer to pointer. The following
statement will cause compilation error.
13. By: Asaye Chemeda Email: asayechemeda@yahoo.com 38
The reason for the compilation error is that pp is a scalar
pointer to pointer while marks is a pointer to an array of
pointers. The above assignment is just like trying to assign
an array variable to a simple data type variable. If the
reason behind the impossibility of above assignment is
not clear, go through what we recently discussed about
two-dimensional array names once again. Incorporating
the above statements in a program with output statements
and analyzing the results might also help for a better
understanding.
If two-dimensional array names are pointers to an array
of pointers, then how are we going to use them to access
and manipulate array elements as pointers? Good
question at the right time. There are two options for this.
One of them is using the array name partially as an array
and partially as a pointer. The other option is by using the
array name completely as a pointer.
In the first option, the array name will be used to access
the intermediate array of pointers by using the array
subscripting operator. After this, the elements of the
intermediate array of pointers will be used to access and
manipulate the array elements through pointer arithmetic
and dereferencing.
In the second option, the data type of the array name will
be casted from pointer to pointer to simply a pointer.
Then, the casted array name will be used to access all the
array elements through pointer arithmetic and
dereferencing without using the array subscripting
operators.
Both of the above options will be discussed in detail one
by one. Although the second option is easier, starting
from the first option creates a smooth transition.
We will again consider the marks two-dimensional array.
Since the array has two rows, consider the array to be a
set of two one-dimensional arrays each representing a
single row. We have said that we can consider the array
name marks to be an identifier for one-dimensional
array of pointers. Each element in the one-dimensional
array of pointers is a pointer to the first element of each
row. Since the actual two-dimensional array has two rows,
the one-dimensional array of pointers will have two
elements. Now, we can write the marks array as follows:
Note that, each element in the above one-dimensional
array of pointers is an array name for each row of the
actual two-dimensional marks array. If marks is an
identifier for a one-dimensional array of two elements,
how can we write the two elements in terms of marks?
It is easy, right? We can write it using the array
subscripting operator as follows:
Since the two elements in the above marks array are
names for each row, we can also write the following
statements.
You see how the name marks along with the []
operator is used as a name for each row. The names
marks[0] and marks[1] will act as constant
pointers. As long as we do not apply arithmetic operation
which attempts to change their values, marks[0] and
marks[1] can now be used as normal pointers. By
dereferencing them, we can access and manipulate the all
the array elements. For instance, the following program
changes the value of marks[0][1] from 89.3 to
92.9 and prints all the elements of marks array.
Program 3.5[Correct].cpp
If you have understood that marks along with a single
[] operator with the row number inside is just an array
name for each row and if you recall how names of one
dimensional arrays (such as each row of marks array) can
be used as pointers, the above program is straight
forward. The output of the above program is shown
below.
Program 3.5 shows how name of two-dimensional array
can be used to access and manipulated all the elements of
the array by the combination of subscripting and pointer
dereferencing. Do you feel that you understood every
piece of it? Or do you feel that have some doubts?
Whichever your feeling is, you are ok. If you understood
it, proceed to what we are going to discuss next.
Otherwise, go through it once again, write your own
programs, analyze outputs from different angles and you
will definitely say “it isn’t that difficult”.
14. By: Asaye Chemeda Email: asayechemeda@yahoo.com 39
Now, let us proceed to the second and easier way to
access all elements in two-dimensional arrays by using the
array name as a pointer. In this approach, consider the
two-dimensional array to be a continuous one-
dimensional array as shown in Fig. 2. The equivalent one-
dimensional array is formed by appending each row at the
end of the previous row. Can you imagine what should
also be done during the conversion from two-dimensional
to one-dimensional array? Two basic changes should be
made.
The first change should be the data type of the array
name, not the individual array elements. The name of a
two-dimensional arrays is a pointer to array of pointers
while that of one-dimensional arrays is a pointer.
Therefore, if the conversation is to be made, the data type
of the array name should be casted accordingly. The
casting can be done as follows:
As long as we make the explicit casting as above
consistently, the arrayName can be used as just a name
of one dimensional array, i.e., the array name will behave
as a pointer not a pointer to array of pointers. Consider
the following two dimensional array declaration:
Without casting, the array name sum is a pointer to a
pointer. Remember, even if it is a pointer to a pointer,
sum cannot be assigned to a scalar pointer to pointer.
However, by explicitly casting it, we can make it behave
just like an array name of a one dimensional array. We can
imagine the above array to be as such:
The following statement returns a value of 9, which is the
value first element of sum array.
Note that, in the above statement we used only one
dereferencing operator. Even so, we accessed the value of
the first element. Had we tried this without casting sum
to (int *), we would have only got the address of the
element. The value of the second element, which is 4, can
be returned by the following statement.
Therefore, by casting the names of two-dimensional
arrays, we can use them as constant scalar pointers.
Although the casting is essential, it is not the only change
which should be applied to use two-dimensional array
names as scalar pointers. In addition to casting, another
change should also be made while converting two-
dimensional arrays into an equivalent one-dimensional
array. Can you guess what that change could be? It is the
index of each element.
When indexing through [] operator only is used to
access elements of two-dimensional arrays, the row and
column numbers should be explicitly stated. However,
when the two-dimensional array is converted to an
equivalent one-dimensional array, there will only be a
single index-the row number. As a result, the row and
column indexes which are supposed to access element in
the two-dimensional array should be adjusted so that they
can be used to access the same array element in the one-
dimensional array.
When two-dimensional arrays are condensed to form a
one-dimensional array, the equivalent single index to be
used for accessing the elements is determined from three
values: row and column numbers of the element in the
two-dimensional array and number of columns. That is
why it is always mandatory to explicitly specify the column
number during the definition of two dimensional arrays.
Conversion Rule: If a two-dimensional array has n columns, for
indexing puroses, it can be considered as a one-dimensional array in
which the element on the ith row and jth column in the two dimensional
array becomes kth element in the equivalent one-dimensional array,
where k=i*n+j.
If we apply casting and if we implement the above
conversion rule properly to access the right element, using
two dimensional array names as pointers becomes as easy
as it is for one-dimensional array names. Do you agree
with this or do you have a different view? Before deciding,
let us have a look at the following program.
Program 3.6[Correct].cpp
In the above program, a two-dimensional array sum
having three columns is declared and initialized on line 6.
The single index for the equivalent one-dimensional array
k is defined on line 7. Two for-loops were used to
navigate through all the array elements. The outer for-
loop with i counter is navigates across the row and the
inner for-loop with j counter navigates across the
column. If the array is to be converted to an equivalent
15. By: Asaye Chemeda Email: asayechemeda@yahoo.com 40
one-dimensional array, the index to be used for this array
(k) is calculated on line 10.
The cout statement on line 11, prints two values. The
first one prints the value of an array element through
indexing only. The second one prints the same value
through pointer only. When an element of the array is
printed using pointer arithmetic, first the array name is
casted. Then, the index in the equivalent one-dimensional
array is added through pointer arithmetic to point to each
array element. By dereferencing, the value of the array
element is returned and printed. By the way, we don’t
need two for-loops when accessing elements of two
dimensional arrays through pointers. One for-loop which
navigates through all the elements can also be used. Since
the number of elements in the array is 9, the following
single for-loop prints all the elements of sum array in the
same way the above program did.
What do you think the problem will be if we use the
following piece of code instead of the above one? In other
words, what could go wrong if we use pointer increment
instead of integer addition as in the following piece of
code?
If you recall, the legal pointer arithmetic operations on
array names, the answer is pretty simple. Array names are
constant pointers and their values cannot be changed.
Integer addition just adds an integer to the array name
without changing the memory address pointed by the
name. Integer addition through ++ operator, however,
attempts to change value of the memory address
contained in the array name, which is illegal. That is why
the above piece of code is wrong. Clear enough?
Aren’t you still convinced that using two-dimensional
array names as pointers is not that difficult? What about
after having the following formula?
If a is the name of a two-dimensional array having n
columns and a base data type of dataType, element
a[i][j] can equivalently be accessed and manipulated
using the array name as pointer using the following
formula:
So simple. Using two-dimensional array names as pointers
has been discussed to the detail we need for this chapter.
But, is that all what we are going to discuss about
pointers? Not really. Pointers are one of the powerful
blessings of C++ and mastering them should be a virtue
of someone who wants to be a decent programmer.
Few more interesting concepts are remaining and we will
proceed to next one. Trust me. Our persistence will pay
off.
Before starting the topic of “Arrays and pointers”, we said
that any curiosity that you might have about pointers to
pointers will get response in this topic. The time has now
come to answer what the applications of pointers to
pointers are in C++ programming. One of the
applications of pointers to pointers comes into picture,
when we use pointers as two-dimensional arrays.
Remember that we haven’t yet discussed how pointers
can be used as two-dimensional arrays. Before proceeding
to it, recalling the basics of two-dimensional array names
might be advantageous.
Two dimensional array names are pointers to an array of
pointers. Because of this, the array name cannot be
assigned to a scalar pointer to a pointer. The array of
pointers stands between the name and array elements. As
a result, the array elements cannot be directly accessed by
only a single dereferencing of the array name.
If we want to use pointers as two-dimensional arrays,
there may be several options. One of them is by creating
a pointer which is a replica of the property of the array
name. There is also another option in which pointers to
pointers can be used to create and used as dynamic arrays.
Both the options will be covered in this chapter.
In order to create a pointer with similar properties as a
name of two-dimensional array, the pointer should be a
pointer to an array of pointers. In addition, the elements
in the array of pointers should point to the first element
in each row of the two-dimensional array. If a pointer to
pointer is declared in such a manner, it can be used as a
two-dimensional array itself and the elements of the array
can be accessed by subscripting.
For a better understanding, let us consider the following
two dimensional array.
The above name array is a two-dimensional array with 11
columns and four rows. Each string literal is a row by
itself. The characters in the above array can be arranged
as follows.
16. By: Asaye Chemeda Email: asayechemeda@yahoo.com 41
If we want to use a pointer to pointer which can be used
as a two-dimensional array, first we need to create an array
of pointers. The array of pointers will have the same
number of elements as the number of rows in the name
array. Let us declare the array of pointers as follows:
Each element of the pointer array p should now be
initialized with the memory address of the first element in
each row of name array as follows:
The scalar pointer to pointer, which can be used a two-
dimensional array, can now be declared and initialized
with the address of the first element of pointer array p.
this is done as follows:
The pointer pp is a pointer to an array of pointers. Isn’t
pp a replica of the name array? Of course it is. Now, we
can use subscripting on pp to access and manipulate any
element of the name array. It just acts as a two
dimensional array. The following for-loop prints all the
characters as they are written during the declaration of
name array.
The scalar pointer to pointer pp was just used as an array.
You see what the application of pointers to pointers could
be. They can be used as arrays provided that they point to
an array of pointers having elements, which in turn point
to the first elements in each row of a two-dimensional
array.
The above procedure shows one of the ways in which
pointers can be used as two dimensional arrays. Before
proceeding to the last topic of this chapter, let us recall
what we postponed to do in this topic. Can you guess
what that might be? It is about passing two-dimensional
arrays to functions. We put off its discussion, remember?
To pass two dimensional arrays to functions, we can use
different approaches. We can cast the array and pass it as
a scalar pointer. We can define array of pointers with each
element pointing to the element in the first column of the
two-dimensional array. Then, we can pass the array
pointers to a function which receives pointer arrays or
scalar pointers to pointers.
The following program shows three different approaches
to pass two dimensional arrays. In the program, three
functions will be defined and all of them print the last
character of the name array given above.
Program 3.7[Correct].cpp
In the above program, the name array is passed to
print1 function by casting it to a scalar character
pointer on line 16. The formal parameter of print1
function is a scalar pointer and through pointer arithmetic
each element of the name array can be accessed and
manipulated.
An array of pointers p is declared on line 17 and initialized
on line 19. Then, this array of pointer is passed to
print2 function on line 20. The formal parameter of
print2 function is an array of pointers itself. The
elements of name array can now be accessed and
manipulated through a combination of subscripting and
pointer arithmetic.
The array of pointers p can also be passed to function
which receives scalar pointers to pointers like the
print3 function. Then, through subscripting, the
elements of the name array can be accessed and
manipulated.
The output of the above program is displayed below. The
output of all the three functions is the last character of the
name array, which is ‘k’.
Almost done. One important topic is remaining though.
Before proceeding to the topic, let us refer to program
3.7. In the program, the name array is declared with 11
columns and initialized with four string literals (names).
17. By: Asaye Chemeda Email: asayechemeda@yahoo.com 42
This means that any of the initializing string literals cannot
have more than 10 characters (the remaining one column
is for the null character). In other words, any of the
initializing names cannot have more than 10 characters.
Such arrays in which their size is defined before compile-
time are known as static arrays.
Now, assume you want to create a program which takes
names of the customers of the company that you are
working at. How many columns will you allocate for the
array which will store the names? 11 or 20 or how many?
This numbers of columns may be too small for some
names. If having small number of columns is a problem,
why not take a very large value for the column number so
that there will not be a problem when any name is taken
by the program? It seems a good solution. But, is it? If we
declare the array with a large value for the array which is
going to store names, don’t you think we are going to
waste memory space when names with small number of
characters are taken by the program?
Then, why not leave mentioning the number of columns
during declaration? That is not an option because the
compiler won’t allow us. If using small number for the
column may not store long names, if using large number
of columns wastes memory space and if leaving the
number of columns unmentioned is not an option, what
could be the best solution? The solution for this is
creating a dynamic array with dynamic array size. In
dynamic arrays, any of the array sizes are so flexible that
they can be determined after the program is compiled.
The following topic will explain it more.
Dynamic memory allocation
Throughout this chapter, we have been discussing about
pointers. Among the various concepts we discussed, we
have seen how pointers store memory addresses of
variables, how pointers are dereferenced and how
pointers can be used as arrays. In all these cases, the
pointers are useless unless there is a variable created
beforehand. The interesting question is: if the variables
can be manipulated without the use of pointers, why do
we need to define additional data?
Here is the answer. In C++ programming, memory space
is allocated for all non-pointer variables during compile-
time. However, there may be variables which only require
memory space depending on the environment during run-
time. Or variables which obtain their values during run-
time might be necessary within a program for efficiency
purposes. In such cases, pointers become powerful
options for accessing a memory space allocated during
run-time.
The pointer variables which we had been dealing with in
the previous topics obtain a memory space during
compile-time. Therefore, all non-pointer variables and the
type of pointer variables which we discussed so far obtain
the memory space allocated for them during compile
time. Such variables for which memory space is allocated
for them during compile time are known as static
variables. And those variables for which memory space
is allocated for them during run-time are known as
dynamic variables. The fact which makes pointers very
useful is that dynamic variables cannot be created without
pointers.
Then, how do we define dynamic variables? Good
question. In order to create dynamic variables in C++, a
special word is used. Dynamic variables are created in
C++ using the keyword new. If a variable is declared with
new, its memory is allocated during run-time and the
address of the allocated memory is returned. Therefore,
the variable declared by the keyword new should always
be a pointer. Note that dynamic memory space may not
always be granted.
Using new, two types of dynamic variables can be
created: a pointer with single address and an array of
pointers with multiple addresses. The syntax used to
create dynamic variables is as follows.
Dynamic single variables are declared as follows.
In the above declaration, a memory space sufficient for a
data type of dataType is allocated during run-time and
the address is returned to pointer.
For array of dynamic variables,
In the above declaration, a memory space sufficient for
Size number of elements each with a data type of
dataType is allocated during run-time and the base
address is returned to pointerArray.
After pointers or pointer arrays are created dynamically,
the elements which they point to can be accessed and
manipulated through dereferencing. Once, a variable is
defined using the keyword new, the memory space which
is allocated for the variable should be de-allocated before
using the same variable to obtain a new dynamic memory
space. Otherwise, a situation known as memory leak will
be created. This can be easily elaborated by the following
code fragment.
In the above code fragment, a double pointer p is
declared on line 1. A dynamic memory is allocated for the
dynamic variable which is going to be pointed to by p on
18. By: Asaye Chemeda Email: asayechemeda@yahoo.com 43
line 2. The value of the dynamic variable is initialized with
30.28 on line 3. On line 4, the pointer p again obtains a
dynamic memory space. On line 4, the value at the recent
memory location pointed by p is initialized with a value
of 38.73. Diagrammatically, this is depicted as follows.
Let us say the addresses for the memory allocated on line
2 and on line 4 are 100 and 200 respectively.
Let us focus on what happened on line 4. Even if p was
pointing to memory address 100, a new dynamic
memory, with address of 200, is allocated and the address
is returned to p. Even if the value in memory address 100
may not be used after line 4, the memory address 100,
can never be accessed. This situation is known as memory
leak. The memory size of address 100 may be few bytes.
However, thousands or millions of such leaks may result
in running out of memory space and ultimate termination
of the program.
To avoid memory leaks, dynamic memory spaces can be
de-allocated using the keyword delete. The way in
which delete is used depends on the type of the
dynamic memory to be de-allocated. To de-allocate
memory spaces allocated for single variables, the
following syntax is used.
For de-allocation of memory spaces allocated for arrays
of variables, the syntax is:
We can improve the above code fragment as follows.
The keyword delete only de-allocates the allocated
dynamic memory for future use. It has nothing to do with
the pointers or pointer arrays. After the dynamic memory
is de-allocated using delete, the pointers may still point
to the memory address they used to point. In such cases,
the pointers are said to be dangling.
To avoid the risk of dangling pointers, the pointers are
usually assigned with NULL after the memory is de-
allocated.
After our discussion on how memory is allocated
dynamically during program execution, let us see how
dynamic arrays are created in C++. It may be useful
recalling how pointers are used as arrays.
Let us upgrade program 3.7 so that a user will be
prompted to enter a name and let us store the entered
characters in a two-dimensional dynamic array called
pName. The values of the number of rows and columns
will be obtained at run-time. As a result, both these values
are variables. The number of rows will be denoted by
rows and the number of columns by columns. The
value of rows, which represents the number of names
that is going to be stored in the dynamic array, will be
entered by the customer. The value of columns, which
represents the number of characters in the names entered
by the customer, is determined from the length of the
string which stores the entered names.
To create two-dimensional dynamic arrays, we need to
define pointers to pointers. Let us denote the pointer to
pointer which will be used as a dynamic two-dimensional
array a name of pName. Note that both pName and
*pName are pointers.
To create a two-dimensional array, pName will be an
array of pointers. Each element in the array of pointers
will be an array name to an array of characters. The array
of characters will be the characters in the names entered
by the customer. First, pName will be defined to be an
array of pointers with dynamically allocated memory
space.
Each element of the pName array can be accessed and
initialized through subscripting. Note that the value of
rows in the above declaration will be determined during
program execution. Therefore, the number of names that
the dynamic array is going to store will be determined at
run time? Isn’t this an elegant way of optimizing your
memory space? Of course it is. More is yet to come. The
number of columns will also be made to be dynamic.
If i represents a row number, memory is allocated
dynamically for each column in the row under
consideration by using the following statement.
19. By: Asaye Chemeda Email: asayechemeda@yahoo.com 44
After this definition, pName will become a dynamic two-
dimensional array and its elements can be accessed and
manipulated through indexing.
During our discussion in chapter two, we said that
functions cannot return arrays. However, this will not be
a problem anymore. Can you guess why? Because we have
pointers. Even though functions cannot return arrays,
they can return pointers. Obviously, pointers can act as
arrays. The logic is simple now. If functions can return
pointers and if pointers can act as arrays, why don’t we
return arrays as pointers? You see another importance of
pointers. In general, if a function has to return an array, a
pointer can substitute the array and the pointer can be
returned as an array.
Let us incorporate what we discussed about dynamic
arrays and returning arrays as pointers in a program which
improves the drawbacks that could happen if we use static
arrays. Here is the program.
Program 3.8[Correct].cpp
In the above program, there are three functions including
main function. The values of the variables rows and
columns are obtained at run-time but the variables are
made global so that every function can access them.
In the main function, a pointer to pointer pName is
defined on line 8. The pointer will be used to create the
dynamic array. On line 9, the customer is prompted to
enter the number of names that he wants to enter. This is
just to show the dynamic nature of the array which we are
going to create by entering different numbers of names
for the dynamic array to hold. The number which will be
entered by the user will be the number of rows in the
array. Therefore, it will be stored in the rows variable.
The purpose of the statement in line 11 will be explained
in the next chapter. On line 12, a dynamic memory is
allocated for array of char pointers and the base address
is returned to pName. Each element in the array of
pointers will be used point to the base address of the
characters in a single name. Therefore, the number of
elements in this array of pointers will be equal to rows
and the assignment to pName was made as such.
On line 13, a function named print is called by passing
pName as an argument. The formal parameter of the
print function is also made to conform to the data type
of the passed argument. Within print function, a
pointer to pointer is defined and it is initialized by the
pointer returned by read function.
The read function takes a pointer to a pointer of char
data type as a parameter. It is the read function which
reads the names entered by the customer and stores them
in the dynamic array accordingly. Note that, since the
read function returns a pointer to pointer, the return
type of the function is stated as such. In the read
function, a string variable sName is declared on line 18.
This variable takes the name of one customer. There are
two-four loops within the function. The outer for-loop
loops across the rows while the inner for-loop loops
across all the characters in a given name. Within the outer
for-loop, the customer is prompted to enter his name on
line 20. The statement on line 21, which will be explained
in detail in the next chapter, reads all the characters in a
string entered by the customer until the ‘Enter’ key is
pressed. The entered string will be stored in the sName
variable.
Here comes another dynamic nature of the array which
we are going to create. The number of columns in a given
row will be determined after determining the number of
characters in the entered name. Isn’t this what we wanted
to improve in program 3.7? The number of characters in
20. By: Asaye Chemeda Email: asayechemeda@yahoo.com 45
the entered name is determined by determining the length
of sName as it is done on line 22. The determined
number of characters plus one space for the null character
is then assigned to the columns variable. This means
that the value of the columns variable is determined at
run-time. On line 23, a dynamic memory is allocated for
an array having number of elements equal to columns.
This dynamic array will be the dynamic column for each
row of the two-dimensional dynamic array. You see,
entering long names is not a problem anymore and no
memory space is going to be wasted. The exact memory
requirement will be allocated for any name entered.
On the same line of the program, i.e. line 23, the array of
pointers in name variable, which also refers to the same
memory address as pName, is assigned with the base
address of the dynamic memory allocated for each
column with the help of the array subscripting operator
[]. For instance, pName[i] contains the base memory
address of the array of characters which stores the ith
name entered by the customer.
After this, each column in the given row is assigned with
the corresponding character in the sName string on line
25. The sName string changes every time the customer
enters a name and presses the ‘Enter’ key. On line 26, the
last character in each row is assigned with null character.
Remember that on line 22, one is added to the length of
sName in the assignment to columns to accommodate
the null character. After all names are entered and stored
in the dynamic array, the pointer to pointer name is
returned by the read function. This returned pointer to
pointer is then used to assign another pointer to pointer
defined in the print function. Through this pointer to
pointer, all the names stored in the dynamic array can be
accessed by indexing. A four loop and a while-loop inside
the print function will print every character in the
entered names until a null character is encountered. How
dynamic was that?
There could also be other easier ways by which a program
with the same goal as program 3.8 can be written.
However, the program was also intended to show the
implementation of our recent discussion on dynamic
arrays and returning arrays as pointers. Indeed, the
program created a dynamic array which improves the
efficiency of memory allocation.
That is all for this chapter. It was relatively longer than the
other chapters. But, wasn’t it worth? Since pointers are
areas in C++ which can lead even a professional
programmer to write faulty programs which are difficult
to bug, the time and the effort we spent in this chapter
will be rewarded. Besides, pointers provide us the means
by which we can manipulate data in a program which
other approaches may not be capable of. So, the devotion
we put in this chapter is justified.
Even if this and the previous chapters covered lots of
basic components in C++ programming, wring an
efficient program may ask a lot more. As we have
progressed from chapter to chapter, we have gathered
more and more knowledge that we need to have as a
programmer. So, do you want to know more? I know your
answer is also yes because ‘C++ is fun’. Isn’t it? Let us go
to the next chapter then.