学习要纲:建议掌握
gdb调试(b ,d ,fin ,bt ,print ,awatch ,up ,down ,set pretty等)
SourceInsight软件看代码(全局搜索 文件搜索等)
git如何调取分支合并(git branch,git blame,git log,git pull,git reset --hard等)
等内容,下面是对于指针的一个重新学习.
C语言的指针:
- 使得程序简单、紧凑、高效
- 有效地表示复杂数据结构
- 动态分配内存
- 得到多于一个的函数返回值
1.指针的基本用法
A.地址和变量:在计算机的内存中,每一个字节单元,都有一个编号,称为地址。
在C语言之中,内存单元的地址称为指针,专门用来存放地址的变量称为指针变量
1byte = 8 bits
<存储类型> <数据类型> * <指针变量名> = <地址量>;
使用*取出相应的值,*p / *(&a)
B.指针的赋值运算值的是通过赋值运算符指向地址变量送一个地址值
注意:一般在Centos之中指针的大小都为四个字节
0x 00 00 00 00 - 0x FF FF FF FF :四个字节 2^(4*8)-1
2.指针的运算
指针运算是指以指针变量所存放的地址量作为运算量而进行的运算
p + n:指针向着大地址方向进行移动,表示p + sizeof(p 的类型) * n,为地址量
px -py:指数据的个数
3.指针与数组
4.指针与二维数组
5.指针与字符串
A.字符指针
B.注意点
初始化字符指针是把内存中字符串的首地址赋予指针,而不是把该字符串复制到指针中
char str[] = "Hello World";
char *p = str;
char ch1[] = "Hello World";
char ch2[] = "Hello World";
注意:ch1和ch2虽然是内容相同的,但是地址是不同的.
c.在C编程过程中,当一个字符指针指向一个字符串常量时,不能够修改指针指向的对象的值
char *p = "Hello World";
char *p = 'H';//错误,字符指针常量是不允许改变的
这个地方是一个易错点:
可以见到字符串常量的地址是相同的,且输出字符指针的方式是直接打印其值.
给出一个错误案例:
想要把第一个字母变成小写,会出现Segmentation fault报错,说明内存访问过程出现了问题.
6.二级指针
A.
B.
C.稍微进阶版---此处需要结合指针与字符串进行学习
#include <stdio.h>int main()
{char *s[] = { "apple", "pear", "potato" };char ** p;int i, n;i = 0;n = sizeof(s) / sizeof(char *);p = &s[0];while (i < n){printf("%s %s\n",s[i],*(p+i));i++;}getchar();return 0;}
7.void指针和const修饰符
void指针是一种不确定数据类型的指针变量,在没有进行强制类型转换之前,不能进行任何指针的算数运算.
A.一般形式:void *<指针变量名称>
可以见到*p 和 *q是非法间接寻址,因为不知道如何取类型的地址大小.
当将* p变为* (int *)p即可
注意:void指针在进行使用的时候必须要进行强制转换.
B.对于void指针在没有进行强制类型转换之前是不能够进行任何的算数运算
C.const变量
常量化变量值
const <数据类型> 变量名 = [<表达式>];
常量化变量是死了使得变量的值不被修改,变量有const修饰时,想要用指针间接访问变量,指针也要有const修饰.
①const int *p ------
不能够通过指针改变目标值.
② int * const p ------
只能够将上面的赋值过程变为int const *q = &m,也就是q的地址不能够修改
也就是const修饰谁,谁可以不可以修改
③const int * const r = &m;
任何值都不能更改
D.main函数是否可以带参数,涉及到const指针
int main(int argc,const char * argc[])注意这个地方是针对于在Centos系统下想起来一个命令
gcc -std=c++11 ys.cpp -o a
注意:这个地方的
int argc = 1
const char * argc[] = {"./a","192.168.6.118"}
8.函数参数的用法
A.函数之间参数的传递
①全局变量传递参数
形参的地址不一样
②复制传递
调用函数将实参传递给被调用函数,被调用函数将创建同类型的形参并用实参进行初始化.
形参是新开辟的存储空间,在函数中改变形参的值,不会影响到实参.
③地址传递
按照地址进行传递,实参为变量的地址,形参是同类型的指针;
被调用函数中对形参的操作,将直接改变实参的值(被调用函数对指针目标的操作相当于对实参本身的操作)
编写一个函数,统计字符串中小写字母的个数,并把字符串中的小写字母转化成大写字母
a.统计字符串中小写字母的个数
注意:当然这个功能是非常容易进行实现的,需要进行注意的是这个地方的形参为什么是定义的为const char *p,这样定义的话就是不会改变其*p的内容,和前面的内容进行了串联.
b.将小写字母转换为大写字母
这个地方需要进行注意空格的ascll为32.
B.数组传递参数
三种方式:
①全局数组传递方式
②复制传递方式
例题:编写函数,计算一个一维整型数组的所有元素的和
实参为数组的指针,形参为数组名(本质是一个指针变量)
对于上面那句话的解读是,比如说一个形参 int p[],相当于int *p
③地址传递方式
实参为数组的指针,,形参为同类型的指针
例题:去掉字符串里面的空格
9.指针函数
A.指针函数是指一个函数的返回值为地址量的函数
指针函数的定义一般形式如下:
<数据类型> * <函数名称>(<参数说明>)
{
}
举个例子:请你说出下面代码的问题
这个地方的str[20]是局部变量,是在栈上的,返回不了了
结果是乱码了,如下所示:
原因是在于:之前租的房子,现在进不去了,所以打印出来的是乱码看不见.
改进方式:
①将其变为全局变量 char str[20] 改法看起来怪怪的
②改变为静态变量 static char str[20] ------ 可用
③变成全局字符串常量 char * str = "Hello"; 不能够进行修改
④使用malloc的方式
B.
#include <stdio.h>// 定义一个返回int类型指针的函数
int* find_max(int* a, int* b) {return (*a > *b) ? a : b;
}int main() {int x = 10, y = 20;// 调用指针函数find_max,返回x和y中较大的那个数的地址int* max_ptr = find_max(&x, &y);printf("The max value is: %d\n", *max_ptr);return 0;
}
10.函数指针
函数指针用来存放函数的地址,这个地址是一个函数的入口地址
函数名代表了函数的入口地址
函数指针变量说明的一般形式如下:
<数据类型> (*<函数指针名称>) (<参数说明列表>);
#include <stdio.h>// 定义一个返回int类型,接受两个int参数的函数
int add(int a, int b) {return a + b;
}int main() {// 声明一个函数指针,指向接受两个int参数并返回int类型的函数int(*func_ptr)(int, int);// 将函数指针指向add函数func_ptr = add;// 通过函数指针调用add函数printf("The result is: %d\n", (*func_ptr)(3, 4));getchar();return 0;
}
函数指针的声明方式是先声明返回类型,然后是函数指针变量名,再跟随函数的参数类型。
这个地方可以结合指针数组一起使用,相应的指针数组如下所示:
#include <iostream>
using namespace std;int main() {// 定义一个指针数组,能够存储5个int类型的指针int* ptrArray[5];// 定义一些整数int a = 10, b = 20, c = 30, d = 40, e = 50;// 将每个整数的地址赋值给指针数组中的元素ptrArray[0] = &a;ptrArray[1] = &b;ptrArray[2] = &c;ptrArray[3] = &d;ptrArray[4] = &e;// 通过指针数组输出各个整数的值for (int i = 0; i < 5; i++) {cout << "Value of ptrArray[" << i << "]: " << *ptrArray[i] << endl;}return 0;
}