一站式网站,犀牛网站建设,分析网站建设到运营需要多少钱,海南网站运营公司C青少年简明教程#xff1a;C的指针入门
说到指针#xff0c;就不可能脱离开内存。了解C的指针对于初学者来说可能有些复杂#xff0c;我们可以试着以一种简单、形象且易于理解的方式来解释#xff1a;
首先#xff0c;我们可以将计算机内存想象成一个巨大的有许多格子的…C青少年简明教程C的指针入门
说到指针就不可能脱离开内存。了解C的指针对于初学者来说可能有些复杂我们可以试着以一种简单、形象且易于理解的方式来解释
首先我们可以将计算机内存想象成一个巨大的有许多格子的储物柜。每个柜子都有一个编号我们可以用这个编号来记住我们在哪个柜子里存放了东西。在编程世界中这个编号就叫做“地址”。当我们在程序中创建一个变量例如 int i 5;其实就是在告诉计算机“在你的储物柜里找一个地方给这个地方打上i的标签然后把5存入里面。”指针就相当于一张包含了柜子编号地址的便签。通过它我们可以快速地找到我们存储的东西。也就是说在我们创建一个指针时例如 int *p i;我们其实就是在说“我们找一个便签写下i的地址然后我们在需要时就可以通过查看这个便签来直接找到i。”对于初学者来说能理解“指针是什么”以及“怎么使用”指针那就已经非常不错了。 指针的应用非常广泛像是动态内存管理、数组、字符串、函数指针、数据结构等等都离不开指针。它可以让我们更加灵活地操作和管理内存可以实现很多强大的功能。更深入的内容可以等到以后在学习的过程中逐步了解。 指针是C和C语言中非常重要的概念初学的时候会被指针搞蒙。想学好指针我的经验是不要试图一开始就想理解指针概念而是要耐心准确的了解指针的各个方面这是一个过程随着学习的深入自然就知道指针是什么了。 指针是C和C语言中非常重要的概念初学的时候会被指针搞蒙。想学好指针我的经验是不要试图一开始就想理解指针概念而是要耐心准确的了解指针的各个方面这是一个过程随着学习的深入自然就知道指针是什么了。
【C指针pointer权威文档
Microsoft Ignite 指针 (C) 指针 (C) | Microsoft Learn
英文 Pointer declaration - cppreference.com 中文 指针声明 - cppreference.com 】
在C中地址和指针是紧密相关的。地址是一个变量或对象在内存中的位置而指针是一个变量它存储了一个地址。换句话说指针是指向一个特定类型的变量或对象的地址。
通过指针我们可以使用间接的方式访问或修改变量或对象的值。我们可以通过将变量的地址赋给指针来创建一个指针。
在C和C中*前后的空格是可选的而且编译器都会将它们解析为相同的意思。在C中声明指针的一般语法格式如下
type* pointerName;
其中 type 通常是指被指向的数据类型例如 int, float, char, 或者是结构体或类的名称。 pointerName 是你为指向变量的指针所取的名称也称为指针变量名可以是任何有效的标识符。注意指针的名称前缀* 是用于表示它是一个指针的。
以下这些声明是等价的
int* ptr;
int *ptr;
int * ptr;
int*ptr; 如果在一条语句中定义几个指针变量每个变量前必须有符号*例如
int *ip1, *ip2; //ip1和ip2都是指向int型对象的指针
double dp, *dp2; //dp2是指向double型对象的指针dp是指向double型对象 C中的指针变量和普通变量有很大的不同。
首先指针变量存储的是内存地址而普通变量存储的是实际的数据值。指针变量可以用来访问和修改存储在该内存地址中的数据而普通变量不能。
其次指针变量可以动态地分配内存而普通变量的内存大小是在编译时就确定了的。这使得指针变量非常有用因为它们允许程序在运行时动态地创建和管理内存。
最后指针变量可以用来传递内存地址而不是将整个数据复制到另一个变量中。这可以节省内存并提高程序的性能。
需要注意的是使用指针变量需要小心因为它们可以很容易地导致内存泄漏、悬空指针和其他问题。因此在使用指针变量时应该非常谨慎并且尽可能使用更安全的方法来处理内存。 C中指针是一种特殊的变量它存储的是另一个变量的内存地址。这使得你可以通过指针间接地访问或修改那个变量的值。
以下是一个简单的示例
int num 10; // 定义一个整数变量
int *ptr num; // 创建一个整数指针指向num的地址
cout num endl; // 输出变量的值
cout num endl; // 输出变量的地址
cout *ptr endl; // 输出指针所指向的变量的值
cout ptr endl; // 输出指针的值即存储的地址
在上面的示例中我们使用运算符获取变量num的地址并将其赋给指针ptr。我们可以通过*运算符来访问指针所指向的变量的值如*ptr。
需要注意的是指针的类型必须与其指向的变量或对象的类型匹配。例如指向整数的指针必须是int*类型。这是因为在读取或修改指针指向的内容时编译器需要知道要读取或修改的变量的类型。
定义指针变量时的 * 运算符和使用指针变量时的 * 运算符的含义和作用却是完全不同的。
在定义指针变量时* 运算符用于表示该变量是一个指针变量即可存储地址的变量。例如
int* ptr; // 定义一个 int 类型的指针变量 ptr
在使用指针变量时* 运算符用于访问指针所指向的地址上存储的数据。这个过程通常被称为解引用Dereferencing。例如
int num 42;
int* ptr num; // 定义一个指向 num 的指针变量 ptr
*ptr 24; // 在访问指针所指向的地址上的数据并将其修改为 24
在这个例子中*ptr 用于访问指针 ptr 所指向的地址上存储的数据 num并将其修改为 24。 指针变量是一个存储指针的变量通过指向的地址可以定位到具体的数据。
地址是一个数值是内存单元的编号用于确定计算机内存中特定位置的位置。
参加下图0x231001是指针变量p的地址0x231007是变量i的地址。指针变量存放的是变量i的地址也就是指针变量的值。所以p0x231007*p5p0x231001。注意其中地址值是示意性的。 说明
计算机内存想象成一个巨大的有许多格子储物柜。每个格子都有一个编号——内存地址常称为地址我们可以用这个编号来记住我们在放了东西的位置。
定义一个变量
int i 5;
这样就将i 和5关联起来了i 相当于标签名对i 的存取操作就是5所在的计算机内存空间存取5在计算机内存空间的存放位置就是地址。这个地址可以通过i 获得。【地址值的分配是由计算机系统完成的不固定。】
定义指针变量
int *p;
给指针变量p赋值
p i
就是把变量i 的地址给了p,也常称为p指向i 。这时*p和i 等价——值一样。
上面两句可合写为一句
int *p i;
注意点
(1) 对于上面示例语句 i和p是一样的i和*p也是一样的。
(2) 如果*、在类型说明符之后则为定义指针、引用如果出现在表达式中则为解引用、取地址。
(3) 不允许把一个数赋给指针变量。如
int *pA;
//pA 10 //不可以 报错
int a 10; //pA a; //不可以报错
(4) 除指针变量初始化赋值如int *p i;被赋值的指针变量前不能有*如*p i;是错误的。 示例代码
#include iostream
using namespace std;int main() { int a 10; //星号符作为指针类型声明符 int * p a; // 声明一个指向整型变量 a 的指针 // i和*p是一样的 cout *p * p endl; // 输出 10。星号符作为指针解引用运算符cout a a endl; // 输出 10。// i和p是一样的cout p p endl;cout a a endl; return 0;
}运行结果 注意你的运行结果 p和a显示的地址值是随机的地址值的分配是由计算机系统完成的不固定但两者值相等。 下面是一些常见的指针操作
1.声明指针可以使用*运算符来声明一个指针变量其指向的是某个数据类型的地址。例如
int* ptr; // 声明一个指向int类型数据的指针ptr
2.获取地址可以使用运算符获取某个变量的地址将其赋值给指针。例如
int num 10; // 定义一个整型变量num
int* ptr num; // 定义一个指向num的指针ptr
3.解引用操作使用解引用运算符*可以获取指针所指向的变量的值。例如
int num 10;
int* ptr num;
std::cout The value of the variable pointed by ptr is: *ptr std::endl; // 输出10
4.指针算术运算指针可以进行加、减、自增、自减等算术运算。例如
int arr[] {1, 2, 3, 4, 5};
int* ptr arr; // 将指针指向数组首元素
std::cout The first element of the array is: *ptr std::endl; // 输出1
ptr; // 将指针指向下一个元素
std::cout The second element of the array is: *ptr std::endl; // 输出2
5.数组和指针数组名本身就是一个指针指向数组的首元素。因此可以使用指针来操作数组例如上面的例子也可以使用数组名来操作数组。例如
int arr[] {1, 2, 3, 4, 5};
std::cout The first element of the array is: arr[0] std::endl; // 输出1
std::cout The second element of the array is: *(arr1) std::endl; // 输出2
6.指针和字符串指针可以用来处理字符串因为指针可以指向字符数组的第一个元素。通过指针我们可以访问和操作字符串中的字符。下面是一个示例演示了如何使用指针处理字符串
#include iostream
#include string
using namespace std;int main() {string str Hello; // 字符串使用string类型表示char *ptr str[0]; // 通过指针获取字符串的第一个字符// 通过指针遍历字符串并打印每个字符while (*ptr ! \0) {cout *ptr ;ptr; // 指针移到下一个字符位置}cout endl;int a 10;int ref a; // 引用引用变量acout a a endl;cout ref ref endl;ref 20; // 通过引用直接操作变量a的值cout a a endl;return 0;
}7.指针和函数
以下是一些指针和函数的常见用法
指针作为函数参数将指针传递给函数允许函数修改指针指向的变量。例如使用指针实现一个交换两个整数的函数
void swap(int* a, int* b) { int temp *a; *a *b; *b temp;
}
函数返回指针函数可以返回指针指向函数内部创建的动态对象。例如实现一个创建整数对象并返回其指针的函数可以写为
int* create_int(int value) { int* ptr new int(value); return ptr;
} 下面给出几个完整示例。
例1、一个简单例子源码
#include iostream
using namespace std;int main()
{int num 10;int *ptr num; // 定义一个指向整型变量num的指针ptrnum是num的地址cout num的值为 num endl; // 输出变量num的值cout 指针ptr所指向的变量的值为 *ptr endl; // 输出指针ptr所指向的变量的值cout 变量num的地址为 num endl; // 输出变量num的地址cout 指针ptr所存储的地址为 ptr endl; // 输出指针ptr所存储的地址return 0;
}在这个例子中定义了一个整型变量num并将它的值设置为10接着定义了一个指向num的指针ptr并将它的值设为num的地址。使用了运算符获取num的地址。然后通过指针ptr输出了num的值以及指针ptr的存储地址ptr自己的地址和所指向的变量的地址即num的地址。最后返回0以结束程序。
运行结果
num的值为10 指针ptr所指向的变量的值为10 变量num的地址为0x23fe1c 指针ptr所存储的地址为0x23fe1c 例2、通过指针来访问和操作数组元素
#include iostream
using namespace std;int main() {int arr[5] {1, 2, 3, 4, 5};int* ptr arr; // 将指针ptr指向数组arr的第一个元素// 使用指针访问数组元素for (int i 0; i 5; i) {cout Element at index i : *(ptr i) endl;}return 0;
}上面的例子中定义了一个包含5个整数的数组arr并初始化了其元素。然后将指针ptr指向数组的第一个元素也就是arr[0]。通过使用指针ptr和偏移量i可以访问数组中的各个元素。
在循环中使用*(ptr i)的方式来访问数组元素。这里的ptr i表示指针ptr加上偏移量i得到了指向数组中第i个元素的指针然后通过*操作符解引用指针即可获取该元素的值。在每次迭代中输出当前索引和对应元素的值。
输出结果是
Element at index 0: 1 Element at index 1: 2 Element at index 2: 3 Element at index 3: 4 Element at index 4: 5 以上是一些常见的指针操作。下面的部分初学者可以了解不需深究。
还有一些指针操作例如动态内存分配和释放可以使用“new”运算符在堆上分配一块内存并返回该内存块的首地址。例如
int* ptr new int; // 在堆上分配一个int类型的内存块并将其地址赋给指针ptr
*ptr 10; // 给该内存块赋值
std::cout The value of the variable pointed by ptr is: *ptr std::endl; // 输出10
使用“delete”运算符来释放在堆上分配的内存。例如
int* ptr new int;
// 做一些操作
delete ptr; // 释放ptr所指向的内存块 需要注意的是指针操作容易引起内存泄漏和悬空指针等问题下面解说之。
内存泄漏 (Memory Leak)
内存泄漏是指程序在运行过程中动态分配了内存但没有在不再需要时释放这些内存导致系统内存被消耗殆尽。内存泄漏的常见原因是使用new分配内存后没有对应的delete操作。使用 new 运算符分配给指针的内存需要使用 delete 运算符来释放该内存。这是为了防止内存泄漏确保程序动态分配的内存能在不再需要时正确释放。以下是一个简单的例子
int* p new int; // 使用 new 动态分配内存
*p 42; // 使用这块内存
delete p; // 使用 delete 释放内存
p nullptr; // 将指针设为 nullptr 避免悬空指针
当使用 new[] 运算符分配数组内存时应该使用 delete[] 运算符来释放
int* arr new int[10]; // 使用 new[] 动态分配数组内存
// 使用数组...
delete[] arr; // 使用 delete[] 释放数组内存
arr nullptr; // 将指针设为 nullptr 避免悬空指针
通过遵循这些规则可以有效避免内存泄漏和悬空指针问题。建议使用C标准库提供的智能指针如 std::unique_ptr 和 std::shared_ptr来自动管理内存这样可以进一步减少手动管理内存的风险。
悬空指针 (Dangling Pointer)
悬空指针是指一个指向已释放或无效内存区域的指针。当访问悬空指针时会导致未定义行为可能引发程序崩溃或数据损坏。以下是几个常见的悬空指针情况
1. 指向局部变量的指针超出作用域
int* getPointer() { int x 42; // 局部变量 return x; // 返回局部变量的地址
} // x超出作用域被销毁 void danglingPointerExample() { int* p getPointer(); // p现在是一个悬空指针 // *p 引用已被销毁的内存未定义行为
}
2. 释放内存后未将指针设为nullptr
void danglingPointerExample() { int* p new int(10); delete p; // 释放p指向的内存 // p现在成为悬空指针 // *p 20; // 访问已释放的内存未定义行为 p nullptr; // 将p设置为nullptr避免悬空指针
} 如何避免这些问题
及时释放内存 动态分配内存后在不再使用时应及时释放。初始化指针 使用指针前确保其指向有效内存或nullptr。使用智能指针 使用C标准库中的智能指针如std::unique_ptr和std::shared_ptr它们可以自动管理内存减少内存泄漏和悬空指针的风险。
智能指针Smart Pointers是C11及其后续版本引入的一种用于管理动态分配内存的指针类型。智能指针通过封装原始指针并以对象的方式管理内存提供了自动内存管理的能力。它们可以自动地释放分配的内存避免内存泄漏和悬空指针问题。智能指针是现代C中推荐使用的方式之一能够提高代码的安全性和可维护性。C标准库提供了几种智能指针类型其中最常用的是std::unique_ptr和std::shared_ptr。
C标准库提供了几种智能指针类型其中最常用的是std::unique_ptr和std::shared_ptr。若想使用时需要包含memory头文件。
memory 是 C 标准库中的一个头文件它提供了对内存管理功能的支持包括智能指针如 std::unique_ptr 和 std::shared_ptr、内存分配器、对齐和未初始化内存等功能。
std::unique_ptr是一种独占式智能指针它在任何时刻都只允许一个std::unique_ptr对象拥有对其指向的内存的控制权。这有助于防止内存泄漏。当std::unique_ptr超出作用域或者被重新赋值时它自动释放所指向的内存。
std::shared_ptr是一种共享式智能指针它允许多个std::shared_ptr对象共同拥有对同一内存地址的控制权。通过引用计数来管理内存当最后一个拥有该内存的std::shared_ptr对象被销毁时内存会被自动释放。
【提示在 C 标准库中std:: 命名空间包含了所有标准库组件。因此当我们使用标准库中的类或函数时必须要指定 std:: 命名空间。如果不想在每次使用标准库组件时都加上 std:: 前缀可以通过 using 指令来引入整个命名空间或者指定的组件成员using namespace std;只引入 std 命名空间中的某个成员如 unique_ptr 可用 using std::unique_ptr;】
示例unique_ptr 管理动态分配的数组
#include iostream
#include memory // 引入 memory 头文件
using namespace std;int main() {// 创建一个 std::unique_ptr 管理 int 数组unique_ptrint[] arr(new int[5]);for (int i 0; i 5; i) {arr[i] i * 10;}for (int i 0; i 5; i) {cout arr[ i ] arr[i] endl;}// 不需要显式删除数组智能指针会在离开作用域时自动释放内存return 0;
}在这个示例中我们使用 new int[5] 来创建一个包含 5 个整数的动态数组并将其赋值给 std::unique_ptrint[]。这样可以正确地管理数组的内存并且当 arr 超出作用域时内存将自动释放。 附、C指针pointer介绍https://blog.csdn.net/cnds123/article/details/108981367
C指针这是一篇正经知识总结https://zhuanlan.zhihu.com/p/517099946