-
Arrays
An array is a series of elements of the same type placed in contiguous memory locations that can be individually referenced by adding an index to a unique identifier.
That means that, for example, five values of type
int
can be declared as an array without having to delacre 5 different variables (each with its own identifier). Instead, using an arraym the five int values are stored in contiguous memory locations, and all five can be accessed using the same identifier with the proper index.For example, an array containing 5 integer values of type
int
calledfoo
could be represented as :When each blank panel represents an element of the array. In this case, these are values of type int.
Like a regular variable, an array must be declared before it is used. A typical declaration for an array in C++ is :
type name [elements];
where type is a valid type (such as
int
,float
, …),name
is a valid identifier and theelements
filed (which is always enclosed in square brackets []), specifies the length of the arry in terms of the number of elements.Therefore, the foo array, with five elements of type int, can be declared as :
int foo [5];
-
Arrays as parameters
At some point, we need to pass an array to a function as a parameter.
In C++, it is not possible to pass the entire block of memory represented by an array to a function directly as an argument. But what can be passed instead is its address.
In practice, this has almost the same effect, and it is a much faster and more efficient operation.
To accept an array as parameter for a function, the parameters can be declared as the array type, but with empty brackets, omitting the actual size of the array.
void procedure (int arg[]) void procedure (int myarray[][3][4])
In a way, passing an array as argument always loses a dimension.
The reason behind is that, for historical reasons, arrays cannot be directly copied, and thus what is really passed is a pointer. This is a common souce of errors for novice programmers.
-
Library arrays
The arrays explained above are directly implemented as a language feature, inherited from the C language. They are a great feature, but by restricting its copy and easily decay into pointers, they probably suffer from an excess of optimiztion.
To overcome some of these issues with language ***built-in arrays***, C++ provides an alternative array type as a standard ***container***. It is a type template (a class template, in fact) defined in header .
Suffice it to say that they operate in a similar way to built-in arrays, except that they allow being copied (an actually expensive that copies the entire block of memory, and thus to use with care) and decay into pointers only when explicitly told to do so (by means of its member
data
). -
Initializing arrays
By default, regular arrays of local scope (for example, those declared within a function) are left uninitialized. This means that none of its elements are set to any particular value; their contents are undermined at the point the array is declared.
But the elements in an array can be explicitly initialized to specific values when it is declared, by enclosing those initial values in braces {}. For example:
int foo[5] = {16, 2, 77, 40, 12071};
This statement declares an array that can be represented like this:
The number of values between braces {} shall not be greater than the number of elements in the array. If declared with less, the remaining elements are set to their default values (which for fundamental types, means they are filled with zeroes).
When an initialization of values is provided for an array, C++ allows the possibility of leaving the square brackets empty []. In this case, the compiler will assume automatically a size foe the array that matchs the number of values included between the braces {}.
int foo[] = {16, 2, 3};
Finally, the evolution of C++ has led to the adoption of universal initialization also for arrays. Therefore, there is no longer need for the equal sign between the declaration and the initializer.
int foo[] = {10, 20, 30}; int foo[] {10, 20, 30}
Static arrays, and those ***declared directly in a namespace (outsize any function)***, are always initialized. If no explicit initializer is specified, all the elements are default-initialized ( with zeroes, for fundamental types).
-
Accessing the values of an array
The values of any of the elements in an array can be accessed just like the value of a regular variable of the same type. The syntax is :
name [index]
foo[2] = 75; x = foo[2];
Accessing out-of-range elements do not cause errors on compilation, but can cause errors on runtime.
Bracket [] has two uses performing two different tasks:
- One is to specify the size of arrays when they are declared
- Second one is to specify indeices for concrete array elements when they are accessed.
int foo[5]; foo[2] = 75;
-
Multidimensional arrays
Multidimensional arrays can be described as “arrays of arrays”. For example, a bidimensional array can be imagined as a two-dimensional table made of elements, all of them of a same uniform data type.
int jimmy[3][5]; // a bidimensinal array of 3 per 5 elements of type int
the way to reference the second element vertically and fourth horizontally in an expression would be:
jimmy[1][3]
Multidimensinal arrays are just an abstraction for programmers, since the same results can be achieved with a simple array, by multiplying its indices:
int jimmy [3][5]; // is equivalent to int jimmy [15]; // (3 * 5 = 15)
-
-
Character sequence
strings are, in fact, sequences of characters, we can represent them also as plain arrays of elements of a character type.
By convention, the end of strings represented in character sequences is signaled by a special character: the
null character
, whose literal value can be written as'\0'
(backslash, zero).-
Initialization of null-terminated character sequences
Sequences of characters enclosed in double-quotes (") are literal constants. And their type is , in fact, a null-terminated array of characters.
Once string literal has been declared(initialized), they cannot be assigned values,
myword = "bye"; // equal to myword[] = {'b', 'y', 'e'}; // would not be valid myword = {'B','y','e', '\0'}; // each of its elements can be assigned a value individually myword[0] = 'B';
-
Strings and null-terminated character sequences
Plain arrays with null-terminated sequences of characters are the typical types used in the C language to represent strings, which is known as
C-stings
, difference from from the standard library defined specific type for strings (classstring
).The string literals still always produce null-terminated character sequences, not
string
objects.cin
andcout
support null-terminated sequences directly.strings
have a dynamic size determind duringruntime
, while the size ofarrays
is determined on thecompilation
, before the program runs.
-
-
Pointers
Variables
can be explained as locations in the computer’smemory
which can be accessed by theiridentifier
(theirname
).When a variable is declared, the memory needed to store its value is assigned a specific location in memory (its memory address). An OS decides the particular memory locations on runtime.
-
Address-of operator (&)
The address of a variable can be obtained by preceding the name of a variable with an
ampersand sign (&)
, known as address-of operator.foo = &myvar;
This would assign the address of variables
myvar
to foo.The actual address of a variable in memory cannot be known before runtime.
The variable that stores the address of another variable (like
foo
in the previous example) is what in C++ is called apointer
.Pointers are a very powerful feature of the language that has many uses in lower level programming.
-
Dereference operator (*)
Pointers
are said to "point to " the variable whose address they store.An interesting property of pointers is that they can be used to
access the variable
they point to directly. This is done by preceding the pointer name with thedereference operator (*)
. The operater itself can be read as “value pointed to by
”.baz = foo; // baz equal to foo (1776) baz = *foo; // baz equal to value pointed to by foo (25)
The reference and dereference operators are thus complementary:
- & is the address of operator, and can be read simply as “
address of
” *
is the dereference operator, and can be read as “value pointed to by
”
Thus, they have sort of opposite meanings : An address obtained with
&
can be dereferenced with*
. - & is the address of operator, and can be read simply as “
-
Declaring pointers
Due to the ability of a pointer to directly refer to the value that it points to, a pointer has different properties when it points to a
char
than when it points to anint
or afloat
. Once dereferenced , the type needs to be known. And for that, the declaration of a pointer needs to include the data type the pointer is going to point to.The declaration of pointers follows this syntax:
type * name;
where
type
is the data type pointed to by the pointer. This type is not the type of the pointer itself, but the data the pointer points to.int *number;
The asterisk(
*
) used when declaring a pointer only means that it is a pointer (it ispart of its type compound specifier
), and should not be confused with thedereference operator
seen a bit earlier.int * p1, *p2; // two pointers int * pi, p2; // p1 is pointer, p2 is int type, spaces do not matters
-
Pointers and arrays
int myarray[20]; int * mypointer; mypointer = myarray; myarray = mypointer; // not valid
mypointer
andmyarray
would be equivalent and would have very similar properties. The main difference being thatmypointer
can be asssigned a different address, whereasmyarray
can never be assigned anything, and will always represent the same block of 20 elements of typeint
.a[5] = 0; *(a+5)=0;
Brackets([]) were explained as specifying the index of an index of an element of the array.
In fact these brackets are adereferenceing operator known as offset operator. They dereference the variable they follow just as
*
does, but they also add the number between brackets to the address being dereferenced. -
Pointer initialization
int myvar; int * myptr = &myvar; // pointers can be initialized either to the address of a variable int * bar = myptr; // or the value of another pointer(or array)
Pointers can be initialized to point to specific locations at the veru moment they are defined.
-
Pointer arithmetics
Only
addition
andsubtraction
operations are allowed, the other make no sense in the world ofpointers
. But both addition and subtraction have a slightly different behavior with pointers, according to thesize of the data type
to which they point.When fundamental data types were introduce, we saw that types have different sizes.
When adding one to a pointer is made to point to the following element of the same type, and, therefore, the size in bytes of the type it points to is added to the pointers.
char * mychar; // 1byte short * myshort; // 2bytes ++mychar ; // add 1 ++myshort; // add 2 mychar = mychar + 1; // add 1 myshort = myshort + 1; // add 2
This is applicable both when adding and subtracting any number to a pointer.
Regarding the increment (
++
) and decrement (--
) operators, they both can be used as either prefix or suffix of an expression, with a slight difference in behavior: as a prefix, the increment happens before the expression is evaluated, and as a suffix, the increment happens after the expression is evaluated.The postfix operators such as increment and decrement have higher precedence than prefix operators, such as the dereference operator (*) .
*p++; // equal to *(p++);
What it does is to increase the value of p (so it now points to the next element), but because ++ is used as postfix, the whole expression is evaluated as the value pointed orginally by the the pointer (the address it pointed to before being incremented).
-
Pointers and const
Pointers can be used to access a variable by its address, and this access may include modifying the value pointed. But it is also possible to declare pointer that can access the pointed value to read it, but not to modify it. But not to modify it. For this, it is enough with qualifying the type pointed to by the pointer as
const
.int x; int y = 10; const int* p = &y; x = *p; *p = x; // error: modifying p, which is const-qualified
A pointer to non-const can be implicityly converted to a pointer to const, But not the other way around!
A function that takes a
pointer to non-const
as parameter can modify the value passed as argument, while a function that takes a pointer to const as parameter cannot.int * p1 = &x; // non-const pointer to non-const int const int * p2 = &x; // non-const pointer to const int int const * p2_2 = &x; // same as above int * const p3 = &x; // const pointer to non-const int const int * const p4 = &x; // const pointer to const int
The syntax with const and pointers is definitely tricky, and recognizing the cases that best suit each use tends to requeire some experience.
-
Pointers and sting literals
String literal are arrays containing null-terminate character sequences .
const char * foo = "hello";
This declares an array with the literal representation for “
hello
”, and then a pointer to its first element is assigned tofoo
. -
Pointers to pointers
C++ allows the use of pointers that point to pointers, that these, in its turn, point to data (or even to other pointers). The syntax simply requires an asterisk (*) for each level of indirection in the declaration of the pointer:
char a; char * b; char ** c; a = 'z'; b = &a; c = &b;
each one of them would correspond to a different value:
c
is of typechar**
and a value of 8092;*c
is of typechar*
and a value of 7230;**c
is of typechar
and a value of ‘z’;
-
void pointers
The
void
type of pointer is a special type of pointer.In C++,
void
represents the absence of type.Therefore, void pointers are pointers that point to a value that has no type (and thus also an undetermind length and undetermined dereferencing properties).
sizeof
is an operator integrated in the C++ language that returns the size in bytes of its argument. For non-dynamic data types, this value is a constant. Therefore, for eaample,sizeof(char)
is 1, becausechar
has always a size of one byte. -
Invalid pointers and null pointers
In principle, pointers are meant to point to valid addresses, such as address of a variable or the address of an element in an array. But pointers can actually point to any address, including addresses that do not refer to any valid element. Typical examples of this are
uninitialized pointers
and pointers to nonexistent elements of an array:int *p; // uninitialized pointer (local variable) int myarray[10]; int *q = myarray+20; // element out of bounds
Neither
p
norq
point to addresses known to contain a value, but none of the above statements causes an error. In C++, pointers are allowed to take any address value, no matter whether there actually is something at that address or not.What can cause an error is to dereference such a pointer (i.e., actually accessing the value they point to). Accessing such a pointer causes undefined behavior, ranging from an error during runtime to accessing some random value.
But, sometimes, a pointer really needs to explicitly point to nowhere, and not just an invalid address. For such cases, there exists a special value that any pointer type can take: the null pointer value. This value can be expressed in C++ in two ways: either with an integer value of
zero
, or with thenullptr
keyword:int * p = 0; int * q = nullptr;
Both p and q are null pointers, meaning that they explicitly point to nowhere, and they both actually compare equal: all null pointers compare euqal to other null pointers. It is also quite usual to see the defined constant NULL be used in older code to refer to the null pointer value:
int * r = NULL;
NULL
is defined in several headers of the standard library. and is defined as an alias of some null pointer constant value (such as0
ornullptr
).Do not confuse null pointers with void pointers!
A
null pointer
is a value that any pointer can take to represent that it is pointing to “nowhere”, while avoid
pointer is a type of pointer that can point to somewhere without a specific type. -
Pointers to functions
C++ allows operations with pointers to functions. The typical use of this is for passing a function as an argument to another function.
Pointers to functions are declared with the same syntax as a regular function declaration, except that the name of the function is enclosed between parentheses() and an asterisk(*) is inserted before the name:
int operation (int x, int y, int (*functicall)(int, int)) { int g; g = (*functocall)(x, y); return (g); } int main() { int m, n; int (*minus)(int, int) = subtraction; m = operation(7, 5, addition); n = operation(20, m, minus); }
minus
is a pointer to a function that has two parameters of typeint
. It is initialized to point to the functionsubtraction
:int (* minus)(int, int) = subtraction;
-
-
Dynamic memory
All memory needs were determind before program execution by defining the variables needed. But there may be cases where the memory needs of a program can only determined during runtime. For example , thern the memory needed depends on user input. On these cases, programs need to
dynamically allocate memory
, for which the C++ language integrates the operatorsnew
anddelete
.-
Operators
new
andnew[]
Dynamic memory is allocated using operator
new
.new
is followed by a data type specifier and , if a sequence of more than one element is required, the number of these within []. It returns a pointer to the beginning of the new block of memory allocated. Its syntax is :pointer = new type pointer = new type [number of elements]
int * foo; // 定义一个指针 foo = new int [5];
foo
is a pointer, and thus , the first element pointed to byfoo
can be accessed either with the expressionfoo[0]
or the expression*foo
(both are equivalent). The second element can be accessed either withfoo[1]
or*(foo+1)
.There is a substantial difference between declaring a
normal array
and allocatingdynamic memory
for a block of memory using new. The most important differece is that the size of a regualr array needs to be a constant expression, and thus its size has to be determined at the moment of designing the program, before it run, whereas the dynamic memory allocation performed by new allows to assign memory dyring runtime using any variable value as size.C++ provides two standard mechanisms to check if the allocation wa successful:
- one is by handling exceptions which named
bad_alloc
is thrown and the program execution is terminated. This exception method is the method used by default bynew
- The other method is known as
nothrow
, the pointer returned bynew
is anull pointer
, and the program continues its execution normally
- one is by handling exceptions which named
-
Operators
delete
anddelete[]
In most cases, memory allocated dynamically is only needed during specific periods of time within a program; once it is no longer needed, it can be freed so that the momeory becomes available again for other requests of dynamic memory. This is the purpose pf operator
delete
, whose syntax is:delete pointer; delete[] pointer;
The value passed as argument to
delete
shall be either a pointer to a memory block previously allocated withnew
, or anull pointer
.It is considerd good practice for programs to always be able to handle failures to allocate memory, either by checking the pointer value (if
nothrow
) or by catching the proper exception. -
Dynamic memory in C
new
anddelete
for allocating dynamic memory in C++ were not available in the C language.
-
-
Data Structures
-
Data structures
A data structure is a group of data elements grouped together under one name. These data elements, known as members, can have
different types
anddifferent length
. Data structures can be declared in C++ using the following syntax:struct type_name{ member_type1 member_name1; member_type2 member_name2; member_type3 member_name3; . . } object_names;
is a name for the structure type;
can be a set of valid identifiers for objects that have the type of this structure.
struct product { int weight; // member_type member_name double price; }; product apple; produce banana, melon; // equal to struct product { int weight; double price; } apple, banana, melon;
Once the three
objects
of a determinedstructure
type are declared , itsmembers
can be accessed dirctly. The syntax for that is simply to insert a dot (.
) between the object name and the member name.One of the features of data structures is the ability to refer to both their members individually or to the entire structure as a whole.
-
Pointers to structures
Like any other type, structures can be pointed to by its own type of pointers:
struct movies_t{ string title; int year; }; movies_t amovie; movies_t * pmovie; pmovie = &amovie;
The
arrow operator (->)
is a dereference operator that is used exclusively with pointers to objects that have members.This operator serves to access the member of an object directly from its address. For example :
pmovie -> title; // 从指针到其所指对象的member,整体作为一个变量,返回这个title的值
is, for all purposes, equivalent to :
*pmovie.title // equal to *(pmovie.title)
-
Nesting Structures
Structures can also be nested in such a way that an element of a structure is itself another structure.
-
-
Other data types
-
Type aliases (typedef / using)
A type
alias
is a different name by which a type can be identified. In C++, any valid type can be aliased so that it can be referred to with a different identifier.In C++, there are two syntaxes for creating such type aliases:
-
Inherited form the C language, uses the typedef keyword
typedef existing_type new_type_name; // existing_type is any type, either fundamental or compound typedef char C; typedef char * pChar;
-
using new_type_name = existing_type;
using C = char; using pChar = char *;
using
is more generic, becausetypedef
has certain limitaions in the realm of templates. -
-
Unions
Unions allow one portion of memory to be accessed as different data types. Its declaration and use is similar to the one of structures, but its functionality is totally different:
union type_name{ member_type1 member_name1; member_type2 member_name2; ... } object_names;
This creates a new
union
type, identifid bytype_name
, in which member elements occupy the same physical space in memory. The size of this type is the one of the largest member element.Each of these memebrs is of a different data type, But since all of them are referring to the same location in memory, the modification of one of the members will affect the value of all of them. It is not possible to store different values in them in a way that each is independent of the others.
-
Anonymous unions
When unions are members of a class (or structure), they can be declared with no name. Which means they become anonymous unions.
-
Enumerated types (enum)
Enumerated types are types that are defined with a set of custom identifiers, known as enumerators, as possible values. Objects of these enumerated types can take any of these enumerators as value.
enum type_name { value1, value2, ... } object_names;
This declaration includes no other type, neither fundamental nor compound, in its definition, which means this creates a whole new data type from scratch withou basing it on any other existing type.
-
Enumerated types with enum class
To create real
enum
type which are neither implicitly convertible toint
and that neither have enumerator values of typeint
, but theenum
type itself, usdingenum class
(orenum struct
) instead ofenum
:enum class Colors { black, blue, .. };
C++学习系列四:Arrays||Character sequences||Poiters||Dynamic Memory||Data Structures||Other data types
猜你喜欢
转载自blog.csdn.net/The_Time_Runner/article/details/107304683
今日推荐
周排行