C/C++ - 指针、引用篇(上)

简介: C/C++ - 指针、引用篇(上)

概述:

  1. 普通变量:一个地址对应一个内容;


  1. 指针变量:一个地址对应一个内容(该内容为一个地址),所以指针变量有两个地址,一个是存储内容(地址)的该单元地址(类似于普通变量的地址),还有一个是存储内容里的内容是个地址。


  1. 指针也是一种变量,它所表示的不是数据的值,而是存储着数据的内存的地址。通过使用指针,就可以对任意指定地址的数据进行读写。

请大家看一下代码清单4-2。这是定义了d、e、f这3个指针变量的C语言程序。和通常的变量定义有所不同,在定义指针时,我们通常会在变量名前加一个星号(*)。我们知道,d、e、f都是用来存储32位(4字节)的地址的变量。然而,为什么这里又用来指定char (1字节)、short(2字节)、long(4字节)这些数据类型呢?大家是不是也感到很奇怪?实际上,这些数据类型表示的是从指针存储的地址中一次能够读写的数据字节数。

代码清单4-2 各种数据类型指针的定义

char *d;     //char类型的指针d的定义
short *e;    //short类型的指针e的定义
long *f;     //long类型的指针f的定义

假设d、e、f的值都是100。在这种情况下,使用d时就能够从编号100的地址中读写1个字节的数据,使用e时就是2个字节(100地址和101地址)的数据,使用f时就是4个字节(100地址~103地址)的数据。怎么样?指针是不是很简单呢(图4-5)。

image.png

图4-5 指针的数据类型表示一次可以读写的长度

4.指针大小:是指针变量占用内存的大小(字节数),在32位机上,所有指针类型变量占用内存字节数都为4。因为32位机就是 4字节 * 8个二进制位/字节 计算出来的。如果在64位机上,指针占用内存大小就是:
8个字节。


5.数组指针(或称“行指针”):int (*p)[n];(或 int (*)[n]p)()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。

数组指针:首先它是一个指针,它指向一个数组。在 32 位系统下任何类型的指针永远是占 4 个字节,至于它指向的数组占多少字节,不知道,具体要看数组大小。它是“指向数组的指针”的简称。


6.指针数组:int *p[n]; []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身的大小决定,每一个元素都是一个指针,在 32 位系统下任何类型的指针永远是占 4 个字节。它是“储存指针的数组”的简称。


7.语法没错,但是偏离了实际意义:

int *p;
scanf("%d", p);
scanf("%d", &p);

8.%c、%s 在指针中的使用:

char a[20]="12345", *p=a;
printf("%c\n", *p); //1
printf("%s\n", p);  //12345

9.以下都是对的:

char str[]="123456889";
char *p;
p=str;
p="34546";
char *ps=&(*("123"));


10.指针初始化 & 默认值

指针变量未被初始化,指针变量就不会被分配存储空间(无论是全局还是局部)。在程序中如果使用了未被分配空间的指针,就会出现难以查找的错误。

char *p1;
int main()
{
    char *p2, *p3;
    p3=(char*)malloc(sizeof(char));
}

此时此刻,p1和p2都是一样的情况,本身有地址,但是内容区域是空的(因为没有分配存储空间,导致使用取内容符号会报错);而p3属于常规申请内存操作。

11.待更新...


案例一:

int main()
{
    int n=10,n1=20; // 值的变量
    int *a=&n; // 地址的变量,其实与值的变量一样的,只是值的变量,这个变量的值在变,地址不变;而地址的变量,地址在变(当然这里的地址对应的值是跟地址绑定在一起的)
    int *c=a;
    printf("a == %d\n",a);
    printf("a == %d\n",*a);
    printf("c == %d\n",c);
    printf("c == %d\n\n",*c);
    *a=1000;
    printf("a == %d\n",a);
    printf("a == %d\n",*a);
    printf("c == %d\n",c);
    printf("c == %d\n\n",*c);
    a=&n1; // a地址改变了对c没有影响
    printf("a == %d\n",a);
    printf("a == %d\n",*a);
    printf("c == %d\n",c);
    printf("c == %d\n\n",*c);
    int *&b=a; // b就代表a,单单只是名字不一样
    printf("a == %d\n",a);
    printf("a == %d\n",*a);
    printf("b == %d\n",b);
    printf("b == %d\n\n",*b);
    b=c; // b地址改变了对a有影响
    printf("a == %d\n",a);
    printf("a == %d\n",*a);
    printf("b == %d\n",b);
    printf("b == %d\n",*b);
    printf("c == %d\n",c);
    printf("c == %d\n\n",*c);
    return 0;
}

