C++编程中指针的声明与基本使用讲解_C 语言

使用以下序列声明指针。

[storage-class-specifiers] [cv-qualifiers] type-specifiers
[ms-modifier] declarator ;

其中,任何有效指针声明符均可用于 declarator。简单指针声明符的语法如下所示:

* [cv-qualifiers] identifier [= expression]

1.声明说明符:
可选存储类说明符。
应用于要指向的对象的类型的可选 const 或 volatile 关键字。
类型说明符:可表示要指向的对象的类型的类型名称。
2.声明符:
可选的 Microsoft 专用修饰符。

* 运算符。
应用于指针本身的可选 const 或 volatile 关键字。
标识符。
可选初始值设定项。
指向函数的指针的声明符类似于以下形式:

(* [cv-qualifiers] identifier )( argument-list ) [cv-qualifers]
[exception specification] [= expression];

对于指针数组,语法如下所示:

* identifier [ [ constant-expression ] ]

但是,指针声明符可能更复杂。
多个声明符及其初始值设定项可能同时出现在前面有声明说明符且以逗号分隔的列表中的一个声明中。
指针声明的简单示例如下:

char *pch;

前面的声明指定 pch 指向 char 类型的对象。
更复杂的示例是

static unsigned int * const ptr;

前面的声明指定 ptr 是一个指向 unsigned int 类型(带静态存储持续时间)的对象的常量指针。
下一个示例演示如何声明和初始化多个指针:

static int *p = &i, *q = &j;

在前面的示例中,指针 p 和 q 都指向类型 int 的对象并分别初始化为 i 和 j 的地址。存储类说明符 static 应用于这两个指针。

// pointer.cpp
// compile with: /EHsc
#include <iostream>
int main() {
 int i = 1, j = 2; // local variables on the stack
 int *p;

 // a pointer may be assigned to "point to" the value of
 // another variable using the & (address of) operator
 p = & j; 

 // since j was on the stack, this address will be somewhere
 // on the stack. Pointers are printed in hex format using
 // %p and conventionally marked with 0x.
 printf_s("0x%p\n", p);

 // The * (indirection operator) can be read as "the value
 // pointed to by".
 // Since p is pointing to j, this should print "2"
 printf_s("0x%p %d\n", p, *p);

 // changing j will change the result of the indirection
 // operator on p.
 j = 7;
 printf_s("0x%p %d\n", p, *p );

 // The value of j can also be changed through the pointer
 // by making an assignment to the dereferenced pointer
 *p = 10;
 printf_s("j is %d\n", j); // j is now 10

 // allocate memory on the heap for an integer,
 // initialize to 5
 p = new int(5);

 // print the pointer and the object pointed to
 // the address will be somewhere on the heap
 printf_s("0x%p %d\n", p, *p);

 // free the memory pointed to by p
 delete p;

 // At this point, dereferencing p with *p would trigger
 // a runtime access violation.

 // Pointer arithmetic may be done with an array declared
 // on the stack or allocated on the heap with new.
 // The increment operator takes into account the size
 // of the objects pointed to.
 p = new int[5];
 for (i = 0; i < 5; i++, p++) {
 *p = i * 10;
 printf_s("0x%p %d\n", p, *p);
 }

 // A common expression seen is dereferencing in combination
 // with increment or decrement operators, as shown here.
 // The indirection operator * takes precedence over the
 // increment operator ++.
 // These are particularly useful in manipulating char arrays.
 char s1[4] = "cat";
 char s2[4] = "dog";
 char* p1 = s1;
 char* p2 = s2;

 // the following is a string copy operation
 while (*p1++ = *p2++);

 // s2 was copied into s1, so now they are both equal to "dog"
 printf_s("%s %s", s1, s2);
}

输出:

0x0012FEC8
0x0012FEC8 2
0x0012FEC8 7
j is 10
0x00320850 5
0x00320850 0
0x00320854 10
0x00320858 20
0x0032085C 30
0x00320860 40
dog dog

另一个示例演示如何在数据结构中使用指针;本例中采用链接列表。

// pointer_linkedlist.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;

struct NewNode {
 NewNode() : node(0){}
 int i;
 NewNode * node;
};

void WalkList(NewNode * ptr) {
 if (ptr != 0) {
 int i = 1;
 while (ptr->node != 0 ) {
  cout << "node " << i++ << " = " << ptr->i << endl;
  ptr = ptr->node;
 }
 cout << "node " << i++ << " = " << ptr->i << endl;
 }
}

void AddNode(NewNode ** ptr) {
 NewNode * walker = 0;
 NewNode * MyNewNode = new NewNode;
 cout << "enter a number: " << endl;
 cin >> MyNewNode->i;

 if (*ptr == 0)
 *ptr = MyNewNode;
 else {
 walker = *ptr;
 while (walker->node != 0)
  walker = walker->node;

 walker->node = MyNewNode;
 }
}

