Posted inInformation Technology

C++ Arrays and Pointers Example

an array of blank orange notepads stuck on purple surface

C++ arrays and pointers really originate with the C programming language. There have been massive improvements since the 1980s. Back then declaring arrays of structures was a bit iffy. Much of that had to do with the Stack vs. Near Heap vs. Far Heap issues of memory segmentation and “bytes allocated for alignment.” Refer to the std::vector post for further information on that.

The Code

We have a lot to talk about with this short example. We are going to dynamically allocate an array, fill it with prime numbers, then print it out. Our main() will do almost nothing.

/*  ArraysAndPointersExample
 *
 *  Never use auto in production code!!!
 */

// globals before locals
//
#include <iostream>
#include <iomanip>
#include <cmath>
#include <cstdlib>

#include "config.h"

unsigned long * allocateAndFill( size_t max);
void reportPrimes( size_t max, unsigned long *primes);

int main(int argc, char **argv) {
	std::cout << "ArraysAndPointersExample" << std::endl;
	std::cout << "Version " << ArraysAndPointersExample_VERSION_MAJOR << "." << ArraysAndPointersExample_VERSION_MINOR << std::endl;

	size_t max {};

	std::cout << "How many primes would you like to find? ";
	std::cin >> max;

	if (max > 0)
	{
		unsigned long *primes = allocateAndFill( max);

		reportPrimes( max, primes);

		delete[] primes;		// don't forget to clean up
								// thanks to standard changes this works even
								// if primes is a nullptr.

		primes = nullptr;		// just to be polite
	}

	/*
	 * Tiny x86 minds have a bad habit of hard coding 0. I've done it myself.
	 * This is an extremely bad practice which shreds portability.  OpenVMS
	 * interprets the low-order bits of $STATUS (found in $SEVERITY) like so:
	 *
	 * 0 = Warning ("%facility-W-mnemonic, message text")
	 * 1 = Success (No message by default)
	 * 2 = Error ("%facility-E-mnemonic, message text")
	 * 3 = Informational ("%facility-I-mnemonic, message text")
	 * 4 = Severe (Fatal) Error ("%facility-F-mnemonic, message text")
	 *
	 * Read this book
	 * https://www.theminimumyouneedtoknow.com/app_book.html
	 * for more information on that.
	 *
	 * Many other real computer platforms encode the bits in other ways so a
	 * return value may have a wide range of "success" values that can codify
	 * the level of success.
	 *
	 * On OpenVMS EXIT_SUCCESS typically gets mapped to SS$_NORMAL.
	 *
	 * Note the (). Many people don't use them (myself included) but
	 * return is a function.
	 */
	return (EXIT_SUCCESS);
}

unsigned long * allocateAndFill( size_t max)
{
	/*
	 * Unlike a declared array this will not go away when we
	 * exit the function. Anything you "new" you must delete yourself.
	 * Note that we allocate this array in a called function and delete
	 * it in the main.
	 */
	unsigned long *primes {new unsigned long[max]};

	size_t count {1};		// count of primes found
	primes[0] = 2;			// first prime

	unsigned long trial {primes[0]+1};

	while (count < max )
	{
		bool isPrime {true};

		/*
		 * bit of a cheat here. Stops us from running through entire list.
		 * mathematically, if the value we are dividing by with modulus
		 * gets up to the integer of the square root we can stop. Nothing larger
		 * could be an even division.
		 */
		const unsigned long limit = static_cast<unsigned long>(std::sqrt(trial));
		for (size_t i {}; primes[i] <= limit && isPrime; ++i)
		{
			isPrime = trial % primes[i] > 0;	// False for exact division
		}

		if (isPrime)
		{
			primes[count++] = trial;
		}

		trial += 2;		// this is a cheat. It works because we pre-load 2
						// and our first trial is 3. If you set the trial to
						// the first prime value in the list (2) this code
						// would never find another.
	}

	return (primes);
}

//	output 10 to a line
void reportPrimes( size_t max, unsigned long *primes)
{
	const size_t PER_LINE = 10;
	const size_t PRINT_WIDTH = 10;

	for (size_t i {}; i < max; ++i)
	{
		std::cout << std::setw(PRINT_WIDTH) << primes[i];
		if ((i+1) % PER_LINE == 0)
		{
			std::cout << std::endl;
		}
	}
	std::cout << std::endl;
}

The Array

Lines 27 through 38 are the first thing we need to talk about. When you carve your main() down to the basics, you can avoid multiple exits wrapping the meat of the application within an if statement. Most examples stuff everything into main() but that is not a good way of doing things. Unless an application framework forces you to do otherwise main() should have no more than twenty lines of actual code. A hundred lines of comments are fine, but actual code should be under twenty.

If it isn’t obvious, via the comment when reading lines 29 and 33, we allocate our array via a called function. Dropping down to line 75 you will see where we new it inside of an initializer so it is zeroed. Any time you new something, it lives until you nuke it via an appropriate delete. A stack declared

