个人主页:PingdiGuo_guo
收录专栏:C++干货专栏
文章目录
前言
1.字符指针
1.1字符指针的概念
1.2字符指针的用处
1.3字符指针的操作
1.3.1定义
1.3.2初始化
1.4字符指针使用注意事项
2.数组参数,指针参数
2.1数组参数
2.1.1数组参数的概念
2.1.2数组参数的作用
2.1.3一维数组的传参
2.1.4二维数组的传参
2.2指针参数
2.2.1指针参数的概念
2.2.2指针参数的作用
2.2.3一级指针与二级指针
2.2.3一级指针的传参
2.2.4二级指针的传参
2.3练习
2.3.1数组参数的练习题目
2.3.2指针参数的练习题目
总结
前言
相关文章:C++指针(二)、C++指针(一)
本篇博客是介绍字符指针,数组参数与指针参数的。
1.字符指针
1.1字符指针的概念
字符指针是指向字符数组或字符串的指针。在C语言中,字符数组和字符串都是以字符指针的形式来操作和访问的。字符指针指向字符数组或字符串的第一个字符的地址,通过指针可以访问数组或字符串中的各个字符。
1.2字符指针的用处
字符指针有很多用途,以下是一些常见的用途:
1. C字符串操作:字符指针可以用来处理C字符串,包括字符串的拷贝、连接、比较等操作。由于C字符串以null字符结尾,我们可以使用字符指针来访问和操作字符串的每个字符。
2. 字符串数组访问:字符指针可以用于访问字符串数组中的每个字符串。通过递增指针的方式,可以依次访问字符串数组中的每个元素。
3. 动态内存分配:字符指针可以用于动态分配内存,即在程序运行时根据需要分配内存空间。通过使用`new`运算符,可以分配一段连续的内存,并返回第一个元素的指针。
4. 字符指针数组:字符指针可以用于创建指向字符的指针数组。这在处理多个字符串时非常有用,每个指针可以指向一个独立的字符串。
5. 文件操作:字符指针可以用于进行文件的读取和写入操作。通过指针的方式,我们可以逐字符或逐行读取文件内容,并将指针移动到文件的特定位置。
这些只是字符指针的一些常见用途,实际上,字符指针在C和C++中有广泛的应用,可以用于字符串处理、动态内存管理、文件操作等多种场景。
1.3字符指针的操作
1.3.1定义
字符指针需要关键字char*来定义,其他部分与普通指针的定义相同。如下:
char* ptr;
这样,我们就定义了一个字符指针。
1.3.2初始化
字符指针初始化有两种方式:
1.定义一个字符并为其赋值,然后用&来获取这个字符变量的地址。代码如下:
#include <bits/stdc++.h>
using namespace std;
int main()
{
char ch = 'w';
char* pc = &ch;
cout<<*pc<<endl;
return 0;
}
到这里,可能有些同学会疑惑:*pc里不是存储了变量ch的地址了么,为啥输出的是ch里的值呢?
这里我们要注意,在代码中,
*pc
是一个解引用操作符,它用于获取指针pc
所指向的内存地址上存储的值。在这种情况下,*pc
表示获取指针pc
所指向的内存地址上存储的字符。所以,cout<<*pc
输出的是ch
里的值,即字符w
。
那可能有些同学又要问了:PingdiGuo_guo,那啥情况输出的是地址呢?
注意:当我们直接输出指针变量本身时,会输出该指针变量所存储的地址而不是地址上的值。如果要输出指针变量所指向的地址上的值,需要使用解引用操作符*。
以下是一个例子:
#include <iostream>
using namespace std;
int main() {
int x = 10;
int* ptr = &x;
cout << ptr << endl; // 输出指针变量ptr的值,即x的地址
cout << *ptr << endl; // 输出指针ptr所指向的地址上存储的值,即x的值
return 0;
}
上述代码输出结果:
第一行输出的是指针变量ptr存储的地址,第二行输出的是指针ptr所指向的地址上存储的值。
2.我们直接把一个字符串赋值给指针变量。如下:
#include <iostream>
using namespace std;
int main()
{
const char* ps = "hello.";
cout<<ps<<endl;
return 0;
}
大家看一看,运行上述代码,*ps里储存的是什么呢?
有同学觉得储存的的应该是字符串"hello."的地址,实际上储存的是字符串"hello."的首字符,也就是字符'h'的地址。
1.4字符指针使用注意事项
字符指针使用注意事项:
1. 注意字符串的结束符:C语言中的字符串以'\0'作为结束符,因此在使用字符指针操作字符串时,需要确保字符串末尾有一个'\0'字符,否则可能导致未定义的行为。
2. 避免修改字符串常量:字符串常量是只读的,因此尝试修改字符串常量的内容会导致编译错误或者运行时错误。如果需要修改字符串,应该使用可修改的字符数组或者动态分配内存的字符指针。
3. 空指针检查:在使用字符指针之前,应该进行空指针检查,以避免对空指针进行操作,导致程序崩溃或者出现未定义的行为。
4. 确保指针指向有效的内存区域:在使用字符指针操作字符串时,应确保指针指向的内存区域包含了足够的空间,以存储字符串的内容,并且字符串的结束符'\0'也需要在指针指向的内存区域内。
5. 避免指针越界访问:在使用字符指针操作字符串时,应避免指针越界访问,即超过了字符串的有效范围进行访问,这可能导致程序崩溃或者出现未定义的行为。
6. 避免指针悬挂:在使用字符指针时,需要确保指针指向的内存区域有效,并且在指针不再使用时及时释放或者置为NULL,以避免产生悬挂指针或者内存泄漏的问题。
7. 注意指针运算的规则:字符指针支持指针运算,例如指针加法和指针减法,但要注意指针运算时遵循指针的类型大小,以避免指针运算越界或者计算错误。
8. 使用函数库提供的字符串操作函数:C语言提供了一系列函数库来操作字符串,例如strcpy、strlen、strcat等,使用这些函数能够更方便、安全地操作字符串,同时避免常见的错误。
2.数组参数,指针参数
2.1数组参数
2.1.1数组参数的概念
数组参数是指在函数的参数列表中声明一个数组作为参数。当函数被调用时,实际传递给该参数的是数组的地址,而不是整个数组本身。通过使用数组参数,可以将数组的内容传递给函数,并在函数中对数组进行操作或者返回相应的结果。
2.1.2数组参数的作用
数组参数在函数中的作用主要包括以下几点:
1. 传递数组数据:通过数组参数,可以将数组的数据传递给函数进行处理。函数可以读取、修改数组的元素,执行各种操作。
2. 减少内存开销:使用数组参数可以避免将整个数组的副本传递给函数,而是只传递数组的地址或首元素地址。这样可以减少内存开销和数据复制次数。
3. 返回数组数据:函数也可以通过数组参数返回数据。在函数内部修改数组元素后,函数外部的数组也会被修改。
4. 可变长度数组:在函数参数中使用可变长度数组,可以根据需要传递不同大小的数组。这样可以编写更加灵活的函数,适应不同规模的数据。
5. 简化代码:通过使用数组参数,可以将对数组的一系列操作封装在函数中,提高代码的可读性和可维护性。
总的来说,数组参数可以使函数更加灵活、高效地处理数组数据,提高代码的可复用性和可维护性。它是C语言中非常重要的函数参数类型之一。
2.1.3一维数组的传参
void is(int a[])
{
}
void id(int a[100])
{
}
void ih(int* a[10])
{
}
void ij(int* a)
{
}
void ig(int** a)
{
}
注:上述这些格式都可以运行。
2.1.4二维数组的传参
二维数组的传参基本形式与一维数组类似,只是在声明参数时需要指明数组的行数和列数。
// 形参为二维数组,指定列数的方式
void functionName(dataType arrayName[][columns], int rows) {
// 函数体
// 可以通过双重循环访问和修改数组元素
}
// 形参为二维数组,指定指针的方式
void functionName(dataType *arrayName[], int rows, int columns) {
// 函数体
// 可以通过双重循环访问和修改数组元素
}
// 形参为二维数组,指定指向指针的指针的方式
void functionName(dataType **arrayName, int rows, int columns) {
// 函数体
// 可以通过双重循环访问和修改数组元素
}
在上述代码中,dataType是数组中元素的数据类型,arrayName是二维数组的名称,rows是数组的行数,columns是数组的列数。
在函数参数列表中,我们可以使用dataType arrayName[][columns]或者dataType *arrayName[]或dataType **arrayName来声明一个二维数组作为参数。
在函数体内部,可以使用双重循环来访问和修改二维数组的元素。
2.2指针参数
2.2.1指针参数的概念
指针参数是指函数中的参数被声明为指针类型的参数。指针参数允许函数通过引用传递内存地址,从而能够修改原始数据的值。
2.2.2指针参数的作用
指针参数的作用有以下几个方面:
1. 传递大型数据结构:通过将指针作为参数传递给函数,可以避免复制整个数据结构。这对于大型结构体或数组来说尤为重要,可以提高程序性能。
2. 在函数内部修改变量的值:通过传递指向变量的指针作为参数,函数可以直接修改变量的值。这对于需要在函数中修改全局变量或者需要通过函数返回多个值的情况非常有用。
3. 动态内存分配:通过指针参数,可以在函数内部对动态分配的内存进行操作。例如,在函数中动态创建一个数组,并通过指针参数返回数组的地址。
4. 传递数组:通过将数组的首地址作为指针参数传递给函数,可以在函数内部操作数组中的元素。
需要注意的是,使用指针参数需要谨慎,避免出现空指针或者越界访问等问题。在使用指针参数时,需要确保传递正确的地址,并在函数内部对指针进行适当的处理和保护。
总之,指针参数在函数传参中具有重要作用,可以通过引用传递内存地址,实现对变量和数据的修改和操作。
2.2.3一级指针与二级指针
一级指针和二级指针是指针的不同级别。
一级指针(Level 1 Pointer)是指一个指针变量(如int*)指向一个存储地址,该地址保存了一个值或者另一个指针。通过一级指针可以直接访问到被指向的内存地址中的值。
二级指针(Level 2 Pointer),也被称为指向指针的指针,是指一个指针变量(如int**)存储了一个指针的地址。通过二级指针可以间接地访问到被指向的内存地址中的值。
二级指针常用于需要对一级指针进行修改的情况,例如在函数中传入一个指针的指针,以便修改指针的值。同时,你也可以定义三级指针、四级指针以及更高级别的指针,但在实际开发中很少用到。
2.2.3一级指针的传参
一级指针的传参是通过将指针作为函数的参数传递给函数。
在函数中,可以通过一级指针来访问和修改指针所指向的值。
下面是一级指针传参的基本形式:
void func(int* ptr) {
// 使用指针访问和修改指针所指向的值
*ptr = 10;
// ...
}
int main() {
int x = 0;
int* p = &x;
// 将指针p作为参数传递给函数func
func(p);
// x的值已经被修改为10
return 0;
}
在上面的例子中,通过将指针p作为参数传递给函数func,函数内部可以通过指针ptr来访问和修改p所指向的值。在函数中,通过解引用操作符*来访问指针所指向的值。
需要注意的是,传递一级指针作为参数时,函数内部对指针所指向的值的修改是有效的,因为传递的是指针变量本身的地址,而不是指针变量的副本。
问:在这种情况下,函数能接受什么参数?
答:在这种情况下,函数func
可以接受一个指向整型变量的指针作为参数。在main
函数中,通过将指针p
作为参数传递给func
函数,函数func
可以使用指针访问和修改指针所指向的值。
2.2.4二级指针的传参
二级指针的传参是通过将指向指针的指针作为函数的参数传递给函数。
二级指针在某些情况下用于传递指针数组或二维数组的地址,或者用于动态分配内存等场景。
下面是二级指针传参的基本形式:
void func(int** ptr) {
// 使用二级指针访问和修改指针所指向的值
**ptr = 20;
// ...
}
int main() {
int x = 0;
int* p = &x;
int** pp = &p;
// 将二级指针pp作为参数传递给函数func
func(pp);
// x的值已经被修改为20
return 0;
}
在上面的例子中,通过将二级指针pp作为参数传递给函数func,函数内部可以通过二级指针ptr来访问和修改pp所指向的值。在函数中,通过解引用操作符*两次来访问指针所指向的值。
需要注意的是,传递二级指针作为参数时,需要对指针进行两次解引用才能访问到最终的值。在函数内部修改指针所指向的值也是有效的。
此外,还要注意对于二维数组的传参,可以使用一级指针或二级指针来传递二维数组的地址。
问:在这种情况下,函数能接受什么参数?
答:在这种情况下,函数func
可以接受一个指向指向整型变量的一级指针的二级指针作为参数。
2.3练习
2.3.1数组参数的练习题目
题目:
编写一个函数,接收一个整型数组和数组长度作为参数,并返回数组中的最大值。
步骤:
1. 在main函数中声明一个整型数组并初始化。
2. 定义一个函数findMax,接收一个整型数组和数组长度作为参数,并返回一个整型值。
3. 在findMax函数中,定义一个变量max,初始化为数组的首个元素。
4. 使用循环遍历数组,比较数组中的每个元素与max的值,并更新它。
5. 返回最大值。
6. 在main函数中调用findMax函数,并将返回值打印出来。
流程图:
开始
声明和初始化数组以及定义数组长度
定义函数findMax,接收数组和长度作为参数,并返回最大值
定义变量max,并初始化为数组的首个元素
循环遍历数组
比较当前元素和max的值,若大于max,则更新max的值
返回max
调用findMax函数并打印返回值
结束
代码:
#include <iostream>
int findMax(int arr[], int size) {
int max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
int main() {
int arr[] = {3, 5, 1, 4, 2};
int size = sizeof(arr) / sizeof(arr[0]);
int max = findMax(arr, size);
std::cout << "Max: " << max << std::endl;
return 0;
}
在这个例子中,findMax函数接收一个整型数组和数组长度作为参数,并返回最大值。函数内部使用循环遍历数组,比较每个元素与max的值,并根据情况更新它。在main函数中声明数组arr并初始化,然后调用findMax函数并传递相应的参数,将返回值存储在max变量中,并使用std::cout 打印出来。
运行结果:
2.3.2指针参数的练习题目
题目:
编写一个函数,接收一个整型数组的指针和数组长度作为参数,并将数组中的所有元素翻转。
步骤:
1. 在main函数中声明一个整型数组并初始化。
2. 定义一个函数reverseArray,接收一个指向整型数组的指针和数组长度作为参数,并将数组中的所有元素翻转。
3. 在reverseArray函数中,使用两个指针start和end,分别指向数组的起始和末尾位置。
4. 使用循环,交换`start`和`end`指针所指向的元素,并同时将`start`往后移动一位,`end`往前移动一位。
5. 循环继续,直到start指针超过或等于end指针。
6. 在main函数中调用reverseArray函数,并将修改后的数组打印出来。
流程图:
开始
声明和初始化数组以及定义数组长度
定义函数reverseArray,接收整型数组指针和长度作为参数
定义指针start,指向数组起始位置
定义指针end,指向数组末尾位置
循环,交换start和end指针所指向的元素,并同时移动start和end指针
若start指针超过或等于end指针,则跳出循环
在main函数中调用reverseArray函数,并打印修改后的数组
结束
代码:
#include <iostream>
void reverseArray(int* arr, int size) {
int* start = arr;
int* end = arr + size - 1;
while (start < end) {
int temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]);
std::cout << "Original array: ";
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
reverseArray(arr, size);
std::cout << "Reversed array: ";
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
return 0;
}
在这个例子中,reverseArray函数接收一个指向整型数组的指针和数组长度作为参数,并将数组中的所有元素翻转。函数内部使用两个指针start和end分别指向数组的起始和末尾位置,通过循环交换指针所指向的元素,并同时移动指针,直到start指针超过或等于`end`指针。在main函数中声明数组arr并初始化,然后调用reverseArray函数并传递相应的参数,将数组的元素翻转。最后使用std::cout打印出原始数组和翻转后的数组。
运行结果:
总结
本篇博客到这里就结束了,感谢大家的支持与观看,如果有好的建议欢迎留言,谢谢大家啦!