int main() {
 char ans = ' ';
 NewNode * ptr = 0;
 do {
 cout << "a (add node) d (display list) q (quit)" << endl;
 cin >> ans;
 switch (ans) {
 case 'a':
  AddNode(&ptr);
  break;
 case 'd':
  WalkList(ptr);
  break;
 }
 } while (ans != 'q');
}

输出:

a
45
d
a
789
d
qa (add node) d (display list) q (quit)
enter a number:
a (add node) d (display list) q (quit)
node 1 = 45
a (add node) d (display list) q (quit)
enter a number:
a (add node) d (display list) q (quit)
node 1 = 45
node 2 = 789
a (add node) d (display list) q (quit)

固定和可变指针

const 和 volatile 关键字用于更改处理指针的方式。 const 关键字指定指针在初始化后无法修改;此后指针将受到保护,防止进行修改。
volatile 关键字指定与后跟的名称关联的值可由用户应用程序中的操作以外的操作修改。因此,volatile 关键字对于声明共享内存中可由多个进程访问的对象或用于与中断服务例程通信的全局数据区域很有用。
如果某个名称被声明为 volatile,则每当程序访问该名称时,编译器都会重新加载内存中的值。这将显著减少可能的优化。但是,当对象的状态可能意外更改时,这是保证可预见的程序性能的唯一方法。
若要将指针指向的对象声明为 const 或 volatile,请使用以下形式的声明:

const char *cpch;
volatile char *vpch;

若要将指针的值(即指针中存储的实际地址)声明为 const 或 volatile,请使用以下形式的声明:

char * const pchc;
char * volatile pchv;

C++ 语言会阻止将允许修改声明为 const 的对象或指针的赋值。此类赋值会移除用来声明对象或指针的信息,从而违反原始声明的意图。请考虑以下声明:

const char cch = 'A';
char ch = 'B';

假定前面声明了两个对象(const char 类型的 cch 和 char 类型的 ch),以下声明/初始化将是有效的:

const char *pch1 = &cch;
const char *const pch4 = &cch;
const char *pch5 = &ch;
char *pch6 = &ch;
char *const pch7 = &ch;
const char *const pch8 = &ch;

以下声明/初始化存在错误。

char *pch2 = &cch;  // Error
char *const pch3 = &cch;  // Error

pch2 的声明声明了一个可以用来修改常量对象的指针,因此不允许使用。 pch3 的声明指定 pointer 是常量,而不是对象;与不允许使用 pch2 的原因相同,也不允许使用该声明。
以下八个赋值显示了通过指针进行的赋值以及对前面的声明的指针值的更改;现在,假设 pch1 到 pch8 的初始化是正确的。

*pch1 = 'A'; // Error: object declared const
pch1 = &ch;  // OK: pointer not declared const
*pch2 = 'A'; // OK: normal pointer
pch2 = &ch;  // OK: normal pointer
*pch3 = 'A'; // OK: object not declared const
pch3 = &ch;  // Error: pointer declared const
*pch4 = 'A'; // Error: object declared const
pch4 = &ch;  // Error: pointer declared const

声明为 volatile 或 const 和 volatile 的组合的指针遵循相同的规则。
指向 const 对象的指针通常用于函数声明中,如下所示:

errno_t strcpy_s( char *strDestination, size_t numberOfElements, const char *strSource );

前面的语句声明了函数 strcpy_s,其中,三个参数中的两个是指向 char 的类型指针。由于参数是按引用而不是按值传递的,因此,如果未将 strSource 声明为 const,则该函数可以自由修改 strDestination 和 strSource。将 strSource 声明为 const 可向调用方保证调用的函数无法更改 strSource。
注意
由于存在从 typename * 到 const typename * 的标准转换,因此将 char * 类型的参数传递到 strcpy_s 是合法的。但是,反之则不行;不存在从对象或指针中移除 const 特性的隐式转换。
给定类型的 const 指针可以分配给同一类型的指针。但是,非 const 类型的指针不能赋给 const 指针。以下代码显示了正确和错误的赋值:

// const_pointer.cpp
int *const cpObject = 0;
int *pObject;

int main() {
pObject = cpObject;
cpObject = pObject;  // C3892
}

以下示例显示了当有指针指向某个指向对象的指针时如何将对象声明为 const。

// const_pointer2.cpp
struct X {
  X(int i) : m_i(i) { }
  int m_i;
};

int main() {
  // correct
  const X cx(10);
  const X * pcx = &cx;
  const X ** ppcx = &pcx;

  // also correct
  X const cx2(20);
  X const * pcx2 = &cx2;
  X const ** ppcx2 = &pcx2;
}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c++
指针
c语言声明指针、c语言指针编程题、c语言指针编程题 经典、c语言指针编程练习题、声明式编程语言,以便于您获取更多的相关知识。