unsigned long primes [max];

is not initalized and ceases to exist when it goes out of scope. Since we are talking about arrays and pointers with this example, stack declared arrays are also a pointer.

Old School Array Use

/*  p-test-1
 *
 *
 */

// globals before locals
//
#include <iostream>
#include <iomanip>
#include <cmath>
#include <cstdlib>

int main( int argc, char **argv )
{
    unsigned long tbl [] {24, 56, 1, 99, 256};
    unsigned long *ptr = tbl;

    std::cout << "tbl [3]: " << tbl[3] << std::endl;

    ptr +=4;
    std::cout << "value at ptr + 4: " << *ptr << std::endl;

    return ( EXIT_SUCCESS );
}
roland@roland-HP-Z2-SFF-G4-Workstation:~/Projects/p-test-1$ g++ main.cpp -o p-test-1
roland@roland-HP-Z2-SFF-G4-Workstation:~/Projects/p-test-1$ ls
main.cpp p-test-1
roland@roland-HP-Z2-SFF-G4-Workstation:~/Projects/p-test-1$ ./p-test-1
tbl [3]: 99
value at ptr + 4: 256

Things most definitely got out of hand and developers took this to extremes. Read the 3-Dimensional array example code in this post. There is absolutely no reason to write code like that in production today! The fact someone thinks it is a legit coding test is exactly why senior developers walk out of interviews. We won’t do shit like that! Only a skill-less hack would.

In the 1980s before we had Vector classes and in-memory SQLite database tables, we had to do things like that. Deep in the bowels of every relational database engine there is probably code like that. It should not exist in production code you write! Someone fresh out of school will have to maintain that.

return is a function

Please read the rant in the comments found in lines 40 – 63. I’m as guilty as sin when it comes to leaving the () off a single return value. I have had clients pointing it out since the late 1980s. One of the few things I like about Eclipse is that it flags the mission () as a bug.

Having spent the past decade on x86 wanna-be-a-real-computer-one-day-when-I-grow-up processors, I’ve also got many bad examples out there of

return (0);

When you do that, the code is non-portable. Only on x86 and some ARM processors that cloned x86 bad habits is zero considered a success. In the land of real computers every OS has some form of $SEVERITY where a set of bits in the return value are a category mask. In the land of OpenVMS odd values are success, even failure and zero a warning. The rest of that integer return contains a “message number” you can use to lookup what, if any, issue occurred.

The Output

ArraysAndPointersExample
Version 0.1
How many primes would you like to find? 200
2 3 5 7 11 13 17 19 23 29
31 37 41 43 47 53 59 61 67 71
73 79 83 89 97 101 103 107 109 113
127 131 137 139 149 151 157 163 167 173
179 181 191 193 197 199 211 223 227 229
233 239 241 251 257 263 269 271 277 281
283 293 307 311 313 317 331 337 347 349
353 359 367 373 379 383 389 397 401 409
419 421 431 433 439 443 449 457 461 463
467 479 487 491 499 503 509 521 523 541
547 557 563 569 571 577 587 593 599 601
607 613 617 619 631 641 643 647 653 659
661 673 677 683 691 701 709 719 727 733
739 743 751 757 761 769 773 787 797 809
811 821 823 827 829 839 853 857 859 863
877 881 883 887 907 911 919 929 937 941
947 953 967 971 977 983 991 997 1009 1013
1019 1021 1031 1033 1039 1049 1051 1061 1063 1069
1087 1091 1093 1097 1103 1109 1117 1123 1129 1151
1153 1163 1171 1181 1187 1193 1201 1213 1217 1223

Roland Hughes started his IT career in the early 1980s. He quickly became a consultant and president of Logikal Solutions, a software consulting firm specializing in OpenVMS application and C++/Qt touchscreen/embedded Linux development. Early in his career he became involved in what is now called cross platform development. Given the dearth of useful books on the subject he ventured into the world of professional author in 1995 writing the first of the "Zinc It!" book series for John Gordon Burke Publisher, Inc.

A decade later he released a massive (nearly 800 pages) tome "The Minimum You Need to Know to Be an OpenVMS Application Developer" which tried to encapsulate the essential skills gained over what was nearly a 20 year career at that point. From there "The Minimum You Need to Know" book series was born.

Three years later he wrote his first novel "Infinite Exposure" which got much notice from people involved in the banking and financial security worlds. Some of the attacks predicted in that book have since come to pass. While it was not originally intended to be a trilogy, it became the first book of "The Earth That Was" trilogy:
Infinite Exposure
Lesedi - The Greatest Lie Ever Told
John Smith - Last Known Survivor of the Microsoft Wars

When he is not consulting Roland Hughes posts about technology and sometimes politics on his blog. He also has regularly scheduled Sunday posts appearing on the Interesting Authors blog.