image.png


案例二:

struct node
{
    int val;
    node *left;
    node *right;
    // 这里left、right一定要初始化NULL,否则遍历的时候指针乱值并不等于NULL
    node(const int x):val(x),left(NULL),right(NULL){}
};
void preTraversal(node *t) // 前序遍历
{
    if(t)
    {
        printf("%d ",t->val);
        preTraversal(t->left);
        preTraversal(t->right);
    }
}
int main()
{
    node *rt=new node(-1);
    rt->left=new node(1);
    rt->right=new node(2);
    rt->left->left=new node(3);
    preTraversal(rt);
    puts("\n");
    node *newNode=new node(4); // 修改叶子结点(不影响)
    rt->left->left=newNode;
    preTraversal(rt);
    puts("\n");
    newNode=new node(5); // 修改中间结点(上下都有相连)(下面的有影响)
    rt->left=newNode;
    preTraversal(rt);
    return 0;
}

image.png



案例三:*p、p、&p之间的关系


image.png


案例四:二维指针(二维数组)

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a[2][3]={0,2,4,6,8,10};
    cout<<a<<endl;
    cout<<a[0]<<endl;
    cout<<(a+1)<<endl;
    cout<<*(a+1)<<endl;
    cout<<**(a+1)<<endl;
    cout<<*(a[1]+1)<<endl;
    cout<<**(a+1)+2<<endl; 
    cout<<*(*(a+1)+2)<<endl;
    cout<<**((a+1)+2)<<endl;
    return 0;
}
Console:
0x6afee8
0x6afee8
0x6afef4
0x6afef4
6
8
8
10
4198653

案例五:

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int d;
    node * next;
};
int main()
{
    node *L,*L2,*nd=new node;
    nd->d=1; nd->next=NULL;
    L2=L=nd;
    printf("L2 == %d\n",L2);
    printf("L == %d\n",L);
    L=NULL; // 对L2没有影响,因为指针就像个临时工,给它什么地址,就去另外一个地址做事情
    printf("L2 == %d\n",L2);
    printf("L == %d\n",L);
    return 0;
}
Console:
L2 == 16126224
L == 16126224
L2 == 16126224
L == 0
目录
相关文章
|
3天前
|
存储 程序员 C++
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
20 0
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
86 4
|
2月前
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
2月前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
61 1
|
2月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
43 2
|
2月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
2月前
|
存储 C++ 索引
C++函数指针详解
【10月更文挑战第3天】本文介绍了C++中的函数指针概念、定义与应用。函数指针是一种指向函数的特殊指针,其类型取决于函数的返回值与参数类型。定义函数指针需指定返回类型和参数列表,如 `int (*funcPtr)(int, int);`。通过赋值函数名给指针,即可调用该函数,支持两种调用格式:`(*funcPtr)(参数)` 和 `funcPtr(参数)`。函数指针还可作为参数传递给其他函数,增强程序灵活性。此外,也可创建函数指针数组,存储多个函数指针。
|
3月前
|
编译器 C++
【C++核心】指针和引用案例详解
这篇文章详细讲解了C++中指针和引用的概念、使用场景和操作技巧,包括指针的定义、指针与数组、指针与函数的关系,以及引用的基本使用、注意事项和作为函数参数和返回值的用法。
54 3
|
2月前
|
算法 C++
【算法】双指针+二分(C/C++
【算法】双指针+二分(C/C++
|
2月前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值