时间: 2024-10-29 15:17:15

C++编程中指针的声明与基本使用讲解_C 语言的相关文章

实例讲解C++编程中的虚函数与虚基类_C 语言

虚函数① #include "stdafx.h" #include <iostream> using namespace std; class B0//基类B0声明 { public: void display(){cout<<"B0::display()"<<endl;}//公有成员函数 }; class B1: public B0//公有派生类B1声明 { public: void display(){cout<<

对C语言中指针的理解与其基础使用实例_C 语言

C语言的指针,关键意思在于"指". "指"是什么意思? 其实完全可以理解为指示的意思.比如,有一个物体,我们称之为A.正是这个物体,有了这么个称谓,我们才能够进行脱离这个物体的实体而进行一系列的交流.将一个物体的指示,是对这个物体的抽象.有了这种抽象能力,才有所谓的智慧和文明.所以这就是"指示"这种抽象方法的威力. 退化到C语言的指针,指针是一段数据/指令(在冯诺易曼体系中,二者是相通,在同一空间中的)的指示.这是指示,也就是这段数据/指令的起始

C++编程中使用设计模式中的policy策略模式的实例讲解_C 语言

   在看<C++设计新思维>的时候,发现在一开始就大篇幅的介绍策略模式(policy),策略模式不属于经典设计模式中的一种,但是其实在我们日常的开发中是必不可少的.policy,策略,方针,这里的意思是指把复杂功能的类尽量的拆分为功能单一的简单类的组合,简单的类只负责单纯行为或结构的某一方面.增加程序库的弹性,可复用性,可扩展性.policy是一个虚拟的概念,他定义了某一类class的一些接口规范,并不与C++语法的关键字对应,只是一个抽象的概念. 实例1: //policy模式的常见使用实

详解C++编程中向函数传递引用参数的用法_C 语言

引用类型的函数参数向函数传递引用而非大型对象的效率通常更高. 这使编译器能够在保持已用于访问对象的语法的同时传递对象的地址. 请考虑以下使用了 Date 结构的示例: // reference_type_function_arguments.cpp struct Date { short DayOfWeek; short Month; short Day; short Year; }; // Create a Julian date of the form DDDYYYY // from a G

深入解析C++编程中范围解析运算符的作用及使用_C 语言

范围解析运算符 :: 用于标识和消除在不同范围内使用的标识符. 语法 复制代码 代码如下: :: identifier class-name :: identifier namespace :: identifier enum class :: identifier enum struct :: identifier 备注identifier 可以是变量.函数或枚举值.具有命名空间和类以下示例显示范围解析运算符如何与命名空间和类一起使用: namespace NamespaceA{ int x;

C++设计模式编程中Facade外观模式的使用实例解析_C 语言

外观模式提供了一个统一的接口,用来访问子系统的一群接口.外观定义了一个高层接口,让子系统更容易使用.外观模式让接口变得简单,简化了子系统的接口.外观模式十分简单,简而言之,就是简化你的类的接口,将一系列的复杂的过程封装到内部,对外只提供最简单的接口.结构图: 适用场景:当你要为一个复杂子系统提供一个简单接口时.子系统往往因为不断演化而变得越来越复杂.大多数模式使用时都会产生更多更小的类.这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难.F

C语言编程中实现二分查找的简单入门实例_C 语言

架设有一个数组 v 已经按升序排列了,数组 v 有 n=20 个元素.数组中有个元素 x,如何知道 x 位于该数组的第几位呢? 解决这个问题的一个普遍方法就是二分查找法.下面是程序: #include <stdio.h> int binsearch(int x, int v[], int n); main() { int i, result, n; int wait; int x = 17; // 需要查找的数值 int v[19]; // 定义一个数组 // 给数组赋值 for(i = 0;

在C语言编程中设置和获取代码组数的方法_C 语言

C语言setgroups()函数:设置组代码函数头文件: #include <grp.h> 定义函数: int setgroups(size_t size, const gid_t * list); 函数说明:setgroups()用来将list 数组中所标明的组加入到目前进程的组设置中. 参数size 为list()的gid_t 数目, 最大值为NGROUP(32). 返回值:设置成功则返回0, 如有错误则返回-1. 错误代码: EFAULT:参数list 数组地址不合法. EPERM:权限

c/c++中变量的声明和定义深入解析_C 语言

不管是函数还是变量的声明 ,都是为了告诉编译器我要使用这个变量或者函数了,用于类型检查.在定义 的时候编译器是不会分配任何内存的, 比如下面的函数: 复制代码 代码如下: void func() {      int a ;      int b = 0 ;      a = 0 ;} 当函数执行到int a ;的时候,这是一个声明,编译器不会为其分配内存空间 .当执行到a = 0; 这是一个定义,编译器才会为其分配内存空间.因此声明不一定是定义,定义一定就是定义,还可以包含声明.但是下面的这种